[FIX] l10n_ke_edi_tremol: allow multiple taxes per line

The existing implementation for the fiscal device applies a number of
limitations to the content of the invoice in order to ensure the invoice
can be regularised and serialised in such a way as to make it
transmissible to the device.

One of these checks was ensuring that only one tax can be applied per
line. This check was implemented as the device itself only accepts one
tax per line.

This check isn't entirely accurate, since the user should be able to add
taxes such as levies, service charges etc (non-vat taxes) to the the
line, and have them skipped when transmitting the invoice data to the
device.

As such, this commit adapts the code to alter the checks (allowing for
only one 'VAT' tax, and any number of other taxes). The way the price
total is determined is altered to use only the "VAT" tax and base amount
to get the price_total for the line from the corresponding line's tax
details.

This commit also adds a test.

closes odoo/odoo#148397

Task-id: None
X-original-commit: bb3a32c7f45ac5adb3dddf560f6af33f9bca3780
Signed-off-by: Josse Colpaert <jco@odoo.com>
Signed-off-by: Daniel Kosky (dako) <dako@odoo.com>
This commit is contained in:
Daniel Kosky (dako)
2024-01-05 23:10:47 +00:00
parent 5ad1a6db51
commit 0ea5ab6d1c
2 changed files with 52 additions and 10 deletions
@@ -68,16 +68,13 @@ class AccountMove(models.Model):
move_errors.append(_("This credit note must reference the previous invoice, and this previous invoice must have already been submitted."))
for line in self.invoice_line_ids.filtered(lambda l: l.display_type == 'product'):
if not line.tax_ids or len(line.tax_ids) > 1:
move_errors.append(_("On line %s, you must select one and only one tax.", line.name))
vat_taxes = line.tax_ids.filtered(lambda tax: tax.amount in (16, 8, 0))
if not vat_taxes or len(vat_taxes) > 1:
move_errors.append(_("On line %s, you must select one and only one VAT tax.", line.name))
else:
if line.tax_ids.amount == 0 and not line.tax_ids[0].l10n_ke_item_code_id:
if vat_taxes[0].amount == 0 and not line.tax_ids[0].l10n_ke_item_code_id:
move_errors.append(_("On line %s, a tax with a KRA item code must be selected, since the tax is 0%% or exempt.", line.name))
for tax in move.invoice_line_ids.tax_ids:
if tax.amount not in (16, 8, 0):
move_errors.append(_("Tax '%s' is used, but only taxes of 16%%, 8%%, 0%% or Exempt can be sent. Please reconfigure or change the tax.", tax.name))
if move_errors:
errors.append((move.name, move_errors))
@@ -167,12 +164,18 @@ class AccountMove(models.Model):
break
msgs = []
tax_details = self._prepare_edi_tax_details()
for line in self.invoice_line_ids.filtered(lambda l: l.display_type == 'product' and l.quantity and l.price_total > 0 and not discount_dict.get(l.id) >= 100):
# Here we use the original discount of the line, since it the distributed discount has not been applied in the price_total
price = round(line.price_total / abs(line.quantity) * 100 / (100 - line.discount), 2) * currency_rate
percentage = line.tax_ids[0].amount
price_total = 0
percentage = 0
item_code = line.tax_ids[0].l10n_ke_item_code_id
for tax in tax_details['invoice_line_tax_details'][line]['tax_details']:
if tax['tax'].amount in (16, 8, 0): # This should only occur once
tax_details = tax_details['invoice_line_tax_details'][line]['tax_details'][tax]
price_total = abs(tax_details['base_amount_currency']) + abs(tax_details['tax_amount_currency'])
percentage = tax['tax'].amount
price = round(price_total / abs(line.quantity) * 100 / (100 - line.discount), 2) * currency_rate
uom = line.product_uom_id and line.product_uom_id.name or ''
line_data = b';'.join([
@@ -187,3 +187,42 @@ class TestKeMoveExport(AccountTestInvoicingCommon):
expected_double_negative_header = [b'01; 0;0;1;Sirius Cybernetics Corporation;A000123456F ;Test StreetFurther Test Street;Test StreetFurther Test Street;00500Nairobi ; ;INV202300002 ']
expected_messages = expected_double_negative_header + expected_messages[1:]
self.assertEqual(generated_messages, expected_messages)
def test_export_multi_tax_line_invoice(self):
""" When handling invoices with multiple taxes per line, the export should handle the
reported amounts correctly. Using only the VAT taxes in its calculation and not, for
instance, the 2% tourism levy, or the 4% drinks service charge, or the 10% food service
charge.
"""
tourism_levy = self.env['account.tax'].create({
'name': 'Tourism levy',
'amount': 2,
'company_id': self.company_data['company'].id,
})
multi_tax_line_invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_a.id,
'invoice_line_ids': [
(0, 0, {
'product_id': self.product_a.id,
'quantity': 10,
'price_unit': 1000,
'tax_ids': [
(6, 0, [
self.company_data['company'].account_sale_tax_id.id,
tourism_levy.id,
]),
],
'discount': 25,
}),
],
})
multi_tax_line_invoice.action_post()
generated_messages = multi_tax_line_invoice._l10n_ke_cu_lines_messages()
expected_sale_line = self.line_dict_to_bytes({
'name': b'Infinite Improbability Drive ',
'price': b'1160.0', # This is the unit price, tax included, but only the 16% VAT
'quantity': b'10.0',
'discount': b'-25.0%',
})
self.assertEqual(generated_messages, [expected_sale_line])