diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index db4fbec72a6..15f25475249 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -255,6 +255,7 @@ class MrpProduction(models.Model):
('expected', 'Expected'),
('late', 'Late')], compute='_compute_components_availability')
show_lot_ids = fields.Boolean('Display the serial number shortcut on the moves', compute='_compute_show_lot_ids')
+ forecasted_issue = fields.Boolean(compute='_compute_forecasted_issue')
@api.depends('product_id', 'bom_id', 'company_id')
def _compute_allowed_product_ids(self):
@@ -516,6 +517,18 @@ class MrpProduction(models.Model):
('qty_positive', 'check (product_qty > 0)', 'The quantity to produce must be positive!'),
]
+ @api.depends('product_uom_qty', 'date_planned_start')
+ def _compute_forecasted_issue(self):
+ for order in self:
+ warehouse = order.location_dest_id.get_warehouse()
+ order.forecasted_issue = False
+ if order.product_id:
+ virtual_available = order.product_id.with_context(warehouse=warehouse.id, to_date=order.date_planned_start).virtual_available
+ if order.state == 'draft':
+ virtual_available += order.product_uom_qty
+ if virtual_available < 0:
+ order.forecasted_issue = True
+
@api.model
def _search_delay_alert_date(self, operator, value):
late_stock_moves = self.env['stock.move'].search([('delay_alert_date', operator, value)])
@@ -777,6 +790,19 @@ class MrpProduction(models.Model):
self.is_locked = not self.is_locked
return True
+ def action_product_forecast_report(self):
+ self.ensure_one()
+ action = self.product_id.action_product_forecast_report()
+ action['context'] = {
+ 'active_id': self.product_id.id,
+ 'active_model': 'product.product',
+ 'move_to_match_ids': self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).ids
+ }
+ warehouse = self.picking_type_id.warehouse_id
+ if warehouse:
+ action['context']['warehouse'] = warehouse.id
+ return action
+
def _create_workorder(self):
for production in self:
if not production.bom_id:
diff --git a/addons/mrp/tests/test_stock_report.py b/addons/mrp/tests/test_stock_report.py
index ce97fad278e..f4da9ff1a34 100644
--- a/addons/mrp/tests/test_stock_report.py
+++ b/addons/mrp/tests/test_stock_report.py
@@ -151,3 +151,30 @@ class TestSaleStockReports(TestReportsCommon):
# Checks the forecast report.
report_values, docs, lines = self.get_report_forecast(product_template_ids=product_apple_pie.product_tmpl_id.ids)
self.assertEqual(len(lines), 0, "Must have no line")
+
+ def test_report_forecast_3_report_line_corresponding_to_mo_highlighted(self):
+ """ When accessing the report from a MO, checks if the correct MO is highlighted in the report
+ """
+ product_banana = self.env['product.product'].create({
+ 'name': 'Banana',
+ 'type': 'product',
+ })
+
+ # We create 2 identical MO
+ mo_form = Form(self.env['mrp.production'])
+ mo_form.product_id = product_banana
+ mo_form.product_qty = 10
+ mo_1 = mo_form.save()
+ mo_1.button_plan()
+ mo_2 = mo_1.copy()
+ mo_2.button_plan()
+
+ # Check for both MO if the highlight (is_matched) corresponds to the correct MO
+ for mo in [mo_1, mo_2]:
+ context = mo.action_product_forecast_report()['context']
+ _, _, lines = self.get_report_forecast(product_template_ids=product_banana.product_tmpl_id.ids, context=context)
+ for line in lines:
+ if line['document_in'] == mo:
+ self.assertTrue(line['is_matched'], "The corresponding MO line should be matched in the forecast report.")
+ else:
+ self.assertFalse(line['is_matched'], "A line of the forecast report not linked to the MO shoud not be matched.")
diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml
index 01e1a662e0f..38abb9908d6 100644
--- a/addons/mrp/views/mrp_production_views.xml
+++ b/addons/mrp/views/mrp_production_views.xml
@@ -143,6 +143,7 @@