summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorSimon Lejeune <sle@openerp.com>2020-01-17 16:00:51 +0000
committerSimon Lejeune <sle@openerp.com>2020-01-28 14:51:36 +0000
commit9d98c43581e2579f43b35541b43264866dede5a5 (patch)
tree7f3ba58302c61624cf503d12c4cd399b4d8c3092 /addons
parentaebce884d4531d5671030a45376cae48ce022bab (diff)
[REF] purchase_stock: push the extra quantity
With a product configured as buy on order and a warehouse configured as receipt in two steps, if the user increases the quantity on a po line generated by a sale order before confirming it, the system will send all the quantity to input but only the ordered quantity to customer. The issue is that the "extra quantity" will stay in input. We fix this issue by creating a new move with the extra quantity to the input location so that push rules will send the extra quantity to stock while the ordered quantity will be sent to the customer. There was also an issue when incrementing the quantity on the po line after confirmation if the po line was the result of a reordering rule: only a move from supplier to the location of the reordering rule was created. This commit also introduces a change of semantic: `created_purchase_line_id` is cleared after confirming the RFQ. This allows to merge more in `_merge_moves`. task-1981355 closes odoo/odoo#43545 Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/purchase_mrp/models/mrp_production.py7
-rw-r--r--addons/purchase_stock/models/purchase.py44
-rw-r--r--addons/purchase_stock/models/stock.py2
-rw-r--r--addons/purchase_stock/tests/test_reordering_rule.py38
-rw-r--r--addons/sale_purchase_stock/models/purchase_order.py4
-rw-r--r--addons/sale_purchase_stock/models/sale_order.py4
-rw-r--r--addons/stock/models/stock_rule.py3
7 files changed, 80 insertions, 22 deletions
diff --git a/addons/purchase_mrp/models/mrp_production.py b/addons/purchase_mrp/models/mrp_production.py
index 87cc432dacc..3f365b0cc54 100644
--- a/addons/purchase_mrp/models/mrp_production.py
+++ b/addons/purchase_mrp/models/mrp_production.py
@@ -12,14 +12,15 @@ class MrpProduction(models.Model):
compute='_compute_purchase_order_count',
groups='purchase.group_purchase_user')
- @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id')
+ @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', 'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id')
def _compute_purchase_order_count(self):
for production in self:
- production.purchase_order_count = len(production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id)
+ production.purchase_order_count = len(production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
+ production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
def action_view_purchase_orders(self):
self.ensure_one()
- purchase_order_ids = self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id.ids
+ purchase_order_ids = (self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | self.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
action = {
'res_model': 'purchase.order',
'type': 'ir.actions.act_window',
diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py
index 173a8352fe0..17e1f63d455 100644
--- a/addons/purchase_stock/models/purchase.py
+++ b/addons/purchase_stock/models/purchase.py
@@ -347,11 +347,39 @@ class PurchaseOrderLine(models.Model):
res = []
if self.product_id.type not in ['product', 'consu']:
return res
+
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_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
- template = {
+
+ move_dests = self.move_dest_ids
+ if not move_dests:
+ move_dests = self.move_ids.move_dest_ids
+
+ if not move_dests:
+ qty_to_attach = 0
+ qty_to_push = self.product_qty - qty
+ else:
+ move_dests_initial_demand = self.product_id.uom_id._compute_quantity(
+ sum(move_dests.filtered(lambda m: m.state != 'cancel' and not m.location_dest_id.usage == 'supplier').mapped('product_qty')),
+ self.product_uom, rounding_method='HALF-UP')
+ qty_to_attach = move_dests_initial_demand - qty
+ qty_to_push = self.product_qty - move_dests_initial_demand
+
+ if float_compare(qty_to_attach, 0.0, precision_rounding=self.product_uom.rounding) > 0:
+ product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(qty_to_attach, self.product_id.uom_id)
+ res.append(self._prepare_stock_move_vals(picking, price_unit, product_uom_qty, product_uom))
+ if float_compare(qty_to_push, 0.0, precision_rounding=self.product_uom.rounding) > 0:
+ product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(qty_to_push, self.product_id.uom_id)
+ extra_move_vals = self._prepare_stock_move_vals(picking, price_unit, product_uom_qty, product_uom)
+ extra_move_vals['move_dest_ids'] = False # don't attach
+ res.append(extra_move_vals)
+ return res
+
+ def _prepare_stock_move_vals(self, picking, price_unit, product_uom_qty, product_uom):
+ self.ensure_one()
+ return {
# truncate to 2000 to avoid triggering index limit error
# TODO: remove index in master?
'name': (self.name or '')[:2000],
@@ -360,7 +388,7 @@ class PurchaseOrderLine(models.Model):
'date': self.order_id.date_order,
'date_expected': self.date_planned,
'location_id': self.order_id.partner_id.property_stock_supplier.id,
- 'location_dest_id': (self.orderpoint_id and not self.move_dest_ids) and self.orderpoint_id.location_id.id or self.order_id._get_destination_location(),
+ 'location_dest_id': (self.orderpoint_id and not (self.move_ids | self.move_dest_ids)) and self.orderpoint_id.location_id.id or self.order_id._get_destination_location(),
'picking_id': picking.id,
'partner_id': self.order_id.dest_address_id.id,
'move_dest_ids': [(4, x) for x in self.move_dest_ids.ids],
@@ -378,16 +406,9 @@ class PurchaseOrderLine(models.Model):
'delay_alert': self.delay_alert,
'route_ids': self.order_id.picking_type_id.warehouse_id and [(6, 0, [x.id for x in self.order_id.picking_type_id.warehouse_id.route_ids])] or [],
'warehouse_id': self.order_id.picking_type_id.warehouse_id.id,
+ 'product_uom_qty': product_uom_qty,
+ 'product_uom': product_uom.id,
}
- diff_quantity = self.product_qty - qty
- if float_compare(diff_quantity, 0.0, precision_rounding=self.product_uom.rounding) > 0:
- po_line_uom = self.product_uom
- quant_uom = self.product_id.uom_id
- product_uom_qty, product_uom = po_line_uom._adjust_uom_quantities(diff_quantity, quant_uom)
- template['product_uom_qty'] = product_uom_qty
- template['product_uom'] = product_uom.id
- res.append(template)
- return res
@api.model
def _prepare_purchase_order_line_from_procurement(self, product_id, product_qty, product_uom, company_id, values, po):
@@ -406,6 +427,7 @@ class PurchaseOrderLine(models.Model):
for line in self.filtered(lambda l: not l.display_type):
for val in line._prepare_stock_moves(picking):
values.append(val)
+ line.move_dest_ids.created_purchase_line_id = False
moves = self.env['stock.move'].create(values)
for move in moves:
diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py
index d1e2d946469..20681254bd2 100644
--- a/addons/purchase_stock/models/stock.py
+++ b/addons/purchase_stock/models/stock.py
@@ -93,6 +93,8 @@ class StockMove(models.Model):
def _get_upstream_documents_and_responsibles(self, visited):
if self.created_purchase_line_id and self.created_purchase_line_id.state not in ('done', 'cancel'):
return [(self.created_purchase_line_id.order_id, self.created_purchase_line_id.order_id.user_id, visited)]
+ elif self.purchase_line_id and self.purchase_line_id.state not in ('done', 'cancel'):
+ return[(self.purchase_line_id.order_id, self.purchase_line_id.order_id.user_id, visited)]
else:
return super(StockMove, self)._get_upstream_documents_and_responsibles(visited)
diff --git a/addons/purchase_stock/tests/test_reordering_rule.py b/addons/purchase_stock/tests/test_reordering_rule.py
index eee698d0b66..d5ceda8fe47 100644
--- a/addons/purchase_stock/tests/test_reordering_rule.py
+++ b/addons/purchase_stock/tests/test_reordering_rule.py
@@ -27,6 +27,9 @@ class TestReorderingRule(SavepointCase):
- Receive products in 2 steps
- The product has a reordering rule
- On the po generated, the source document should be the name of the reordering rule
+ - Increase the quantity on the RFQ, the extra quantity should follow the push rules
+ - Increase the quantity on the PO, the extra quantity should follow the push rules
+ - There should be one move supplier -> input and two moves input -> stock
"""
warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1)
warehouse_1.write({'reception_steps': 'two_steps'})
@@ -61,12 +64,33 @@ class TestReorderingRule(SavepointCase):
# On the po generated, the source document should be the name of the reordering rule
self.assertEqual(order_point.name, purchase_order.origin, 'Source document on purchase order should be the name of the reordering rule.')
+ self.assertEqual(purchase_order.order_line.product_qty, 10)
+
+ # Increase the quantity on the RFQ before confirming it
+ purchase_order.order_line.product_qty = 12
+ purchase_order.button_confirm()
+
+ self.assertEqual(purchase_order.picking_ids.move_lines.filtered(lambda m: m.product_id == self.product_01).product_qty, 12)
+ next_picking = purchase_order.picking_ids.move_lines.move_dest_ids.picking_id
+ self.assertEqual(len(next_picking), 2)
+ self.assertEqual(next_picking[0].move_lines.filtered(lambda m: m.product_id == self.product_01).product_qty, 10)
+ self.assertEqual(next_picking[1].move_lines.filtered(lambda m: m.product_id == self.product_01).product_qty, 2)
+
+ # Increase the quantity on the PO
+ purchase_order.order_line.product_qty = 15
+ self.assertEqual(purchase_order.picking_ids.move_lines.product_qty, 15)
+ self.assertEqual(next_picking[0].move_lines.filtered(lambda m: m.product_id == self.product_01).product_qty, 10)
+ self.assertEqual(next_picking[1].move_lines.filtered(lambda m: m.product_id == self.product_01).product_qty, 5)
def test_reordering_rule_2(self):
"""
- Receive products in 1 steps
- The product has two reordering rules, each one applying in a sublocation
- Processing the purchase order should fulfill the two sublocations
+ - Increase the quantity on the RFQ for one of the POL, the extra quantity will go to
+ the original subloc since we don't know where to push it (no move dest)
+ - Increase the quantity on the PO, the extra quantity should follow the push rules and
+ thus go to stock
"""
warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1)
subloc_1 = self.env['stock.location'].create({'name': 'subloc_1', 'location_id': warehouse_1.lot_stock_id.id})
@@ -115,9 +139,17 @@ class TestReorderingRule(SavepointCase):
self.assertTrue(purchase_order, 'No purchase order created.')
self.assertEqual(len(purchase_order.order_line), 2, 'Not enough purchase order lines created.')
+ # increment the qty of the first po line
+ purchase_order.order_line.filtered(lambda pol: pol.orderpoint_id == order_point_1).product_qty = 15
purchase_order.button_confirm()
- res = purchase_order.picking_ids.button_validate()
- Form(self.env[res['res_model']].with_context(res['context'])).save().process()
- self.assertEqual(self.product_01.with_context(location=subloc_1.id).virtual_available, 0)
+ self.assertEqual(self.product_01.with_context(location=subloc_1.id).virtual_available, 5)
+ self.assertEqual(self.product_01.with_context(location=subloc_2.id).virtual_available, 0)
+
+ # increment the qty of the second po line
+ purchase_order.order_line.filtered(lambda pol: pol.orderpoint_id == order_point_2).with_context(debug=True).product_qty = 15
+ self.assertEqual(self.product_01.with_context(location=subloc_1.id).virtual_available, 5)
self.assertEqual(self.product_01.with_context(location=subloc_2.id).virtual_available, 0)
+ self.assertEqual(self.product_01.with_context(location=warehouse_1.lot_stock_id.id).virtual_available, 10) # 5 on the main loc, 5 on subloc_1
+ self.assertEqual(purchase_order.picking_ids.move_lines[-1].product_qty, 5)
+ self.assertEqual(purchase_order.picking_ids.move_lines[-1].location_dest_id, warehouse_1.lot_stock_id)
diff --git a/addons/sale_purchase_stock/models/purchase_order.py b/addons/sale_purchase_stock/models/purchase_order.py
index 59738a97d85..09ec51a7716 100644
--- a/addons/sale_purchase_stock/models/purchase_order.py
+++ b/addons/sale_purchase_stock/models/purchase_order.py
@@ -7,9 +7,9 @@ from odoo import api, models
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
- @api.depends('order_line.move_dest_ids.group_id.sale_id')
+ @api.depends('order_line.move_dest_ids.group_id.sale_id', 'order_line.move_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_count(self):
super(PurchaseOrder, self)._compute_sale_order_count()
def _get_sale_orders(self):
- return super(PurchaseOrder, self)._get_sale_orders() | self.order_line.move_dest_ids.group_id.sale_id
+ return super(PurchaseOrder, self)._get_sale_orders() | self.order_line.move_dest_ids.group_id.sale_id | self.order_line.move_ids.move_dest_ids.group_id.sale_id
diff --git a/addons/sale_purchase_stock/models/sale_order.py b/addons/sale_purchase_stock/models/sale_order.py
index 0a8522e9892..6f74ac76d28 100644
--- a/addons/sale_purchase_stock/models/sale_order.py
+++ b/addons/sale_purchase_stock/models/sale_order.py
@@ -7,9 +7,9 @@ from odoo import api, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
- @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id')
+ @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', 'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id')
def _compute_purchase_order_count(self):
super(SaleOrder, self)._compute_purchase_order_count()
def _get_purchase_orders(self):
- return super(SaleOrder, self)._get_purchase_orders() | self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id
+ return super(SaleOrder, self)._get_purchase_orders() | self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | self.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id
diff --git a/addons/stock/models/stock_rule.py b/addons/stock/models/stock_rule.py
index e3d7f62ea87..b608c230e45 100644
--- a/addons/stock/models/stock_rule.py
+++ b/addons/stock/models/stock_rule.py
@@ -207,7 +207,8 @@ class StockRule(models.Model):
'picking_type_id': self.picking_type_id.id,
'propagate_cancel': self.propagate_cancel,
'warehouse_id': self.warehouse_id.id,
- 'delay_alert': self.delay_alert
+ 'delay_alert': self.delay_alert,
+ 'procure_method': 'make_to_order',
}
return new_move_vals