diff --git a/CHANGELOG b/CHANGELOG index 15bbc9e546..2d8d04fa4b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add start/end date to tax * Use account of write-off journal as default * Add write-off journal type * Add description on write-off move diff --git a/tax.py b/tax.py index ede99e3eed..9c27e9f32d 100644 --- a/tax.py +++ b/tax.py @@ -1,5 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. +import datetime from decimal import Decimal from sql.aggregate import Sum @@ -347,6 +348,8 @@ class TaxTemplate(ModelSQL, ModelView): description = fields.Char('Description', required=True, translate=True) group = fields.Many2One('account.tax.group', 'Group') sequence = fields.Integer('Sequence') + start_date = fields.Date('Starting Date') + end_date = fields.Date('Ending Date') amount = fields.Numeric('Amount', digits=(16, 8), states={ 'required': Eval('type') == 'fixed', @@ -449,7 +452,8 @@ class TaxTemplate(ModelSQL, ModelView): res = {} for field in ('name', 'description', 'sequence', 'amount', 'rate', 'type', 'invoice_base_sign', 'invoice_tax_sign', - 'credit_note_base_sign', 'credit_note_tax_sign'): + 'credit_note_base_sign', 'credit_note_tax_sign', + 'start_date', 'end_date'): if not tax or getattr(tax, field) != getattr(self, field): res[field] = getattr(self, field) for field in ('group',): @@ -571,6 +575,8 @@ class Tax(ModelSQL, ModelView): }, depends=['parent']) active = fields.Boolean('Active') sequence = fields.Integer('Sequence', help='Use to order the taxes') + start_date = fields.Date('Starting Date') + end_date = fields.Date('Ending Date') currency_digits = fields.Function(fields.Integer('Currency Digits'), 'on_change_with_currency_digits') amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)), @@ -749,13 +755,17 @@ class Tax(ModelSQL, ModelView): } @classmethod - def _unit_compute(cls, taxes, price_unit): + def _unit_compute(cls, taxes, price_unit, date): res = [] for tax in taxes: + start_date = tax.start_date or datetime.date.min + end_date = tax.end_date or datetime.date.max + if not (start_date <= date <= end_date): + continue if tax.type != 'none': res.append(tax._process_tax(price_unit)) if len(tax.childs): - res.extend(cls._unit_compute(tax.childs, price_unit)) + res.extend(cls._unit_compute(tax.childs, price_unit, date)) return res @classmethod @@ -766,16 +776,20 @@ class Tax(ModelSQL, ModelView): return sorted(taxes, key=lambda t: (t.sequence, t.id)) @classmethod - def compute(cls, taxes, price_unit, quantity): + def compute(cls, taxes, price_unit, quantity, date=None): ''' - Compute taxes for price_unit and quantity. + Compute taxes for price_unit and quantity at the date. Return list of dict for each taxes and their childs with: base amount tax ''' + pool = Pool() + Date = pool.get('ir.date') + if date is None: + date = Date.today() taxes = cls.sort_taxes(taxes) - res = cls._unit_compute(taxes, price_unit) + res = cls._unit_compute(taxes, price_unit, date) quantity = Decimal(str(quantity or 0.0)) for row in res: row['base'] *= quantity diff --git a/tests/test_account.py b/tests/test_account.py index da8ea40ea6..b7a9b9eae8 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -4,6 +4,7 @@ import unittest import doctest import datetime from decimal import Decimal +from dateutil.relativedelta import relativedelta import trytond.tests.test_tryton from trytond.tests.test_tryton import test_view, test_depends, doctest_dropdb from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT @@ -31,6 +32,7 @@ class AccountTestCase(unittest.TestCase): self.period = POOL.get('account.period') self.balance_non_deferral = POOL.get( 'account.fiscalyear.balance_non_deferral', type='wizard') + self.tax = POOL.get('account.tax') def test0005views(self): 'Test views' @@ -316,6 +318,111 @@ class AccountTestCase(unittest.TestCase): transaction.cursor.rollback() + def test0040tax_compute(self): + 'Test tax compute' + with Transaction().start(DB_NAME, USER, context=CONTEXT): + today = datetime.date.today() + + tax_account, = self.account.search([ + ('name', '=', 'Main Tax'), + ]) + tax = self.tax() + tax.name = tax.description = 'Test' + tax.type = 'none' + tax.invoice_account = tax_account + tax.credit_note_account = tax_account + + child1 = self.tax() + child1.name = child1.description = 'Child 1' + child1.type = 'percentage' + child1.rate = Decimal('0.2') + child1.invoice_account = tax_account + child1.credit_note_account = tax_account + child1.save() + + child2 = self.tax() + child2.name = child2.description = 'Child 1' + child2.type = 'fixed' + child2.amount = Decimal('10') + child2.invoice_account = tax_account + child2.credit_note_account = tax_account + child2.save() + + tax.childs = [child1, child2] + tax.save() + + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2), + [{ + 'base': Decimal('200'), + 'amount': Decimal('40.0'), + 'tax': child1, + }, { + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + + child1.end_date = today + relativedelta(days=5) + child1.save() + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, today), + [{ + 'base': Decimal('200'), + 'amount': Decimal('40.0'), + 'tax': child1, + }, { + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + + child1.start_date = today + relativedelta(days=1) + child1.save() + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, today), + [{ + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, + today + relativedelta(days=1)), [{ + 'base': Decimal('200'), + 'amount': Decimal('40.0'), + 'tax': child1, + }, { + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, + today + relativedelta(days=5)), [{ + 'base': Decimal('200'), + 'amount': Decimal('40.0'), + 'tax': child1, + }, { + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, + today + relativedelta(days=6)), [{ + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + + child1.end_date = None + child1.save() + self.assertEqual(self.tax.compute([tax], Decimal('100'), 2, + today + relativedelta(days=6)), [{ + 'base': Decimal('200'), + 'amount': Decimal('40.0'), + 'tax': child1, + }, { + 'base': Decimal('200'), + 'amount': Decimal('20'), + 'tax': child2, + }]) + def suite(): suite = trytond.tests.test_tryton.suite() diff --git a/view/tax_form.xml b/view/tax_form.xml index 6c4985462d..dc1fb2e40c 100644 --- a/view/tax_form.xml +++ b/view/tax_form.xml @@ -14,6 +14,10 @@ this repository contains the full copyright notices and license terms. -->