changeset 2330071b33cf in modules/account:default details: https://hg.tryton.org/modules/account?cmd=changeset;node=2330071b33cf description: Enable periods to end on any day of the month
issue8855 review252671002 diffstat: CHANGELOG | 2 + __init__.py | 146 +++++++++++++------------ fiscalyear.py | 110 ++++++++++++++---- fiscalyear.xml | 23 ++- tests/test_account.py | 49 ++++++++- view/fiscalyear_create_periods_start_form.xml | 14 ++ view/fiscalyear_form.xml | 3 +- 7 files changed, 232 insertions(+), 115 deletions(-) diffs (517 lines): diff -r 69a5c20480c8 -r 2330071b33cf CHANGELOG --- a/CHANGELOG Sun Jan 12 10:21:51 2020 +0100 +++ b/CHANGELOG Tue Feb 04 10:10:32 2020 +0100 @@ -1,3 +1,5 @@ +* Allow periods to end on any day of the month +* Use a wizard to select parameters when creating periods * Modularize code in GroupLines wizard Version 5.4.0 - 2019-11-04 diff -r 69a5c20480c8 -r 2330071b33cf __init__.py --- a/__init__.py Sun Jan 12 10:21:51 2020 +0100 +++ b/__init__.py Tue Feb 04 10:10:32 2020 +0100 @@ -2,62 +2,62 @@ # this repository contains the full copyright notices and license terms. from trytond.pool import Pool -from .fiscalyear import * -from .account import * +from . import account from . import configuration -from .period import * -from .journal import * -from .move import * -from .move_template import * +from . import fiscalyear +from . import journal +from . import move +from . import move_template +from . import party +from . import period from . import tax -from . import party def register(): Pool.register( - FiscalYear, - BalanceNonDeferralStart, - TypeTemplate, - Type, - AccountTemplate, - AccountTemplateTaxTemplate, - Account, - AccountDeferral, - AccountTax, - OpenChartAccountStart, - GeneralLedgerAccount, - GeneralLedgerAccountContext, - GeneralLedgerLine, - GeneralLedgerLineContext, - BalanceSheetContext, - BalanceSheetComparisionContext, - IncomeStatementContext, - CreateChartStart, - CreateChartAccount, - CreateChartProperties, - UpdateChartStart, - UpdateChartSucceed, - AgedBalanceContext, - AgedBalance, + fiscalyear.FiscalYear, + fiscalyear.BalanceNonDeferralStart, + account.TypeTemplate, + account.Type, + account.AccountTemplate, + account.AccountTemplateTaxTemplate, + account.Account, + account.AccountDeferral, + account.AccountTax, + account.OpenChartAccountStart, + account.GeneralLedgerAccount, + account.GeneralLedgerAccountContext, + account.GeneralLedgerLine, + account.GeneralLedgerLineContext, + account.BalanceSheetContext, + account.BalanceSheetComparisionContext, + account.IncomeStatementContext, + account.CreateChartStart, + account.CreateChartAccount, + account.CreateChartProperties, + account.UpdateChartStart, + account.UpdateChartSucceed, + account.AgedBalanceContext, + account.AgedBalance, configuration.Configuration, configuration.ConfigurationDefaultAccount, configuration.DefaultTaxRule, - Period, - Journal, - JournalSequence, - JournalCashContext, - JournalPeriod, - Move, - Reconciliation, + period.Period, + journal.Journal, + journal.JournalSequence, + journal.JournalCashContext, + journal.JournalPeriod, + move.Move, + move.Reconciliation, configuration.ConfigurationTaxRounding, - Line, - WriteOff, - OpenJournalAsk, - ReconcileLinesWriteOff, - ReconcileShow, - CancelMovesDefault, - GroupLinesStart, - PrintGeneralJournalStart, + move.Line, + move.WriteOff, + move.OpenJournalAsk, + move.ReconcileLinesWriteOff, + move.ReconcileShow, + move.CancelMovesDefault, + move.GroupLinesStart, + move.PrintGeneralJournalStart, tax.TaxGroup, tax.TaxCodeTemplate, tax.TaxCode, @@ -73,41 +73,43 @@ tax.TaxRuleLine, tax.TestTaxView, tax.TestTaxViewResult, - MoveTemplate, - MoveTemplateKeyword, - MoveLineTemplate, - TaxLineTemplate, - CreateMoveTemplate, - CreateMoveKeywords, + move_template.MoveTemplate, + move_template.MoveTemplateKeyword, + move_template.MoveLineTemplate, + move_template.TaxLineTemplate, + move_template.CreateMoveTemplate, + move_template.CreateMoveKeywords, party.Party, party.PartyAccount, - RenewFiscalYearStart, + fiscalyear.CreatePeriodsStart, + fiscalyear.RenewFiscalYearStart, module='account', type_='model') Pool.register( - OpenType, - BalanceNonDeferral, - OpenChartAccount, - CreateChart, - UpdateChart, - OpenJournal, - OpenAccount, - ReconcileLines, - UnreconcileLines, - Reconcile, - CancelMoves, - GroupLines, - PrintGeneralJournal, - CreateMove, + account.OpenType, + fiscalyear.BalanceNonDeferral, + account.OpenChartAccount, + account.CreateChart, + account.UpdateChart, + move.OpenJournal, + move.OpenAccount, + move.ReconcileLines, + move.UnreconcileLines, + move.Reconcile, + move.CancelMoves, + move.GroupLines, + move.PrintGeneralJournal, + move_template.CreateMove, tax.OpenChartTaxCode, tax.OpenTaxCode, tax.TestTax, party.PartyReplace, party.PartyErase, - RenewFiscalYear, + fiscalyear.CreatePeriods, + fiscalyear.RenewFiscalYear, module='account', type_='wizard') Pool.register( - GeneralLedger, - TrialBalance, - AgedBalanceReport, - GeneralJournal, + account.GeneralLedger, + account.TrialBalance, + account.AgedBalanceReport, + move.GeneralJournal, module='account', type_='report') diff -r 69a5c20480c8 -r 2330071b33cf fiscalyear.py --- a/fiscalyear.py Sun Jan 12 10:21:51 2020 +0100 +++ b/fiscalyear.py Tue Feb 04 10:10:32 2020 +0100 @@ -6,18 +6,16 @@ from trytond.i18n import gettext from trytond.model import ModelView, ModelSQL, Workflow, fields from trytond.model.exceptions import AccessError -from trytond.wizard import Wizard, StateView, StateAction, Button +from trytond.pool import Pool from trytond.pyson import Eval, If, PYSONEncoder +from trytond.rpc import RPC from trytond.transaction import Transaction -from trytond.pool import Pool +from trytond.wizard import ( + Wizard, StateView, StateTransition, StateAction, Button) from .exceptions import (FiscalYearNotFoundError, FiscalYearDatesError, FiscalYearSequenceError, FiscalYearCloseError, FiscalYearReOpenError) -__all__ = ['FiscalYear', - 'BalanceNonDeferralStart', 'BalanceNonDeferral', - 'RenewFiscalYearStart', 'RenewFiscalYear'] - STATES = { 'readonly': Eval('state') != 'open', } @@ -73,12 +71,7 @@ ('close', 'open'), )) cls._buttons.update({ - 'create_period': { - 'invisible': ((Eval('state') != 'open') - | Eval('periods', [0])), - 'depends': ['state'], - }, - 'create_period_3': { + 'create_periods': { 'invisible': ((Eval('state') != 'open') | Eval('periods', [0])), 'depends': ['state'], @@ -96,6 +89,9 @@ 'depends': ['state'], }, }) + cls.__rpc__.update({ + 'create_period': RPC(readonly=False, instantiate=0), + }) @staticmethod def default_state(): @@ -181,8 +177,7 @@ super(FiscalYear, cls).delete(fiscalyears) @classmethod - @ModelView.button - def create_period(cls, fiscalyears, interval=1): + def create_period(cls, fiscalyears, interval=1, end_day=31): ''' Create periods for the fiscal years with month interval ''' @@ -191,9 +186,10 @@ for fiscalyear in fiscalyears: period_start_date = fiscalyear.start_date while period_start_date < fiscalyear.end_date: - period_end_date = period_start_date + \ - relativedelta(months=interval - 1) + \ - relativedelta(day=31) + month_offset = 1 if period_start_date.day < end_day else 0 + period_end_date = (period_start_date + + relativedelta(months=interval - month_offset) + + relativedelta(day=end_day)) if period_end_date > fiscalyear.end_date: period_end_date = fiscalyear.end_date name = period_start_date.strftime('%Y-%m') @@ -211,12 +207,9 @@ Period.create(to_create) @classmethod - @ModelView.button - def create_period_3(cls, fiscalyears): - ''' - Create periods for the fiscal years with 3 months interval - ''' - cls.create_period(fiscalyears, interval=3) + @ModelView.button_action('account.act_create_periods') + def create_periods(cls, fiscalyears): + pass @classmethod def find(cls, company_id, date=None, exception=True): @@ -476,8 +469,67 @@ return action, {} +class CreatePeriodsStart(ModelView): + "Create Periods Start" + __name__ = 'account.fiscalyear.create_periods.start' + frequency = fields.Selection([ + ('monthly', "Monthly"), + ('quarterly', "Quarterly"), + ('other', "Other"), + ], "Frequency", sort=False, required=True) + interval = fields.Integer("Interval", required=True, + states={ + 'invisible': Eval('frequency') != 'other', + }, + depends=['frequency'], + help="The length of each period, in months.") + end_day = fields.Integer("End Day", required=True, + help="The day of the month on which periods end.\n" + "Months with fewer days will end on the last day.") + + @classmethod + def default_frequency(cls): + return 'monthly' + + @classmethod + def default_end_day(cls): + return 31 + + @classmethod + def frequency_intervals(cls): + return { + 'monthly': 1, + 'quarterly': 3, + 'other': None, + } + + @fields.depends('frequency', 'interval') + def on_change_frequency(self): + if self.frequency: + self.interval = self.frequency_intervals()[self.frequency] + + +class CreatePeriods(Wizard): + "Create Periods" + __name__ = 'account.fiscalyear.create_periods' + start = StateView('account.fiscalyear.create_periods.start', + 'account.fiscalyear_create_periods_start_view_form', [ + Button("Cancel", 'end', 'tryton-cancel'), + Button("Create", 'create_periods', 'tryton-ok', default=True), + ]) + create_periods = StateTransition() + + def transition_create_periods(self): + FiscalYear = Pool().get('account.fiscalyear') + fiscalyear = FiscalYear(Transaction().context['active_id']) + FiscalYear.create_period( + [fiscalyear], self.start.interval, self.start.end_day) + return 'end' + + def month_delta(d1, d2): - return (d1.year - d2.year) * 12 + d1.month - d2.month + month_offset = 1 if d1.day < d2.day else 0 + return (d1.year - d2.year) * 12 + d1.month - d2.month - month_offset class RenewFiscalYearStart(ModelView): @@ -577,10 +629,12 @@ periods = [p for p in self.start.previous_fiscalyear.periods if p.type == 'standard'] months = month_delta(fiscalyear.end_date, fiscalyear.start_date) + 1 - if len(periods) == months: - FiscalYear.create_period([fiscalyear]) - elif len(periods) == months / 3: - FiscalYear.create_period_3([fiscalyear]) + interval = months / len(periods) + end_day = max(p.end_date.day + for p in self.start.previous_fiscalyear.periods + if p.type == 'standard') + if interval.is_integer(): + FiscalYear.create_period([fiscalyear], interval, end_day) return fiscalyear def do_create_(self, action): diff -r 69a5c20480c8 -r 2330071b33cf fiscalyear.xml --- a/fiscalyear.xml Sun Jan 12 10:21:51 2020 +0100 +++ b/fiscalyear.xml Tue Feb 04 10:10:32 2020 +0100 @@ -54,16 +54,9 @@ <field name="perm_delete" eval="True"/> </record> - <record model="ir.model.button" id="fiscalyear_create_period_button"> - <field name="name">create_period</field> - <field name="string">Create Monthly Periods</field> - <field name="model" - search="[('model', '=', 'account.fiscalyear')]"/> - </record> - - <record model="ir.model.button" id="fiscalyear_create_period_3_button"> - <field name="name">create_period_3</field> - <field name="string">Create 3 Monthly Periods</field> + <record model="ir.model.button" id="fiscalyear_create_periods_button"> + <field name="name">create_periods</field> + <field name="string">Create Periods</field> <field name="model" search="[('model', '=', 'account.fiscalyear')]"/> </record> @@ -114,6 +107,16 @@ <menuitem parent="menu_processing" sequence="40" action="act_fiscalyear_form_close" id="menu_close_fiscalyear"/> + <record model="ir.ui.view" id="fiscalyear_create_periods_start_view_form"> + <field name="model">account.fiscalyear.create_periods.start</field> + <field name="type">form</field> + <field name="name">fiscalyear_create_periods_start_form</field> + </record> + <record model="ir.action.wizard" id="act_create_periods"> + <field name="name">Create Periods</field> + <field name="wiz_name">account.fiscalyear.create_periods</field> + </record> + <record model="ir.ui.view" id="fiscalyear_balance_non_deferral_start_view_form"> <field diff -r 69a5c20480c8 -r 2330071b33cf tests/test_account.py --- a/tests/test_account.py Sun Jan 12 10:21:51 2020 +0100 +++ b/tests/test_account.py Tue Feb 04 10:10:32 2020 +0100 @@ -81,13 +81,17 @@ create_chart.transition_create_properties() -def get_fiscalyear(company, today=None): +def get_fiscalyear(company, today=None, start_date=None, end_date=None): pool = Pool() Sequence = pool.get('ir.sequence') FiscalYear = pool.get('account.fiscalyear') if not today: today = datetime.date.today() + if not start_date: + start_date = today.replace(month=1, day=1) + if not end_date: + end_date = today.replace(month=12, day=31) sequence, = Sequence.create([{ 'name': '%s' % today.year, @@ -95,8 +99,8 @@ 'company': company.id, }]) fiscalyear = FiscalYear(name='%s' % today.year, company=company) - fiscalyear.start_date = today.replace(month=1, day=1) - fiscalyear.end_date = today.replace(month=12, day=31) + fiscalyear.start_date = start_date + fiscalyear.end_date = end_date fiscalyear.post_move_sequence = sequence return fiscalyear @@ -206,6 +210,45 @@ self.assertEqual(len(fiscalyear.periods), 12) @with_transaction() + def test_fiscalyear_create_periods(self): + 'Test fiscalyear create periods' + FiscalYear = Pool().get('account.fiscalyear') + + company = create_company() + with set_company(company): + year = datetime.date.today().year + date = datetime.date + for start_date, end_date, interval, end_day, num_periods in [ + (date(year, 1, 1), date(year, 12, 31), 1, 31, 12), + (date(year + 1, 1, 1), date(year + 1, 12, 31), 3, 31, 4), + (date(year + 2, 1, 1), date(year + 2, 12, 31), 5, 31, 3), + (date(year + 3, 4, 6), date(year + 4, 4, 5), 1, 5, 12), + (date(year + 4, 4, 6), date(year + 5, 4, 5), 3, 5, 4), + (date(year + 5, 4, 6), date(year + 6, 4, 5), 5, 5, 3), + (date(year + 6, 6, 6), date(year + 6, 12, 31), 1, 29, 8), + (date(year + 7, 7, 7), date(year + 7, 12, 31), 3, 29, 3), + (date(year + 8, 1, 1), date(year + 9, 8, 7), 1, 29, 20), + (date(year + 9, 9, 9), date(year + 10, 11, 12), 3, 29, 5), + ]: + fiscalyear = get_fiscalyear( + company, start_date, start_date, end_date) + fiscalyear.save() + FiscalYear.create_period([fiscalyear], interval, end_day) + + self.assertEqual(len(fiscalyear.periods), num_periods) + + self.assertEqual(fiscalyear.periods[-1].end_date, end_date) + self.assertTrue(all( + p.end_date == p.end_date + relativedelta(day=end_day) + for p in fiscalyear.periods[:-1])) + + self.assertEqual(fiscalyear.periods[0].start_date, start_date) + self.assertTrue(all( + p1.end_date + relativedelta(days=1) == p2.start_date + for p1, p2 in zip( + fiscalyear.periods[:-1], fiscalyear.periods[1:]))) + + @with_transaction() def test_account_debit_credit(self): 'Test account debit/credit' pool = Pool() diff -r 69a5c20480c8 -r 2330071b33cf view/fiscalyear_create_periods_start_form.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/fiscalyear_create_periods_start_form.xml Tue Feb 04 10:10:32 2020 +0100 @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- This file is part of Tryton. The COPYRIGHT file at the top level of +this repository contains the full copyright notices and license terms. --> +<form> + <label name="frequency"/> + <field name="frequency"/> + <label name="interval" string="every"/> + <group col="2" id="interval"> + <field name="interval" xexpand="0"/> + <label name="interval" string="months" xalign="0.0" xexpand="1"/> + </group> + <label name="end_day"/> + <field name="end_day"/> +</form> diff -r 69a5c20480c8 -r 2330071b33cf view/fiscalyear_form.xml --- a/view/fiscalyear_form.xml Sun Jan 12 10:21:51 2020 +0100 +++ b/view/fiscalyear_form.xml Tue Feb 04 10:10:32 2020 +0100 @@ -14,8 +14,7 @@ <page string="Periods" id="periods"> <field name="periods" colspan="4"/> <group col="-1" colspan="4" id="buttons"> - <button name="create_period"/> - <button name="create_period_3"/> + <button name="create_periods"/> </group> </page> <page string="Sequences" id="sequences">