Enable periods to end on any day of the month
issue8855 review252671002
This commit is contained in:
1 parent
cf6a464261
commit
96a6fb6bce
7 files changed
+233
-116
No files matched your search
@@ -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
|
||||
|
||||
+74
-72
@@ -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 tax
|
||||
from . import fiscalyear
|
||||
from . import journal
|
||||
from . import move
|
||||
from . import move_template
|
||||
from . import party
|
||||
from . import period
|
||||
from . import tax
|
||||
|
||||
|
||||
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 @@ def register():
|
||||
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')
|
||||
+83
-29
@@ -6,18 +6,16 @@ from dateutil.relativedelta import relativedelta
|
||||
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.pyson import Eval, If, PYSONEncoder
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If, PYSONEncoder
|
||||
from trytond.rpc import RPC
|
||||
from trytond.transaction import Transaction
|
||||
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 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
('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 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
'depends': ['state'],
|
||||
},
|
||||
})
|
||||
cls.__rpc__.update({
|
||||
'create_period': RPC(readonly=False, instantiate=0),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_state():
|
||||
@@ -181,8 +177,7 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
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 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
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 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
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 @@ class BalanceNonDeferral(Wizard):
|
||||
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 @@ class RenewFiscalYear(Wizard):
|
||||
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):
|
||||
|
||||
+13
-10
@@ -54,16 +54,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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
|
||||
|
||||
+46
-3
@@ -81,13 +81,17 @@ def create_chart(company, tax=False):
|
||||
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 @@ def get_fiscalyear(company, today=None):
|
||||
'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
|
||||
|
||||
@@ -205,6 +209,45 @@ class AccountTestCase(ModuleTestCase):
|
||||
FiscalYear.create_period([fiscalyear])
|
||||
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'
|
||||
|
||||
@@ -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>
|
||||
@@ -14,8 +14,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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">
|
||||
|
||||
Reference in new issue
Block a user