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 @@ +