[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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user