[FIX] purchase_stock: generate vendor delay report

The vendor delay report only works with basic use cases. Here are
the issues:
- For each SM, we sum up the done qty of all SMLs without
  considering the UoMs
- When grouping several lines (for instance, by partner and product),
  we sum up the quantities of each line but their UoM can be different
- When grouping several lines, we sum the `qty_total` which are the
  quantity of the related POL. This is an issue: suppose a POL with
  a quantity Q and suppose there are two SM (backorder), if we group
  by product, we will have a total quantity of 2Q, which is
  incorrect (and so will be the delivery rate)

To reproduce the issues: See the tests added by this commit.
Note: the last test (`test_vendor_delay_report_without_backorder`)
was not failing before this commit. It is added to simply improve
  the tests

OPW-3065065

closes odoo/odoo#109471

X-original-commit: c2e351534b9627b6844effba9943b09d4b0b93f3
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-01-10 11:23:22 +01:00
parent 0758297a65
commit d4f82ff1fa
2 changed files with 167 additions and 5 deletions
@@ -29,14 +29,12 @@ SELECT m.id AS id,
m.product_id AS product_id,
Min(pc.id) AS category_id,
Min(po.partner_id) AS partner_id,
Sum(pol.product_uom_qty) AS qty_total,
Min(m.product_qty) AS qty_total,
Sum(CASE
WHEN (pol.date_planned::date >= m.date::date) THEN ml.qty_done
WHEN (m.state = 'done' and pol.date_planned::date >= m.date::date) THEN (ml.qty_done / ml_uom.factor * pt_uom.factor)
ELSE 0
END) AS qty_on_time
FROM stock_move m
JOIN stock_move_line ml
ON m.id = ml.move_id
JOIN purchase_order_line pol
ON pol.id = m.purchase_line_id
JOIN purchase_order po
@@ -45,9 +43,14 @@ FROM stock_move m
ON p.id = m.product_id
JOIN product_template pt
ON pt.id = p.product_tmpl_id
JOIN uom_uom pt_uom
ON pt_uom.id = pt.uom_id
JOIN product_category pc
ON pc.id = pt.categ_id
WHERE m.state = 'done'
LEFT JOIN stock_move_line ml
ON ml.move_id = m.id
LEFT JOIN uom_uom ml_uom
ON ml_uom.id = ml.product_uom_id
GROUP BY m.id
)""")
@@ -202,3 +202,162 @@ class TestPurchaseStockReports(TestReportsCommon):
docs = self.get_report_forecast(product_template_ids=self.product_template.ids)[1]
self.assertEqual(docs['draft_purchase_qty'], 150)
def test_vendor_delay_report_with_uom(self):
"""
PO 12 units x P
Receive 1 dozen x P
-> 100% received
"""
uom_12 = self.env.ref('uom.product_uom_dozen')
po_form = Form(self.env['purchase.order'])
po_form.partner_id = self.partner
with po_form.order_line.new() as line:
line.product_id = self.product
line.product_qty = 12
po = po_form.save()
po.button_confirm()
receipt = po.picking_ids
receipt_move = receipt.move_ids
receipt_move.move_line_ids.unlink()
receipt_move.move_line_ids = [(0, 0, {
'location_id': receipt_move.location_id.id,
'location_dest_id': receipt_move.location_dest_id.id,
'product_id': self.product.id,
'product_uom_id': uom_12.id,
'qty_done': 1,
'picking_id': receipt.id,
})]
receipt.button_validate()
data = self.env['vendor.delay.report'].read_group(
[('partner_id', '=', self.partner.id)],
['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
['product_id'],
)[0]
self.assertEqual(data['qty_on_time'], 12)
self.assertEqual(data['qty_total'], 12)
self.assertEqual(data['on_time_rate'], 100)
def test_vendor_delay_report_with_multi_location(self):
"""
PO 10 units x P
Receive
- 6 x P in Child Location 01
- 4 x P in Child Location 02
-> 100% received
"""
child_loc_01, child_loc_02 = self.stock_location.child_ids
po_form = Form(self.env['purchase.order'])
po_form.partner_id = self.partner
with po_form.order_line.new() as line:
line.product_id = self.product
line.product_qty = 10
po = po_form.save()
po.button_confirm()
receipt = po.picking_ids
receipt_move = receipt.move_ids
receipt_move.move_line_ids.unlink()
receipt_move.move_line_ids = [(0, 0, {
'location_id': receipt_move.location_id.id,
'location_dest_id': child_loc_01.id,
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'qty_done': 6,
'picking_id': receipt.id,
}), (0, 0, {
'location_id': receipt_move.location_id.id,
'location_dest_id': child_loc_02.id,
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'qty_done': 4,
'picking_id': receipt.id,
})]
receipt.button_validate()
data = self.env['vendor.delay.report'].read_group(
[('partner_id', '=', self.partner.id)],
['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
['product_id'],
)[0]
self.assertEqual(data['qty_on_time'], 10)
self.assertEqual(data['qty_total'], 10)
self.assertEqual(data['on_time_rate'], 100)
def test_vendor_delay_report_with_backorder(self):
"""
PO 10 units x P
Receive 6 x P with backorder
-> 60% received
Process the backorder
-> 100% received
"""
po_form = Form(self.env['purchase.order'])
po_form.partner_id = self.partner
with po_form.order_line.new() as line:
line.product_id = self.product
line.product_qty = 10
po = po_form.save()
po.button_confirm()
receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6
action = receipt01.button_validate()
Form(self.env[action['res_model']].with_context(action['context'])).save().process()
data = self.env['vendor.delay.report'].read_group(
[('partner_id', '=', self.partner.id)],
['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
['product_id'],
)[0]
self.assertEqual(data['qty_on_time'], 6)
self.assertEqual(data['qty_total'], 10)
self.assertEqual(data['on_time_rate'], 60)
receipt02 = receipt01.backorder_ids
receipt02.move_ids.quantity_done = 4
receipt02.button_validate()
(receipt01 | receipt02).move_ids.invalidate_recordset()
data = self.env['vendor.delay.report'].read_group(
[('partner_id', '=', self.partner.id)],
['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
['product_id'],
)[0]
self.assertEqual(data['qty_on_time'], 10)
self.assertEqual(data['qty_total'], 10)
self.assertEqual(data['on_time_rate'], 100)
def test_vendor_delay_report_without_backorder(self):
"""
PO 10 units x P
Receive 6 x P without backorder
-> 60% received
"""
po_form = Form(self.env['purchase.order'])
po_form.partner_id = self.partner
with po_form.order_line.new() as line:
line.product_id = self.product
line.product_qty = 10
po = po_form.save()
po.button_confirm()
receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6
action = receipt01.button_validate()
Form(self.env[action['res_model']].with_context(action['context'])).save().process_cancel_backorder()
data = self.env['vendor.delay.report'].read_group(
[('partner_id', '=', self.partner.id)],
['product_id', 'on_time_rate', 'qty_on_time', 'qty_total'],
['product_id'],
)[0]
self.assertEqual(data['qty_on_time'], 6)
self.assertEqual(data['qty_total'], 10)
self.assertEqual(data['on_time_rate'], 60)