diff --git a/addons/mrp_product_expiry/__init__.py b/addons/mrp_product_expiry/__init__.py index 961d779cf21..f7a591caed7 100644 --- a/addons/mrp_product_expiry/__init__.py +++ b/addons/mrp_product_expiry/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import models from . import tests from . import wizard diff --git a/addons/mrp_product_expiry/models/__init__.py b/addons/mrp_product_expiry/models/__init__.py new file mode 100644 index 00000000000..f75cb03c89b --- /dev/null +++ b/addons/mrp_product_expiry/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import mrp_abstract_workorder +from . import mrp_workorder diff --git a/addons/mrp_product_expiry/models/mrp_abstract_workorder.py b/addons/mrp_product_expiry/models/mrp_abstract_workorder.py new file mode 100644 index 00000000000..77bae4eb238 --- /dev/null +++ b/addons/mrp_product_expiry/models/mrp_abstract_workorder.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, _ + + +class MrpAbstractWorkorder(models.AbstractModel): + _inherit = 'mrp.abstract.workorder' + + def _check_expired_lots(self): + # We use the 'skip_expired' context key to avoid to make the check when + # user already confirmed the wizard about using expired lots. + if self.env.context.get('skip_expired'): + return False + expired_lot_ids = self.raw_workorder_line_ids.filtered(lambda ml: ml.lot_id.product_expiry_alert).lot_id.ids + if expired_lot_ids: + return { + 'name': _('Confirmation'), + 'type': 'ir.actions.act_window', + 'res_model': 'expiry.picking.confirmation', + 'view_mode': 'form', + 'target': 'new', + 'context': self._get_expired_context(expired_lot_ids), + } + + def _get_expired_context(self, expired_lot_ids): + context = dict(self.env.context) + context.update({ + 'default_lot_ids': [(6, 0, expired_lot_ids)], + }) + return context diff --git a/addons/mrp_product_expiry/models/mrp_workorder.py b/addons/mrp_product_expiry/models/mrp_workorder.py new file mode 100644 index 00000000000..9e7a04372de --- /dev/null +++ b/addons/mrp_product_expiry/models/mrp_workorder.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class MrpWorkorder(models.Model): + _inherit = 'mrp.workorder' + + def record_production(self): + confirm_expired_lots = self._check_expired_lots() + if confirm_expired_lots: + return confirm_expired_lots + return super(MrpWorkorder, self).record_production() + + def _get_expired_context(self, expired_lot_ids): + context = super(MrpWorkorder, self)._get_expired_context(expired_lot_ids) + context['default_workorder_id'] = self.id + return context diff --git a/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py b/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py index 209bde5886b..0633317f6b1 100644 --- a/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py +++ b/addons/mrp_product_expiry/tests/test_mrp_product_expiry.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from odoo.addons.stock.tests.common import TestStockCommon from odoo.tests.common import Form +from odoo.exceptions import UserError class TestStockProductionLot(TestStockCommon): @@ -55,6 +56,23 @@ class TestStockProductionLot(TestStockCommon): cls.location_stock = cls.env['stock.location'].browse(cls.stock_location) + # Creation of a routing + cls.workcenter = cls.env['mrp.workcenter'].create({ + 'name': 'Bakery', + 'capacity': 2, + 'time_start': 10, + 'time_stop': 5, + 'time_efficiency': 80, + }) + cls.routing = cls.env['mrp.routing'].create({'name': 'COOK'}) + cls.operation = cls.env['mrp.routing.workcenter'].create({ + 'name': 'Bake in the oven', + 'workcenter_id': cls.workcenter.id, + 'routing_id': cls.routing.id, + 'time_cycle': 15, + 'sequence': 1, + }) + def test_01_product_produce(self): """ Checks user doesn't get a confirmation wizard when they produces with no expired components. """ @@ -99,3 +117,61 @@ class TestStockProductionLot(TestStockCommon): # Producing must return a confirmation wizard. self.assertNotEqual(res, None) self.assertEqual(res['res_model'], 'expiry.picking.confirmation') + + def test_03_workorder_without_expired_lot(self): + """ Checks user doesn't get a confirmation wizard when they makes a + workorder without expired components. """ + # Set a routing on the BOM. + self.bom_apple_pie.routing_id = self.routing + # Creates the MO, starts it and plans the Work Order. + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = self.product_apple_pie + mo_form.bom_id = self.bom_apple_pie + mo_form.product_qty = 1 + mo = mo_form.save() + mo.action_confirm() + mo.button_plan() + + wo = mo.workorder_ids[0] + wo.button_start() + # Set a non-expired lot. + wo.raw_workorder_line_ids.write({ + 'qty_done': 3, + 'lot_id': self.lot_good_apple, + }) + + res = wo.record_production() + # Try to record the production using non-expired lot must not return a wizard. + self.assertEqual(res, True) + mo.button_mark_done() + + def test_04_workorder_with_expired_lot(self): + """ Checks user doesn't get a confirmation wizard when they makes a + workorder without expired components. """ + # Set a routing on the BOM. + self.bom_apple_pie.routing_id = self.routing + # Creates the MO, starts it and plans the Work Order. + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = self.product_apple_pie + mo_form.bom_id = self.bom_apple_pie + mo_form.product_qty = 1 + mo = mo_form.save() + mo.action_confirm() + mo.button_plan() + + wo = mo.workorder_ids[0] + wo.button_start() + # Set an expired lot. + wo.raw_workorder_line_ids.write({ + 'qty_done': 3, + 'lot_id': self.lot_expired_apple, + }) + + res = wo.record_production() + # Try to record the production using expired lot must return a + # confirmation wizard. + self.assertNotEqual(res, None) + self.assertEqual(res['res_model'], 'expiry.picking.confirmation') + with self.assertRaises(UserError): + # Cannot finish the MO as the Work Order is still ongoing. + mo.button_mark_done() diff --git a/addons/mrp_product_expiry/wizard/confirm_expiry.py b/addons/mrp_product_expiry/wizard/confirm_expiry.py index 3ff319729c5..d754c070253 100644 --- a/addons/mrp_product_expiry/wizard/confirm_expiry.py +++ b/addons/mrp_product_expiry/wizard/confirm_expiry.py @@ -8,10 +8,11 @@ class ConfirmExpiry(models.TransientModel): _inherit = 'expiry.picking.confirmation' produce_id = fields.Many2one('mrp.product.produce', readonly=True) + workorder_id = fields.Many2one('mrp.workorder', readonly=True) @api.depends('lot_ids') def _compute_descriptive_fields(self): - if self.produce_id: + if self.produce_id or self.workorder_id: # Shows expired lots only if we are more than one expired lot. self.show_lots = len(self.lot_ids) > 1 if self.show_lots: @@ -35,6 +36,9 @@ class ConfirmExpiry(models.TransientModel): def confirm_produce(self): return self.produce_id.with_context(skip_expired=True).do_produce() + def confirm_workorder(self): + return self.workorder_id.with_context(skip_expired=True).record_production() + def return_to_produce_wizard(self): production = self.produce_id.production_id action = production.open_produce_product() diff --git a/addons/mrp_product_expiry/wizard/confirm_expiry_view.xml b/addons/mrp_product_expiry/wizard/confirm_expiry_view.xml index 7a6444bd5c2..d483c624070 100644 --- a/addons/mrp_product_expiry/wizard/confirm_expiry_view.xml +++ b/addons/mrp_product_expiry/wizard/confirm_expiry_view.xml @@ -8,6 +8,7 @@ + {'invisible': [('picking_ids', '=', [])]} @@ -30,6 +31,12 @@ type="object" attrs="{'invisible': [('produce_id', '=', False)]}" class="btn-secondary"/> + +