[FIX] purchase_stock: add POL to existing PO if draft

To reproduce the issue:
1. In Settings:
   - Days to Purchase: 10
2. Create two products P01, P02:
   - Storable
   - With the same seller
     - Delivery Lead Time: 1.0
3. On replenishment page, create a new line:
   - Product: P01
   - Min Qty: 1.0
4. Order once
   - a RfQ is created and the order deadline is in ten days
5. Time travel to the next day
6. Repeat steps 3-4 with P02

Error: a second RfQ has been generated. However, as the first one is
still in draft state, the POL of P02 should be added to that first RfQ

When processing the orderpoint of P02, at some point, we try to find
an existing RfQ with its order deadline equal to `today + 10 days`.
Since we are the next day (step 5), the order deadline of the first
RfQ is `today + 9 days`, so we don't find it.

OPW-3047931

closes odoo/odoo#113982

X-original-commit: 8bf7f9038915be520488b2602c20929ca115a033
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
Adrien Widart (awt)
2023-03-01 23:28:10 +01:00
parent 5d15afdb30
commit ff75b9d4f4
3 changed files with 95 additions and 9 deletions
+3 -2
View File
@@ -305,9 +305,10 @@ class StockRule(models.Model):
('company_id', '=', company_id.id),
('user_id', '=', False),
)
if values.get('orderpoint_id'):
delta_days = self.env['ir.config_parameter'].sudo().get_param('purchase_stock.delta_days_merge')
if values.get('orderpoint_id') and delta_days is not False:
procurement_date = fields.Date.to_date(values['date_planned']) - relativedelta(days=int(values['supplier'].delay))
delta_days = int(self.env['ir.config_parameter'].sudo().get_param('purchase_stock.delta_days_merge') or 0)
delta_days = int(delta_days)
domain += (
('date_order', '<=', datetime.combine(procurement_date + relativedelta(days=delta_days), datetime.max.time())),
('date_order', '>=', datetime.combine(procurement_date - relativedelta(days=delta_days), datetime.min.time()))
@@ -344,7 +344,8 @@ class TestPurchaseLeadTime(PurchaseTestCommon):
delivery_moves._action_confirm()
self.env['procurement.group'].run_scheduler()
po_line = self.env['purchase.order.line'].search([('product_id', '=', product.id)])
self.assertEqual(fields.Date.to_date(po_line.order_id.date_order), fields.Date.today() + timedelta(days=2))
expected_date_order = fields.Date.today() + timedelta(days=2)
self.assertEqual(fields.Date.to_date(po_line.order_id.date_order), expected_date_order)
self.assertEqual(len(po_line), 1)
self.assertEqual(po_line.product_uom_qty, 25.0)
self.assertEqual(len(po_line.order_id), 1)
@@ -354,12 +355,10 @@ class TestPurchaseLeadTime(PurchaseTestCommon):
self.mock_date.today.return_value = fields.Date.today() + timedelta(days=2)
orderpoint._compute_qty()
self.env['procurement.group'].run_scheduler()
po_line = self.env['purchase.order.line'].search([('product_id', '=', product.id)])
self.assertEqual(len(po_line), 2)
self.assertEqual(len(po_line.order_id), 2)
new_order = po_line.order_id.sorted('date_order')[-1]
self.assertEqual(fields.Date.to_date(new_order.date_order), fields.Date.today() + timedelta(days=2))
self.assertEqual(new_order.order_line.product_uom_qty, 5.0)
po_line02 = self.env['purchase.order.line'].search([('product_id', '=', product.id)])
self.assertEqual(po_line02, po_line, 'The orderpoint execution should not create a new POL')
self.assertEqual(fields.Date.to_date(po_line.order_id.date_order), expected_date_order, 'The Order Deadline should not change')
self.assertEqual(po_line.product_uom_qty, 30.0, 'The existing POL should be updated with the quantity of the last execution')
def test_supplier_lead_time(self):
""" Basic stock configuration and a supplier with a minimum qty and a lead time """
@@ -824,3 +824,89 @@ class TestReorderingRule(TransactionCase):
{'location_id': supplier_location_id, 'location_dest_id': input_location_id, 'product_qty': 1},
{'location_id': input_location_id, 'location_dest_id': stock_location_id, 'product_qty': 1},
])
def test_add_line_to_existing_draft_po(self):
"""
Days to purchase = 10
Two products P1, P2 from the same supplier
Several use cases, each time we run the RR one by one. Then, according
to the dates and the configuration, it should use the existing PO or not
"""
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
self.env.company.days_to_purchase = 10
expected_order_date = dt.combine(dt.today() + td(days=10), dt.min.time())
expected_delivery_date = expected_order_date + td(days=1.0)
# expected_delivery_date = expected_delivery_date.replace(hour=12, minute=0, second=0)
product_02 = self.env['product.product'].create({
'name': 'Super Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': self.partner.id})],
})
op_01, op_02 = self.env['stock.warehouse.orderpoint'].create([{
'warehouse_id': warehouse.id,
'location_id': warehouse.lot_stock_id.id,
'product_id': p.id,
'product_min_qty': 1,
'product_max_qty': 0,
} for p in [self.product_01, product_02]])
op_01.action_replenish()
po01 = self.env['purchase.order'].search([], order='id desc', limit=1)
self.assertEqual(po01.date_order, expected_order_date)
op_02.action_replenish()
self.assertEqual(po01.date_order, expected_order_date)
self.assertRecordValues(po01.order_line, [
{'product_id': self.product_01.id, 'date_planned': expected_delivery_date},
{'product_id': product_02.id, 'date_planned': expected_delivery_date},
])
# Reset and try another flow
po01.button_cancel()
op_01.action_replenish()
po02 = self.env['purchase.order'].search([], order='id desc', limit=1)
self.assertNotEqual(po02, po01)
with freeze_time(dt.today() + td(days=1)):
op_02.invalidate_recordset(fnames=['lead_days_date'])
op_02.action_replenish()
self.assertEqual(po02.date_order, expected_order_date)
self.assertRecordValues(po02.order_line, [
{'product_id': self.product_01.id, 'date_planned': expected_delivery_date},
{'product_id': product_02.id, 'date_planned': expected_delivery_date + td(days=1)},
])
# Restrict the merge with POs that have their order deadline in [today - 2 days, today + 2 days]
self.env['ir.config_parameter'].set_param('purchase_stock.delta_days_merge', '2')
# Reset and try with a second RR executed in the dates range (-> should still use the existing PO)
po02.button_cancel()
op_01.action_replenish()
po03 = self.env['purchase.order'].search([], order='id desc', limit=1)
self.assertNotEqual(po03, po02)
with freeze_time(dt.today() + td(days=2)):
op_02.invalidate_recordset(fnames=['lead_days_date'])
op_02.action_replenish()
self.assertEqual(po03.date_order, expected_order_date)
self.assertRecordValues(po03.order_line, [
{'product_id': self.product_01.id, 'date_planned': expected_delivery_date},
{'product_id': product_02.id, 'date_planned': expected_delivery_date + td(days=2)},
])
# Reset and try with a second RR executed after the dates range (-> should not use the existing PO)
po03.button_cancel()
op_01.action_replenish()
po04 = self.env['purchase.order'].search([], order='id desc', limit=1)
self.assertNotEqual(po04, po03)
with freeze_time(dt.today() + td(days=3)):
op_02.invalidate_recordset(fnames=['lead_days_date'])
op_02.action_replenish()
self.assertEqual(po04.order_line.product_id, self.product_01, 'There should be only a line for product 01')
po05 = self.env['purchase.order'].search([], order='id desc', limit=1)
self.assertNotEqual(po05, po04, 'A new PO should be generated')
self.assertEqual(po05.order_line.product_id, product_02)