diff --git a/CHANGELOG b/CHANGELOG index c4d945f8e6..3838ecbd27 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add optional taxes date on invoice line * Convert Invoice.update_taxes into dualmethod * Rename invoice state from cancel to cancelled diff --git a/invoice.py b/invoice.py index 9e1721ce66..0158f14725 100644 --- a/invoice.py +++ b/invoice.py @@ -820,20 +820,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): @property def taxable_lines(self): taxable_lines = [] - # In case we're called from an on_change we have to use some sensible - # defaults for line in self.lines: - if getattr(line, 'type', None) != 'line': - continue - taxable_lines.append(tuple()) - for attribute, default_value in [ - ('taxes', []), - ('unit_price', Decimal(0)), - ('quantity', 0.), - ]: - value = getattr(line, attribute, None) - taxable_lines[-1] += ( - value if value is not None else default_value,) + if getattr(line, 'type', None) == 'line': + taxable_lines.extend(line.taxable_lines) return taxable_lines @property @@ -1744,6 +1733,15 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin): 'readonly': _states['readonly'] | ~Bool(Eval('account')), }, depends=['type', 'invoice_type', 'company', 'account'] + _depends) + taxes_date = fields.Date( + "Taxes Date", + states={ + 'invisible': Eval('type') != 'line', + 'readonly': _states['readonly'], + }, + depends=['type'] + _depends, + help="The date at which the taxes are computed.\n" + "Leave empty for the accounting date.") invoice_taxes = fields.Function(fields.Many2Many('account.invoice.tax', None, None, 'Invoice Taxes'), 'get_invoice_taxes') origin = fields.Reference('Origin', selection='get_origin', select=True, @@ -1920,11 +1918,18 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin): @property def taxable_lines(self): - return [(self.taxes, self.unit_price, self.quantity)] + # In case we're called from an on_change we have to use some sensible + # defaults + return [( + getattr(self, 'taxes', None) or [], + getattr(self, 'unit_price', None) or Decimal(0), + getattr(self, 'quantity', None) or 0, + getattr(self, 'tax_date', None), + )] @property def tax_date(self): - return self.invoice.tax_date + return self.taxes_date or self.invoice.tax_date def _get_tax_context(self): if self.invoice: @@ -2254,6 +2259,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin): for field in ('sequence', 'type', 'invoice_type', 'unit_price', 'description', 'unit', 'product', 'account'): setattr(line, field, getattr(self, field)) + line.taxes_date = self.tax_date line.taxes = self.taxes return line diff --git a/tests/scenario_invoice.rst b/tests/scenario_invoice.rst index 5dab753670..dcab12c817 100644 --- a/tests/scenario_invoice.rst +++ b/tests/scenario_invoice.rst @@ -220,6 +220,12 @@ Credit invoice with refund:: 'cancelled' >>> invoice.reconciled == today True + >>> credit_note, = Invoice.find([ + ... ('type', '=', 'out'), ('id', '!=', invoice.id)]) + >>> credit_note.state + 'paid' + >>> all(line.taxes_date == today for line in credit_note.lines) + True >>> receivable.reload() >>> receivable.debit Decimal('240.00') diff --git a/view/invoice_line_form.xml b/view/invoice_line_form.xml index 5b37712d19..4ecddb1025 100644 --- a/view/invoice_line_form.xml +++ b/view/invoice_line_form.xml @@ -23,6 +23,10 @@ this repository contains the full copyright notices and license terms. -->