Odoo Version
Steps to Reproduce
Fresh database, only account installed, no demo data, generic chart of accounts (reproduced on 18.0 community @ df8a3fb0ccf; the same code is in 19.0).
-
Set the company tax_calculation_rounding_method to round_globally.
-
Create two sale taxes, both price-included: 10% incl and 22% incl.
-
Create a customer invoice with these four lines (quantity 1):
| Line |
Price |
Tax |
| A |
28.00 |
10% incl |
| A discount |
-28.00 |
10% incl |
| B |
16.00 |
22% incl |
| C |
24.00 |
22% incl |
This is a typical e-commerce order: a gift product fully discounted, plus two paid products at a different VAT rate. No third-party module is involved: the example uses only account on a fresh database.
-
Confirm the invoice and look at the journal items.
Current Behavior
| Line |
Tax |
price_subtotal |
balance |
tax_base_amount |
| A |
10% incl |
25.45 |
-25.46 |
|
| A discount |
10% incl |
-25.45 |
25.45 |
|
| B |
22% incl |
13.11 |
-13.11 |
|
| C |
22% incl |
19.67 |
-19.67 |
|
| Tax 22% incl |
|
|
-7.21 |
32.79 |
- The 22% tax is computed globally: 40.00 / 1.22 × 0.22 = 7.21, so its base is 32.79, and the tax line says so (
tax_base_amount = 32.79).
- The 22% base lines only sum to 13.11 + 19.67 = 32.78. The missing cent is put on line A, which carries the 10% tax.
- As a result the journal items show a 10% base of 0.01 with no 10% tax line, and a 22% base of 32.78 instead of 32.79.
Why: account.tax._round_tax_details_base_lines (in _round_tax_details_base_lines, grouping_function) groups base lines only by currency and is_refund (19.0 adds computation_key), not by tax. The delta of the whole document is then given, through _distribute_delta_amount_smoothly, to the line with the largest absolute amount. Here that is line A (|28.00|), which is fully discounted and at another rate.
Consequences:
- The core contradicts itself on the same move: the tax line says the 22% base is 32.79, the base lines say 32.78.
- Tax reports built on tax lines and analyses of journal items grouped by tax (
tax_ids) differ by one cent per affected document. In our production database this happened on 10 documents over 9 months, in both directions (10% → 22% and 22% → 10%), so it becomes very hard to reconcile.
- A tax gets a taxable base (0.01) with no tax amount, and a line that is 100% discounted carries a non-zero base.
Expected Behavior
A rounding delta generated by a tax should stay on base lines that carry that same tax. In the example, the cent should go to line C (24.00, 22% incl), giving:
| Line |
Tax |
balance |
| A |
10% incl |
-25.45 |
| A discount |
10% incl |
25.45 |
| B |
22% incl |
-13.11 |
| C |
22% incl |
-19.68 |
The document total (40.00) and the tax amounts stay unchanged. Only the base line that absorbs the rounding changes.
A possible approach: first distribute the delta inside groups of lines with the same taxes. Then distribute any residual delta on the whole document, as the current code does, so the total still matches the sum of the lines with taxes included.
Related
Reproduction script (odoo-bin shell, rolls back)
company = env.company
company.tax_calculation_rounding_method = 'round_globally'
Tax = env['account.tax']
def mk(name, amt):
return Tax.create({'name': name, 'amount': amt, 'amount_type': 'percent', 'type_tax_use': 'sale', 'price_include_override': 'tax_included', 'company_id': company.id})
t10 = mk('REPRO 10% incl', 10); t22 = mk('REPRO 22% incl', 22)
partner = env['res.partner'].create({'name': 'Repro'})
L = lambda n, p, t: (0, 0, {'name': n, 'quantity': 1, 'price_unit': p, 'tax_ids': [(6, 0, t.ids)]})
mv = env['account.move'].create({'move_type': 'out_invoice', 'partner_id': partner.id, 'currency_id': company.currency_id.id, 'invoice_line_ids': [
L('A', 28, t10), L('A discount', -28, t10), L('B', 16, t22), L('C', 24, t22)]})
mv.action_post()
for l in mv.line_ids.sorted('id'):
print(f"{l.name:12} {l.display_type:12} subtotal={l.price_subtotal:8.2f} balance={l.balance:8.2f} tax_base_amount={l.tax_base_amount:8.2f} taxes={l.tax_ids.mapped('name') or l.tax_line_id.name}")
print('amount_total', mv.amount_total, 'currency', mv.currency_id.name)
env.cr.rollback()
Odoo Version
Steps to Reproduce
Fresh database, only
accountinstalled, no demo data, generic chart of accounts (reproduced on 18.0 community @df8a3fb0ccf; the same code is in 19.0).Set the company
tax_calculation_rounding_methodtoround_globally.Create two sale taxes, both price-included:
10% incland22% incl.Create a customer invoice with these four lines (quantity 1):
This is a typical e-commerce order: a gift product fully discounted, plus two paid products at a different VAT rate. No third-party module is involved: the example uses only
accounton a fresh database.Confirm the invoice and look at the journal items.
Current Behavior
price_subtotalbalancetax_base_amounttax_base_amount= 32.79).Why:
account.tax._round_tax_details_base_lines(in_round_tax_details_base_lines,grouping_function) groups base lines only bycurrencyandis_refund(19.0 addscomputation_key), not by tax. The delta of the whole document is then given, through_distribute_delta_amount_smoothly, to the line with the largest absolute amount. Here that is line A (|28.00|), which is fully discounted and at another rate.Consequences:
tax_ids) differ by one cent per affected document. In our production database this happened on 10 documents over 9 months, in both directions (10% → 22% and 22% → 10%), so it becomes very hard to reconcile.Expected Behavior
A rounding delta generated by a tax should stay on base lines that carry that same tax. In the example, the cent should go to line C (24.00, 22% incl), giving:
balanceThe document total (40.00) and the tax amounts stay unchanged. Only the base line that absorbs the rounding changes.
A possible approach: first distribute the delta inside groups of lines with the same taxes. Then distribute any residual delta on the whole document, as the current code does, so the total still matches the sum of the lines with taxes included.
Related
Reproduction script (odoo-bin shell, rolls back)