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">

Reply via email to