[FIX] stock_account: invalid layer on bill due to rounding

What are the steps to reproduce your issue?
Price Decimal Precision set at 5, currency at 2. Anglosaxon enabled.
Have a product with a unit / purchase cost of 3.30125
Create Purchase for 1500.00 and receipt in.
This creates an SVL and AML for 4951.88
Create Bill with unit cost of 3.30125 and total amount of 4951.88
Confirm Invoice

What is the current behavior that you observe?
The layer unit price value is calculated unrounded (line 304, purchase_stock/models/account_move_line.py) - so 4951.88 / 1500 = 3.30125333333.
This is then subtracted from the Invoice unit price leaving a difference of 0.00000333333
Despite the price_unit difference being less than the precision this then multiplies out to be 0.005 that gets rounded to 0.01.
A new SVL layer is created, and a new move valued at 0.01 and remaining value on existing correct layer is reduced by 1c.
When anglosaxon attempts to reconcile the various moves, it is unable to because there is now a 1c difference in the sum of the relevant AML's.

What would be your expected behavior in this case?
Not to create useless SVL that is wrong
Reconcile anglosaxon records normally.

It happens due to the comparaison between the layer price unit that
is not rounded and the price unit of the account.move.line that is
rounded to the decimal accuracy. We want to keep the most accurate
value so we modify _get_gross_price unit to recompute the unit price
this way we ignore the rounding

close #140410

closes odoo/odoo#143768

X-original-commit: 73aaaa9550b15225e25fcd750ebfd5fb58436064
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
This commit is contained in:
Arnold Moyaux
2023-11-29 17:47:02 +00:00
parent d910f6704e
commit 360b3aae3a
2 changed files with 72 additions and 2 deletions
@@ -764,6 +764,76 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
self.assertAlmostEqual(picking_aml.amount_currency, -20, msg="credit value for stock should be equal to the standard price of the product.")
self.assertAlmostEqual(diff_aml.amount_currency, -80, msg="credit value for price difference")
def test_valuation_rounding(self):
company = self.env.user.company_id
company.anglo_saxon_accounting = True
company.currency_id = self.usd_currency
self.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
self.product1.product_tmpl_id.categ_id.property_valuation = 'real_time'
self.env.ref('product.decimal_price').digits = 5
po = self.env['purchase.order'].create({
'partner_id': self.partner_id.id,
'order_line': [
(0, 0, {
'name': self.product1.name,
'product_id': self.product1.id,
'product_qty': 1500.0,
'product_uom': self.product1.uom_po_id.id,
'price_unit': 3.30125,
}),
],
})
po.button_confirm()
# Receive the goods
receipt = po.picking_ids
receipt.button_validate()
self._bill(po)
layers = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)])
self.assertEqual(len(layers), 1)
self.assertEqual(layers.quantity, 1500)
self.assertEqual(layers.value, 4951.88)
def test_valuation_rounding_price_diff(self):
company = self.env.user.company_id
company.anglo_saxon_accounting = True
company.currency_id = self.usd_currency
self.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
self.product1.product_tmpl_id.categ_id.property_valuation = 'real_time'
self.env.ref('product.decimal_price').digits = 5
po = self.env['purchase.order'].create({
'partner_id': self.partner_id.id,
'order_line': [
(0, 0, {
'name': self.product1.name,
'product_id': self.product1.id,
'product_qty': 1500.0,
'product_uom': self.product1.uom_po_id.id,
'price_unit': 3.30125,
}),
],
})
po.button_confirm()
# Receive the goods
receipt = po.picking_ids
receipt.button_validate()
self._bill(po, price=3.31125)
layers = self.env['stock.valuation.layer'].search([('product_id', '=', self.product1.id)])
self.assertEqual(len(layers), 2)
self.assertEqual(layers[0].quantity, 1500)
self.assertEqual(layers[1].quantity, 0)
self.assertEqual(layers[0].value, 4951.88)
self.assertEqual(round(layers[1].value, company.currency_id.decimal_places), 15)
def test_valuation_multicurrency_with_tax(self):
""" Check that the amount_currency does not include the tax.
"""
+2 -2
View File
@@ -258,8 +258,8 @@ class AccountMoveLine(models.Model):
return self.product_id.type == 'product' and self.product_id.valuation == 'real_time'
def _get_gross_unit_price(self):
price_unit = -self.price_unit if self.move_id.move_type == 'in_refund' else self.price_unit
price_unit = price_unit * (1 - (self.discount or 0.0) / 100.0)
price_unit = self.price_subtotal / self.quantity
price_unit = -price_unit if self.move_id.move_type == 'in_refund' else price_unit
if not self.tax_ids:
return price_unit
prec = 1e+6