Add deductible rate of taxes to supplier invoice
issue6911 review341801002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add deductible rate of taxes to supplier invoice
|
||||
* Add method to post invoices by batch
|
||||
* Add payment term date in invoice
|
||||
* Raise warning on invoice with maturity dates on the past
|
||||
|
||||
@@ -7,10 +7,17 @@ from trytond.pool import PoolMeta
|
||||
class Company(metaclass=PoolMeta):
|
||||
__name__ = 'company.company'
|
||||
|
||||
purchase_taxes_expense = fields.Boolean(
|
||||
"Purchase Taxes as Expense",
|
||||
help="Check to book purchase taxes as expense.")
|
||||
cancel_invoice_out = fields.Boolean(
|
||||
"Cancel Customer Invoice",
|
||||
help="Allow cancelling move of customer invoice.")
|
||||
|
||||
@classmethod
|
||||
def default_purchase_taxes_expense(cls):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def default_cancel_invoice_out(cls):
|
||||
return False
|
||||
|
||||
@@ -42,6 +42,8 @@ setting in the `Account Configuration <account:model-account.configuration>`.
|
||||
Additional taxes can be manually added to the invoice when required.
|
||||
It is also possible to change calculated tax amounts, although these changes
|
||||
get overwritten if the invoice's tax amounts get recalculated.
|
||||
For supplier invoice it is possible to define per line the deductible rate of
|
||||
the taxes.
|
||||
|
||||
When an invoice is processed an `Account Move <account:model-account.move>` is
|
||||
automatically created for it.
|
||||
|
||||
+71
-7
@@ -1820,6 +1820,16 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
'readonly': _states['readonly'] | ~Bool(Eval('account')),
|
||||
},
|
||||
depends=['type', 'invoice_type', 'company', 'account'] + _depends)
|
||||
taxes_deductible_rate = fields.Numeric(
|
||||
"Taxes Deductible Rate", digits=(14, 10),
|
||||
domain=[
|
||||
('taxes_deductible_rate', '>=', 0),
|
||||
('taxes_deductible_rate', '<=', 1),
|
||||
],
|
||||
states={
|
||||
'invisible': Eval('invoice_type') != 'in',
|
||||
},
|
||||
depends=['invoice_type'])
|
||||
taxes_date = fields.Date(
|
||||
"Taxes Date",
|
||||
states={
|
||||
@@ -1908,11 +1918,24 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
return Invoice.fields_get(['type'])['type']['selection'] + [(None, '')]
|
||||
|
||||
@fields.depends(
|
||||
'invoice', '_parent_invoice.currency', '_parent_invoice.company')
|
||||
'invoice', '_parent_invoice.currency', '_parent_invoice.company',
|
||||
'_parent_invoice.type',
|
||||
methods=['on_change_company'])
|
||||
def on_change_invoice(self):
|
||||
if self.invoice:
|
||||
self.currency = self.invoice.currency
|
||||
self.company = self.invoice.company
|
||||
self.on_change_company()
|
||||
self.invoice_type = self.invoice.type
|
||||
|
||||
@fields.depends('company', 'invoice',
|
||||
'_parent_invoice.type', 'invoice_type')
|
||||
def on_change_company(self):
|
||||
invoice_type = self.invoice.type if self.invoice else self.invoice_type
|
||||
if (invoice_type == 'in'
|
||||
and self.company
|
||||
and self.company.purchase_taxes_expense):
|
||||
self.taxes_deductible_rate = 0
|
||||
|
||||
@staticmethod
|
||||
def default_currency():
|
||||
@@ -1984,14 +2007,26 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
return self.currency.digits
|
||||
return 2
|
||||
|
||||
@fields.depends('type', 'quantity', 'unit_price', 'invoice',
|
||||
'_parent_invoice.currency', 'currency')
|
||||
@fields.depends(
|
||||
'type', 'quantity', 'unit_price', 'taxes_deductible_rate', 'invoice',
|
||||
'_parent_invoice.currency', 'currency', 'taxes',
|
||||
'_parent_invoice.type', 'invoice_type',
|
||||
methods=['_get_taxes'])
|
||||
def on_change_with_amount(self):
|
||||
if self.type == 'line':
|
||||
currency = (self.invoice.currency if self.invoice
|
||||
else self.currency)
|
||||
amount = (Decimal(str(self.quantity or '0.0'))
|
||||
* (self.unit_price or Decimal('0.0')))
|
||||
invoice_type = (
|
||||
self.invoice.type if self.invoice else self.invoice_type)
|
||||
if invoice_type == 'in' and self.taxes_deductible_rate != 1:
|
||||
with Transaction().set_context(_deductible_rate=1):
|
||||
tax_amount = sum(
|
||||
t['amount'] for t in self._get_taxes().values())
|
||||
non_deductible_amount = (
|
||||
tax_amount * (1 - self.taxes_deductible_rate))
|
||||
amount += non_deductible_amount
|
||||
if currency:
|
||||
return currency.round(amount)
|
||||
return amount
|
||||
@@ -2004,8 +2039,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
subtotal = Decimal(0)
|
||||
for line2 in self.invoice.lines:
|
||||
if line2.type == 'line':
|
||||
subtotal += line2.invoice.currency.round(
|
||||
Decimal(str(line2.quantity)) * line2.unit_price)
|
||||
subtotal += line2.amount
|
||||
elif line2.type == 'subtotal':
|
||||
if self == line2:
|
||||
break
|
||||
@@ -2020,20 +2054,43 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
return self.origin.invoice.rec_name
|
||||
return self.origin.rec_name if self.origin else None
|
||||
|
||||
@classmethod
|
||||
def default_taxes_deductible_rate(cls):
|
||||
return 1
|
||||
|
||||
@property
|
||||
def taxable_lines(self):
|
||||
# In case we're called from an on_change we have to use some sensible
|
||||
# defaults
|
||||
context = Transaction().context
|
||||
if (getattr(self, 'invoice', None)
|
||||
and getattr(self.invoice, 'type', None)):
|
||||
invoice_type = self.invoice.type
|
||||
else:
|
||||
invoice_type = getattr(self, 'invoice_type', None)
|
||||
if invoice_type == 'in':
|
||||
if context.get('_deductible_rate') is not None:
|
||||
deductible_rate = context['_deductible_rate']
|
||||
else:
|
||||
deductible_rate = getattr(self, 'taxes_deductible_rate', 1)
|
||||
if not deductible_rate:
|
||||
return []
|
||||
else:
|
||||
deductible_rate = 1
|
||||
return [(
|
||||
getattr(self, 'taxes', None) or [],
|
||||
getattr(self, 'unit_price', None) or Decimal(0),
|
||||
((getattr(self, 'unit_price', None) or Decimal(0))
|
||||
* deductible_rate),
|
||||
getattr(self, 'quantity', None) or 0,
|
||||
getattr(self, 'tax_date', None),
|
||||
)]
|
||||
|
||||
@property
|
||||
def tax_date(self):
|
||||
return self.taxes_date or self.invoice.tax_date
|
||||
if getattr(self, 'taxes_date', None):
|
||||
return self.taxes_date
|
||||
elif hasattr(self, 'invoice') and hasattr(self.invoice, 'tax_date'):
|
||||
return self.invoice.tax_date
|
||||
|
||||
def _get_tax_context(self):
|
||||
if self.invoice:
|
||||
@@ -2074,6 +2131,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
@fields.depends('product', 'unit', '_parent_invoice.type',
|
||||
'_parent_invoice.party', 'party', 'invoice', 'invoice_type',
|
||||
'_parent_invoice.invoice_date', '_parent_invoice.accounting_date',
|
||||
'company',
|
||||
methods=['_get_tax_rule_pattern'])
|
||||
def on_change_product(self):
|
||||
if not self.product:
|
||||
@@ -2108,6 +2166,12 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
if tax_ids:
|
||||
taxes.extend(tax_ids)
|
||||
self.taxes = taxes
|
||||
|
||||
if self.company and self.company.purchase_taxes_expense:
|
||||
self.taxes_deductible_rate = 0
|
||||
else:
|
||||
self.taxes_deductible_rate = (
|
||||
self.product.supplier_taxes_deductible_rate_used)
|
||||
else:
|
||||
with Transaction().set_context(date=date):
|
||||
self.account = self.product.account_revenue_used
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
======================
|
||||
Invoice Tax Deductible
|
||||
======================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
>>> from trytond.modules.company.tests.tools import (
|
||||
... create_company, get_company)
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_fiscalyear, create_chart, get_accounts, create_tax)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice')
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
|
||||
Create company::
|
||||
|
||||
>>> _ = create_company()
|
||||
>>> company = get_company()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(company))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create chart of accounts::
|
||||
|
||||
>>> _ = create_chart(company)
|
||||
>>> accounts = get_accounts(company)
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.supplier_taxes_deductible_rate = Decimal('.5')
|
||||
>>> account_category.supplier_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('100')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Post a supplier invoice with 0% deductible::
|
||||
|
||||
>>> invoice = Invoice(type='in')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> line.unit_price = Decimal('50')
|
||||
>>> line.taxes_deductible_rate
|
||||
Decimal('0.5')
|
||||
>>> line.taxes_deductible_rate = Decimal(0)
|
||||
>>> line.amount
|
||||
Decimal('550.00')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('550.00'), Decimal('0.00'), Decimal('550.00'))
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('550.00'), Decimal('0'), Decimal('550.00'))
|
||||
>>> len(invoice.taxes)
|
||||
0
|
||||
|
||||
Post a supplier invoice with 50% deductible rate::
|
||||
|
||||
>>> invoice = Invoice(type='in')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> line.unit_price = Decimal('50')
|
||||
>>> line.amount
|
||||
Decimal('525.00')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('525.00'), Decimal('25.00'), Decimal('550.00'))
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('525.00'), Decimal('25.00'), Decimal('550.00'))
|
||||
>>> len(invoice.taxes)
|
||||
1
|
||||
@@ -301,6 +301,11 @@ def suite():
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite(
|
||||
'scenario_invoice_tax_deductible.rst',
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite('scenario_invoice_group_line.rst',
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
|
||||
@@ -4,6 +4,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page string="Invoice" id="invoice">
|
||||
<label name="purchase_taxes_expense"/>
|
||||
<field name="purchase_taxes_expense"/>
|
||||
<label name="cancel_invoice_out"/>
|
||||
<field name="cancel_invoice_out"/>
|
||||
</page>
|
||||
|
||||
@@ -22,10 +22,14 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="unit_price" symbol="currency"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount" symbol="currency"/>
|
||||
<field name="taxes" colspan="4"/>
|
||||
<label name="taxes_date"/>
|
||||
<field name="taxes_date"/>
|
||||
<newline/>
|
||||
<label name="taxes_deductible_rate"/>
|
||||
<group col="2" id="taxes_deductible_rate">
|
||||
<field name="taxes_deductible_rate" factor="100" xexpand="0"/>
|
||||
<label name="taxes_deductible_rate" string="%" xalign="0.0" xexpand="1"/>
|
||||
</group>
|
||||
<field name="taxes" colspan="4"/>
|
||||
|
||||
<label name="origin"/>
|
||||
<field name="origin" colspan="3"/>
|
||||
|
||||
Reference in New Issue
Block a user