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. -->
+
+
+
+