diff --git a/addons/sale_stock/tests/test_anglo_saxon_valuation.py b/addons/sale_stock/tests/test_anglo_saxon_valuation.py index 1cff7c4e7bb..ba0c78ca14e 100644 --- a/addons/sale_stock/tests/test_anglo_saxon_valuation.py +++ b/addons/sale_stock/tests/test_anglo_saxon_valuation.py @@ -855,3 +855,79 @@ class TestAngloSaxonValuation(SavepointCase): self.assertEqual(income_aml.debit, 0) self.assertEqual(income_aml.credit, 24) + def test_fifo_delivered_invoice_post_delivery_2(self): + """Receive at 8 then at 10. Sale order 10@12 and deliver without receiving the 2 missing. + receive 2@12. Invoice.""" + self.product.categ_id.property_cost_method = 'fifo' + self.product.invoice_policy = 'delivery' + self.product.standard_price = 10 + + 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.stock_location.id, + 'product_uom': self.product.uom_id.id, + 'product_uom_qty': 8, + 'price_unit': 10, + }) + in_move_1._action_confirm() + in_move_1.quantity_done = 8 + in_move_1._action_done() + + # Create and confirm a sale order for 2@12 + sale_order = self.env['sale.order'].create({ + 'partner_id': self.customer.id, + 'order_line': [ + (0, 0, { + 'name': self.product.name, + 'product_id': self.product.id, + 'product_uom_qty': 10.0, + 'product_uom': self.product.uom_id.id, + 'price_unit': 12, + 'tax_id': False, # no love taxes amls + })], + }) + sale_order.action_confirm() + + # Deliver 10 + sale_order.picking_ids.move_lines.quantity_done = 10 + sale_order.picking_ids.button_validate() + + # Make the second receipt + in_move_2 = 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.stock_location.id, + 'product_uom': self.product.uom_id.id, + 'product_uom_qty': 2, + 'price_unit': 12, + }) + in_move_2._action_confirm() + in_move_2.quantity_done = 2 + in_move_2._action_done() + self.assertEqual(self.product.stock_valuation_layer_ids[-1].value, -4) # we sent two at 10 but they should have been sent at 12 + self.assertEqual(self.product.stock_valuation_layer_ids[-1].quantity, 0) + self.assertEqual(sale_order.order_line.move_ids.stock_valuation_layer_ids[-1].quantity, 0) + + # Invoice the sale order. + invoice = sale_order._create_invoices() + invoice.post() + + # Check the resulting accounting entries + amls = invoice.line_ids + self.assertEqual(len(amls), 4) + stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.stock_output_account) + self.assertEqual(stock_out_aml.debit, 0) + self.assertEqual(stock_out_aml.credit, 104) + cogs_aml = amls.filtered(lambda aml: aml.account_id == self.expense_account) + self.assertEqual(cogs_aml.debit, 104) + self.assertEqual(cogs_aml.credit, 0) + receivable_aml = amls.filtered(lambda aml: aml.account_id == self.recv_account) + self.assertEqual(receivable_aml.debit, 120) + self.assertEqual(receivable_aml.credit, 0) + income_aml = amls.filtered(lambda aml: aml.account_id == self.income_account) + self.assertEqual(income_aml.debit, 0) + self.assertEqual(income_aml.credit, 120) + diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index b6ce7a66615..3d72df9e207 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -395,6 +395,7 @@ class ProductProduct(models.Model): 'stock_move_id': move.id, 'company_id': move.company_id.id, 'description': 'Revaluation of %s (negative inventory)' % move.picking_id.name or move.name, + 'stock_valuation_layer_id': svl_to_vacuum.id, } vacuum_svl = self.env['stock.valuation.layer'].sudo().create(vals) @@ -616,6 +617,8 @@ class ProductProduct(models.Model): tmp_value = 0 # to accumulate the value taken on the candidates for candidate in candidates: candidate_quantity = abs(candidate.quantity) + if float_is_zero(candidate_quantity, precision_rounding=candidate.uom_id.rounding): + continue # correction entries if not float_is_zero(qty_invoiced, precision_rounding=candidate.uom_id.rounding): qty_ignored = min(qty_invoiced, candidate_quantity) qty_invoiced -= qty_ignored @@ -625,7 +628,8 @@ class ProductProduct(models.Model): qty_taken_on_candidate = min(qty_to_take_on_candidates, candidate_quantity) qty_to_take_on_candidates -= qty_taken_on_candidate - tmp_value += qty_taken_on_candidate * (candidate.value / candidate.quantity) + tmp_value += qty_taken_on_candidate * \ + ((candidate.value + sum(candidate.stock_valuation_layer_ids.mapped('value'))) / candidate.quantity) if float_is_zero(qty_to_take_on_candidates, precision_rounding=candidate.uom_id.rounding): break