[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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user