[FIX] account: "Total" in company currency on invoice analysis
Currently for the "Total" measure on the invoice analysis report the values in the currency of the invoice are used. This is a problem in multi-currency environments: Different currencies are added together leading to nonsensical sums. This commit converts the "Total" measure to company currency (at the time of the invoice). In multi-company case the "Total" measure is further converted to the currency of the active company (using the day of the report creation for the conversion rate). The "Total" will also be displayed in company currency when viewing the invoices in the list view of an entry in the table (by clicking on the entry). task-3537868 closes odoo/odoo#141277 X-original-commit: 28004b7e92f22cf0f0078d4512f8a5f59c82122c Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
@@ -67,7 +67,21 @@ class AccountInvoiceReport(models.Model):
|
||||
|
||||
@property
|
||||
def _table_query(self):
|
||||
return '%s %s %s' % (self._select(), self._from(), self._where())
|
||||
return '%s %s %s %s' % (self._with(), self._select(), self._from(), self._where())
|
||||
|
||||
@api.model
|
||||
def _with(self):
|
||||
return f'''
|
||||
WITH company_currencies
|
||||
AS MATERIALIZED
|
||||
-- The table res_currency_rate only contains the individual dates where the currency rates changes (start date).
|
||||
-- The following table is similar but i.e. also contains an end date for each rate (the start date of the next rate
|
||||
-- or NULL if it is the last rate change).
|
||||
-- It is used to convert the value line.price_total to currency line.company_currency_id with conversion date line.date
|
||||
(SELECT *
|
||||
FROM ({self.env['res.currency']._select_companies_rates()}) AS all_company_currencies
|
||||
WHERE currency_id IN (SELECT id FROM res_currency currency WHERE currency.active))
|
||||
'''
|
||||
|
||||
@api.model
|
||||
def _select(self):
|
||||
@@ -95,7 +109,14 @@ class AccountInvoiceReport(models.Model):
|
||||
line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
AS quantity,
|
||||
-line.balance * currency_table.rate AS price_subtotal,
|
||||
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
-- line.price_total needs to be converted to line.company_currency_id (using the rate at the time of the invoice),
|
||||
-- get the correct sign and be converted to the currency of the active company (using the rate from today)
|
||||
ROUND(line.price_total
|
||||
* (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
* COALESCE(company_currency_conversion.rate, 1.0)
|
||||
/ COALESCE(line_currency_conversion.rate, 1.0)
|
||||
, company_currency.decimal_places)
|
||||
* currency_table.rate
|
||||
AS price_total,
|
||||
-COALESCE(
|
||||
-- Average line price
|
||||
@@ -119,6 +140,17 @@ class AccountInvoiceReport(models.Model):
|
||||
INNER JOIN account_move move ON move.id = line.move_id
|
||||
LEFT JOIN res_partner commercial_partner ON commercial_partner.id = move.commercial_partner_id
|
||||
JOIN {currency_table} ON currency_table.company_id = line.company_id
|
||||
LEFT JOIN company_currencies line_currency_conversion ON
|
||||
line.currency_id = line_currency_conversion.currency_id
|
||||
AND line.company_id = line_currency_conversion.company_id
|
||||
AND COALESCE(line.date, NOW()) >= line_currency_conversion.date_start
|
||||
AND (line_currency_conversion.date_end IS NULL OR line_currency_conversion.date_end > COALESCE(line.date, NOW()))
|
||||
LEFT JOIN company_currencies company_currency_conversion ON
|
||||
line.company_currency_id = company_currency_conversion.currency_id
|
||||
AND line.company_id = company_currency_conversion.company_id
|
||||
AND COALESCE(line.date, NOW()) >= company_currency_conversion.date_start
|
||||
AND (company_currency_conversion.date_end IS NULL OR company_currency_conversion.date_end > COALESCE(line.date, NOW()))
|
||||
LEFT JOIN res_currency company_currency ON company_currency.id = line.company_currency_id
|
||||
'''.format(
|
||||
currency_table=self.env['res.currency']._get_query_currency_table(self.env.companies.ids, fields.Date.today())
|
||||
)
|
||||
|
||||
@@ -94,6 +94,19 @@ class TestAccountInvoiceReport(AccountTestInvoicingCommon):
|
||||
}),
|
||||
]
|
||||
},
|
||||
{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': cls.partner_a.id,
|
||||
'invoice_date': fields.Date.from_string('1900-01-01'), # before the first currency rate is set
|
||||
'currency_id': cls.currency_data['currency'].id,
|
||||
'invoice_line_ids': [
|
||||
(0, None, {
|
||||
'product_id': cls.product_a.id,
|
||||
'quantity': 1,
|
||||
'price_unit': 1200,
|
||||
}),
|
||||
]
|
||||
},
|
||||
])
|
||||
|
||||
def assertInvoiceReportValues(self, expected_values_list):
|
||||
@@ -102,6 +115,7 @@ class TestAccountInvoiceReport(AccountTestInvoicingCommon):
|
||||
'price_average': vals[0],
|
||||
'price_subtotal': vals[1],
|
||||
'quantity': vals[2],
|
||||
'price_total': vals[3],
|
||||
} for vals in expected_values_list]
|
||||
|
||||
self.assertRecordValues(reports, expected_values_dict)
|
||||
@@ -109,11 +123,12 @@ class TestAccountInvoiceReport(AccountTestInvoicingCommon):
|
||||
def test_invoice_report_multiple_types(self):
|
||||
self.assertInvoiceReportValues([
|
||||
#price_average price_subtotal quantity
|
||||
[2000, 2000, 1],
|
||||
[1000, 1000, 1],
|
||||
[250, 750, 3],
|
||||
[6, 6, 1],
|
||||
[20, -20, -1],
|
||||
[20, -20, -1],
|
||||
[600, -600, -1],
|
||||
[2000, 2000, 1, 2300],
|
||||
[1200, 1200, 1, 1380],
|
||||
[1000, 1000, 1, 1150],
|
||||
[250, 750, 3, 862.5],
|
||||
[6, 6, 1, 6.9],
|
||||
[20, -20, -1, -23],
|
||||
[20, -20, -1, -23],
|
||||
[600, -600, -1, -690],
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user