[FIX] sale_stock,stock_account: ignore SVL without any qty
In some conditions, confirming an invoice will raise an Odoo Server
Error due to a division by zero.
To reproduce the issue:
(Need sale_management)
1. Create a product category PC:
- Costing Method: Average Cost (AVCO)
- Inventory Valuation: Automated
2. Create a product P:
- Product Type: Storable
- Category: PC
- Cost: 50
3. Create a sale order SO:
- 5 x P
4. Confirm and process the delivery D
5. Edit P:
- Cost: 40
6. Open D and create a return R:
- 1 x P without "Update quantities on SO/PO"
7. Process R
8. In Inventory, create a transfer T:
- Operation Type: Receipt
- 1 x P
9. Process T
10. Back to SO, create an invoice and post it
Error: an Odoo Server Error is raised: "ZeroDivisionError: float
division by zero"
When validating the transfer T, a stock valuation layer is created with
a zero quantity and is linked to the first SO.order_line.move_ids (i.e.,
the SM from Stock to Customer with 5 x P)
https://github.com/odoo/odoo/blob/dfab388fdf8c7597774ff1738a1b09295c12fb49/addons/stock_account/models/product.py#L439-L450
Then, when posting the invoice, this SVL is used to make a division:
https://github.com/odoo/odoo/blob/dfab388fdf8c7597774ff1738a1b09295c12fb49/addons/stock_account/models/product.py#L690-L691
In such cases, we should ignore this SVL and use the standard price:
https://github.com/odoo/odoo/blob/dfab388fdf8c7597774ff1738a1b09295c12fb49/addons/stock_account/models/product.py#L695-L699
OPW-2647921
OPW-2646419
closes odoo/odoo#77909
X-original-commit: d5eccda583e28a15e8a74a4e82f9e76ec32d0806
Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
This commit is contained in:
@@ -478,6 +478,67 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
|
||||
self.assertEqual(income_aml.debit, 0)
|
||||
self.assertEqual(income_aml.credit, 24)
|
||||
|
||||
def test_avco_ordered_return_and_receipt(self):
|
||||
""" Sell and deliver some products before the user encodes the products receipt """
|
||||
product = self.product
|
||||
product.invoice_policy = 'order'
|
||||
product.type = 'product'
|
||||
product.categ_id.property_cost_method = 'average'
|
||||
product.categ_id.property_valuation = 'real_time'
|
||||
product.list_price = 100
|
||||
product.standard_price = 50
|
||||
|
||||
so = self.env['sale.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'partner_invoice_id': self.partner_a.id,
|
||||
'partner_shipping_id': self.partner_a.id,
|
||||
'order_line': [(0, 0, {
|
||||
'name': product.name,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': 5.0,
|
||||
'product_uom': product.uom_id.id,
|
||||
'price_unit': product.list_price})],
|
||||
})
|
||||
so.action_confirm()
|
||||
|
||||
pick = so.picking_ids
|
||||
pick.move_lines.write({'quantity_done': 5})
|
||||
pick.button_validate()
|
||||
|
||||
product.standard_price = 40
|
||||
|
||||
stock_return_picking_form = Form(self.env['stock.return.picking']
|
||||
.with_context(active_ids=pick.ids, active_id=pick.sorted().ids[0], active_model='stock.picking'))
|
||||
return_wiz = stock_return_picking_form.save()
|
||||
return_wiz.product_return_moves.quantity = 1
|
||||
return_wiz.product_return_moves.to_refund = False
|
||||
res = return_wiz.create_returns()
|
||||
|
||||
return_pick = self.env['stock.picking'].browse(res['res_id'])
|
||||
return_pick.move_lines.write({'quantity_done': 1})
|
||||
return_pick.button_validate()
|
||||
|
||||
picking = self.env['stock.picking'].create({
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||||
'picking_type_id': self.company_data['default_warehouse'].in_type_id.id,
|
||||
})
|
||||
# We don't set the price_unit so that the `standard_price` will be used (see _get_price_unit()):
|
||||
self.env['stock.move'].create({
|
||||
'name': 'test_immediate_validate_1',
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
|
||||
'picking_id': picking.id,
|
||||
'product_id': product.id,
|
||||
'product_uom': product.uom_id.id,
|
||||
'quantity_done': 1,
|
||||
})
|
||||
picking.button_validate()
|
||||
|
||||
invoice = so._create_invoices()
|
||||
invoice.action_post()
|
||||
self.assertEqual(invoice.state, 'posted')
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# AVCO Delivered
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -673,6 +673,8 @@ class ProductProduct(models.Model):
|
||||
qty_to_take_on_candidates = qty_to_invoice
|
||||
tmp_value = 0 # to accumulate the value taken on the candidates
|
||||
for candidate in candidates:
|
||||
if not candidate.quantity:
|
||||
continue
|
||||
candidate_quantity = abs(candidate.quantity)
|
||||
if candidate.stock_move_id.id in returned_quantities:
|
||||
candidate_quantity -= returned_quantities[candidate.stock_move_id.id]
|
||||
|
||||
Reference in New Issue
Block a user