[FIX] purchase, sale_stock: propagation of UOM

Before this commit, the uom defined on the sale order line and on the
purchase order line was propagated to the moves. This is not the
behaviour of v8-10 and may confuse the stock operator (one time he's
working in dozen, another time in units for example). It also leads to
rounding issues if the configuration is not adapted correctly (product's
uom is in units, sale order line is in dozen, the rouding precision on
the uom is left to their defaults, now if a dozen is partially available
there's a good chance converting back and forth form dozen to units will
break).

An ir.config_parameter was added to restore the behaviour before this
commit.
This commit is contained in:
Simon Lejeune
2018-02-19 14:29:09 +01:00
parent d28f8f704f
commit 97ed2e730e
5 changed files with 129 additions and 6 deletions
+9 -2
View File
@@ -702,7 +702,7 @@ class PurchaseOrderLine(models.Model):
qty = 0.0
price_unit = self._get_stock_move_price_unit()
for move in self.move_ids.filtered(lambda x: x.state != 'cancel' and not x.location_dest_id.usage == "supplier"):
qty += move.product_qty
qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
template = {
'name': self.name or '',
'product_id': self.product_id.id,
@@ -726,7 +726,14 @@ class PurchaseOrderLine(models.Model):
}
diff_quantity = self.product_qty - qty
if float_compare(diff_quantity, 0.0, precision_rounding=self.product_uom.rounding) > 0:
template['product_uom_qty'] = diff_quantity
quant_uom = self.product_id.uom_id
get_param = self.env['ir.config_parameter'].sudo().get_param
if self.product_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
product_qty = self.product_uom._compute_quantity(diff_quantity, quant_uom, rounding_method='HALF-UP')
template['product_uom'] = quant_uom.id
template['product_uom_qty'] = product_qty
else:
template['product_uom_qty'] = diff_quantity
res.append(template)
return res
@@ -174,3 +174,53 @@ class TestCreatePicking(common.TestProductCommon):
customer_move._action_assign()
self.assertEqual(customer_move.state, 'assigned', 'Reservation should work with the new quantity provided by the PO.')
def test_03_uom(self):
""" Buy a dozen of products stocked in units. Check that the quantities on the purchase order
lines as well as the received quantities are handled in dozen while the moves themselves
are handled in units. Edit the ordered quantities, check that the quantites are correctly
updated on the moves. Edit the ir.config_parameter to propagate the uom of the purchase order
lines to the moves and edit a last time the ordered quantities. Receive, check the quantities.
"""
uom_unit = self.env.ref('product.product_uom_unit')
uom_dozen = self.env.ref('product.product_uom_dozen')
self.assertEqual(self.product_id_1.uom_po_id.id, uom_unit.id)
# buy a dozen
po = self.env['purchase.order'].create(self.po_vals)
po.order_line.product_qty = 1
po.order_line.product_uom = uom_dozen.id
po.button_confirm()
# the move should be 12 units
# note: move.product_qty = computed field, always in the uom of the quant
# move.product_uom_qty = stored field representing the initial demand in move.product_uom
move1 = po.picking_ids.move_lines[0]
self.assertEqual(move1.product_uom_qty, 12)
self.assertEqual(move1.product_uom.id, uom_unit.id)
self.assertEqual(move1.product_qty, 12)
# edit the so line, sell 2 dozen, the move should now be 24 units
po.order_line.product_qty = 2
self.assertEqual(move1.product_uom_qty, 24)
self.assertEqual(move1.product_uom.id, uom_unit.id)
self.assertEqual(move1.product_qty, 24)
# force the propagation of the uom, sell 3 dozen
self.env['ir.config_parameter'].sudo().set_param('stock.propagate_uom', '1')
po.order_line.product_qty = 3
move2 = po.picking_ids.move_lines.filtered(lambda m: m.product_uom.id == uom_dozen.id)
self.assertEqual(move2.product_uom_qty, 1)
self.assertEqual(move2.product_uom.id, uom_dozen.id)
self.assertEqual(move2.product_qty, 12)
# deliver everything
move1.quantity_done = 24
move2.quantity_done = 1
po.picking_ids.button_validate()
# check the delivered quantity
self.assertEqual(po.order_line.qty_received, 3.0)
+2 -2
View File
@@ -167,8 +167,8 @@ class TestSaleMrpFlow(common.TransactionCase):
mnf_product_a = self.env['mrp.production'].search([('product_id', '=', product_a.id)])
self.assertTrue(mnf_product_a, 'Manufacturing order not created.')
self.assertEqual(mnf_product_a.product_qty, 10, 'Wrong product quantity in manufacturing order.')
self.assertEqual(mnf_product_a.product_uom_id.id, self.uom_dozen.id, 'Wrong unit of measure in manufacturing order.')
self.assertEqual(mnf_product_a.product_qty, 120, 'Wrong product quantity in manufacturing order.')
self.assertEqual(mnf_product_a.product_uom_id.id, self.uom_unit.id, 'Wrong unit of measure in manufacturing order.')
self.assertEqual(mnf_product_a.state, 'confirmed', 'Manufacturing order should be confirmed.')
# ------------------------------------------------------------------------------------------
+10 -2
View File
@@ -235,7 +235,7 @@ class SaleOrderLine(models.Model):
continue
qty = 0.0
for move in line.move_ids.filtered(lambda r: r.state != 'cancel'):
qty += move.product_qty
qty += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
continue
@@ -260,8 +260,16 @@ class SaleOrderLine(models.Model):
values = line._prepare_procurement_values(group_id=group_id)
product_qty = line.product_uom_qty - qty
procurement_uom = line.product_uom
quant_uom = line.product_id.uom_id
get_param = self.env['ir.config_parameter'].sudo().get_param
if procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
product_qty = line.product_uom._compute_quantity(product_qty, quant_uom, rounding_method='HALF-UP')
procurement_uom = quant_uom
try:
self.env['procurement.group'].run(line.product_id, product_qty, line.product_uom, line.order_id.partner_shipping_id.property_stock_customer, line.name, line.order_id.name, values)
self.env['procurement.group'].run(line.product_id, product_qty, procurement_uom, line.order_id.partner_shipping_id.property_stock_customer, line.name, line.order_id.name, values)
except UserError as error:
errors.append(error.name)
if errors:
@@ -303,3 +303,61 @@ class TestSaleStock(TestSale):
self.assertEqual(len(so1.picking_ids), 2)
picking2 = so1.picking_ids.filtered(lambda p: p.state != 'cancel')
self.assertEqual(picking2.partner_id.id, partner2)
def test_06_uom(self):
""" Sell a dozen of products stocked in units. Check that the quantities on the sale order
lines as well as the delivered quantities are handled in dozen while the moves themselves
are handled in units. Edit the ordered quantities, check that the quantites are correctly
updated on the moves. Edit the ir.config_parameter to propagate the uom of the sale order
lines to the moves and edit a last time the ordered quantities. Deliver, check the
quantities.
"""
uom_unit = self.env.ref('product.product_uom_unit')
uom_dozen = self.env.ref('product.product_uom_dozen')
item1 = self.products['prod_order']
self.assertEqual(item1.uom_id.id, uom_unit.id)
# sell a dozen
so1 = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [(0, 0, {
'name': item1.name,
'product_id': item1.id,
'product_uom_qty': 1,
'product_uom': uom_dozen.id,
'price_unit': item1.list_price,
})],
})
so1.action_confirm()
# the move should be 12 units
# note: move.product_qty = computed field, always in the uom of the quant
# move.product_uom_qty = stored field representing the initial demand in move.product_uom
move1 = so1.picking_ids.move_lines[0]
self.assertEqual(move1.product_uom_qty, 12)
self.assertEqual(move1.product_uom.id, uom_unit.id)
self.assertEqual(move1.product_qty, 12)
# edit the so line, sell 2 dozen, the move should now be 24 units
so1.order_line.product_uom_qty = 2
self.assertEqual(move1.product_uom_qty, 24)
self.assertEqual(move1.product_uom.id, uom_unit.id)
self.assertEqual(move1.product_qty, 24)
# force the propagation of the uom, sell 3 dozen
self.env['ir.config_parameter'].sudo().set_param('stock.propagate_uom', '1')
so1.order_line.product_uom_qty = 3
move2 = so1.picking_ids.move_lines.filtered(lambda m: m.product_uom.id == uom_dozen.id)
self.assertEqual(move2.product_uom_qty, 1)
self.assertEqual(move2.product_uom.id, uom_dozen.id)
self.assertEqual(move2.product_qty, 12)
# deliver everything
move1.quantity_done = 24
move2.quantity_done = 1
so1.picking_ids.button_validate()
# check the delivered quantity
self.assertEqual(so1.order_line.qty_delivered, 3.0)