[FIX] sale_stock: Don't compute UoM twice

The amount on generated COGS lines can have the UoM factor applied two times. For example, if we have a product costing 1$ per item and we sell it by pack of 12 ("Pack" UoM) we will have 1$*12*12=144$ instead of 1$*12=12$

0/ Go to a V14 runbot
1/ Create a company which use COGS (eg. Mexican accounting)
2/ Have a storable product priced at 1.5$ from a product category with Costing Method as "First In First Out (FIFO)" and Inventory Valuation as "Automated"
3/ Create a UoM 12 times bigger than the default Unit (eg. 12 Pack)
4/ Create a purchase order costing 50$ for 50 Unit of the product => cost of the product should be 1$
5/ Create a new purchase order for 50 Unit but costing 0$ (like if got free products from supplier)
6/ Create sale order for 50 units of the product, validate the transfert and create/confirm the invoice => computed COGS amount is 50$ on the invoice => ok
7/ Create a sale order using the "12 Pack" UoM, validate transfert and create/confirm invoice => computed COGS is 144$ => nok

opw-2503368

closes odoo/odoo#77007

X-original-commit: d0d9c0a8f8e35fbd9624d934a8ca6152677fbd3e
Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
Signed-off-by: Achraf <abz-odoo@users.noreply.github.com>
This commit is contained in:
Achraf (abz)
2021-09-23 11:25:57 +00:00
parent 516c414683
commit fff6ffc5a1
2 changed files with 123 additions and 2 deletions
+2 -2
View File
@@ -120,8 +120,8 @@ class AccountMoveLine(models.Model):
qty_to_invoice = self.product_uom_id._compute_quantity(self.quantity, self.product_id.uom_id)
qty_invoiced = sum([x.product_uom_id._compute_quantity(x.quantity, x.product_id.uom_id) for x in so_line.invoice_lines if x.move_id.state == 'posted'])
average_price_unit = self.product_id._compute_average_price(qty_invoiced, qty_to_invoice, so_line.move_ids)
if average_price_unit:
price_unit = self.product_id.uom_id._compute_price(average_price_unit, self.product_uom_id)
price_unit = average_price_unit or price_unit
price_unit = self.product_id.uom_id._compute_price(price_unit, self.product_uom_id)
return price_unit
@@ -1114,3 +1114,124 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
income_aml_2 = amls_2.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue'])
self.assertEqual(income_aml_2.debit, 0)
self.assertEqual(income_aml_2.credit, 12)
def test_fifo_uom_computation(self):
self.env.company.anglo_saxon_accounting = True
self.product.categ_id.property_cost_method = 'fifo'
self.product.categ_id.property_valuation = 'real_time'
quantity = 50.0
self.product.list_price = 1.5
self.product.standard_price = 2.0
unit_12 = self.env['uom.uom'].create({
'name': 'Pack of 12 units',
'category_id': self.product.uom_id.category_id.id,
'uom_type': 'bigger',
'factor_inv': 12,
'rounding': 1,
})
# Create, confirm and deliver a sale order for 12@1.5 without reception with std_price = 2.0 (SO1)
so_1 = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [
(0, 0, {
'name': self.product.name,
'product_id': self.product.id,
'product_uom_qty': 1,
'product_uom': unit_12.id,
'price_unit': 18,
'tax_id': False, # no love taxes amls
})],
})
so_1.action_confirm()
so_1.picking_ids.move_lines.quantity_done = 12
so_1.picking_ids.button_validate()
# Invoice the sale order.
invoice_1 = so_1._create_invoices()
invoice_1.action_post()
# Invoice 1
# Correct Journal Items
# Name Debit Credit
# Product Sales 0.00$ 18.00$
# Account Receivable 18.00$ 0.00$
# Default Account Stock Out 0.00$ 24.00$
# Expenses 24.00$ 0.00$
aml = invoice_1.line_ids
# Product Sales
self.assertEqual(aml[0].debit, 0.0)
self.assertEqual(aml[0].credit, 18.0)
# Account Receivable
self.assertEqual(aml[1].debit, 18.0)
self.assertEqual(aml[1].credit, 0.0)
# Default Account Stock Out
self.assertEqual(aml[2].debit, 0.0)
self.assertEqual(aml[2].credit, 24.0)
# Expenses
self.assertEqual(aml[3].debit, 24.0)
self.assertEqual(aml[3].credit, 0.0)
# Create stock move 1
in_move_1 = self.env['stock.move'].create({
'name': 'a',
'product_id': self.product.id,
'location_id': self.env.ref('stock.stock_location_suppliers').id,
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
'product_uom': self.product.uom_id.id,
'product_uom_qty': quantity,
'price_unit': 1.0,
})
in_move_1._action_confirm()
in_move_1.quantity_done = quantity
in_move_1._action_done()
# Create, confirm and deliver a sale order for 12@1.5 with reception (50 * 1.0, 50 * 0.0)(SO2)
so_2 = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [
(0, 0, {
'name': self.product.name,
'product_id': self.product.id,
'product_uom_qty': 1,
'product_uom': unit_12.id,
'price_unit': 18,
'tax_id': False, # no love taxes amls
})],
})
so_2.action_confirm()
so_2.picking_ids.move_lines.quantity_done = 12
so_2.picking_ids.button_validate()
# Invoice the sale order.
invoice_2 = so_2._create_invoices()
invoice_2.action_post()
# Invoice 2
# Correct Journal Items
# Name Debit Credit
# Product Sales 0.00$ 18.0$
# Account Receivable 18.00$ 0.0$
# Default Account Stock Out 0.00$ 12.0$
# Expenses 12.00$ 0.0$
aml = invoice_2.line_ids
# Product Sales
self.assertEqual(aml[0].debit, 0.0)
self.assertEqual(aml[0].credit, 18.0)
# Account Receivable
self.assertEqual(aml[1].debit, 18.0)
self.assertEqual(aml[1].credit, 0.0)
# Default Account Stock Out
self.assertEqual(aml[2].debit, 0.0)
self.assertEqual(aml[2].credit, 12.0)
# Expenses
self.assertEqual(aml[3].debit, 12.0)
self.assertEqual(aml[3].credit, 0.0)