From 8da28c21563ab4bc2b6762cfda2728f3126b6fd0 Mon Sep 17 00:00:00 2001 From: lreficent Date: Wed, 11 Oct 2017 12:42:58 +0200 Subject: [PATCH 1/2] [9.0][ADD] mrp_production_unreserve --- mrp_production_unreserve/README.rst | 64 +++++++++++++++++++ mrp_production_unreserve/__init__.py | 4 ++ mrp_production_unreserve/__openerp__.py | 20 ++++++ .../data/mrp_workflow.xml | 15 +++++ mrp_production_unreserve/models/__init__.py | 4 ++ .../models/mrp_production.py | 45 +++++++++++++ .../views/mrp_production_view.xml | 21 ++++++ 7 files changed, 173 insertions(+) create mode 100644 mrp_production_unreserve/README.rst create mode 100644 mrp_production_unreserve/__init__.py create mode 100644 mrp_production_unreserve/__openerp__.py create mode 100644 mrp_production_unreserve/data/mrp_workflow.xml create mode 100644 mrp_production_unreserve/models/__init__.py create mode 100644 mrp_production_unreserve/models/mrp_production.py create mode 100644 mrp_production_unreserve/views/mrp_production_view.xml diff --git a/mrp_production_unreserve/README.rst b/mrp_production_unreserve/README.rst new file mode 100644 index 000000000..f37f75a4b --- /dev/null +++ b/mrp_production_unreserve/README.rst @@ -0,0 +1,64 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== +MRP Production Unreserve +======================== + +This module backports the functionality of Manufacturing Orders (MOs) in v10 +which allows to unreserve MOs. + +Usage +===== + +To use this module, you need to: + +#. Go to a confirmed MO with some *Products to Consume* in black font (which + means that they are reserved). +#. Click the new button *Unreserve*. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/129/9.0 + +Known issues / Roadmap +====================== + +* No need to migrate this module further. The feature is out-of-the-box in v10. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Lois Rilo Antelo + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/mrp_production_unreserve/__init__.py b/mrp_production_unreserve/__init__.py new file mode 100644 index 000000000..ec50cfc0f --- /dev/null +++ b/mrp_production_unreserve/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/mrp_production_unreserve/__openerp__.py b/mrp_production_unreserve/__openerp__.py new file mode 100644 index 000000000..c70ec8168 --- /dev/null +++ b/mrp_production_unreserve/__openerp__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "MRP Production Unreserve", + "summary": "Allows you to unreserve Manufacturing Orders.", + "version": "9.0.1.0.0", + "category": "Manufacturing", + "website": "https://github.com/OCA/manufacture", + "author": "Eficent, " + "Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["mrp"], + "data": [ + "views/mrp_production_view.xml", + "data/mrp_workflow.xml", + ], +} diff --git a/mrp_production_unreserve/data/mrp_workflow.xml b/mrp_production_unreserve/data/mrp_workflow.xml new file mode 100644 index 000000000..a8523b6fd --- /dev/null +++ b/mrp_production_unreserve/data/mrp_workflow.xml @@ -0,0 +1,15 @@ + + + + + + + + moves_unreserved + + + True + + + diff --git a/mrp_production_unreserve/models/__init__.py b/mrp_production_unreserve/models/__init__.py new file mode 100644 index 000000000..7560c14d7 --- /dev/null +++ b/mrp_production_unreserve/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import mrp_production diff --git a/mrp_production_unreserve/models/mrp_production.py b/mrp_production_unreserve/models/mrp_production.py new file mode 100644 index 000000000..5d1004b1c --- /dev/null +++ b/mrp_production_unreserve/models/mrp_production.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models +from openerp import workflow + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + unreserve_visible = fields.Boolean( + string='Inventory Unreserve Visible', + compute='_compute_unreserve_visible', + help='Technical field to check when we can unreserve', + ) + + @api.depends('state', 'move_lines.reserved_quant_ids') + def _compute_unreserve_visible(self): + for order in self: + if (order.state in ['done', 'cancel'] or not + order.move_lines.mapped('reserved_quant_ids')): + order.unreserve_visible = False + else: + order.unreserve_visible = True + + @api.multi + def button_unreserve(self): + for production in self: + production.move_lines.filtered( + lambda x: x.state not in ('done', 'cancel')).do_unreserve() + if not production.test_ready(): + workflow.trg_validate( + self.env.uid, 'mrp.production', production.id, + 'moves_unreserved', self.env.cr) + return True + + @api.multi + def action_confirm(self): + """If a MO comes from 'ready' state it doesn't need to be confirmed + again.""" + from_draft = self.filtered(lambda mo: not mo.move_lines) + res = super(MrpProduction, from_draft).action_confirm() + self.write({'state': 'confirmed'}) + return res diff --git a/mrp_production_unreserve/views/mrp_production_view.xml b/mrp_production_unreserve/views/mrp_production_view.xml new file mode 100644 index 000000000..67008aea2 --- /dev/null +++ b/mrp_production_unreserve/views/mrp_production_view.xml @@ -0,0 +1,21 @@ + + + + + + mrp.production.form - mrp_production_unreserved + mrp.production + + + + + + + From b923601c38fbdf8a7c0c9574d57ecca3246f9262 Mon Sep 17 00:00:00 2001 From: lreficent Date: Mon, 18 Dec 2017 13:25:57 +0100 Subject: [PATCH 2/2] [9.0][IMP] mrp_production_unreserve: add tests --- .../models/mrp_production.py | 2 +- mrp_production_unreserve/tests/__init__.py | 4 + .../tests/test_mrp_production_unreserve.py | 76 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 mrp_production_unreserve/tests/__init__.py create mode 100644 mrp_production_unreserve/tests/test_mrp_production_unreserve.py diff --git a/mrp_production_unreserve/models/mrp_production.py b/mrp_production_unreserve/models/mrp_production.py index 5d1004b1c..65dfa95c1 100644 --- a/mrp_production_unreserve/models/mrp_production.py +++ b/mrp_production_unreserve/models/mrp_production.py @@ -10,7 +10,7 @@ class MrpProduction(models.Model): _inherit = "mrp.production" unreserve_visible = fields.Boolean( - string='Inventory Unreserve Visible', + string='MO Unreserve Visible', compute='_compute_unreserve_visible', help='Technical field to check when we can unreserve', ) diff --git a/mrp_production_unreserve/tests/__init__.py b/mrp_production_unreserve/tests/__init__.py new file mode 100644 index 000000000..f2b64ec5a --- /dev/null +++ b/mrp_production_unreserve/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_mrp_production_unreserve diff --git a/mrp_production_unreserve/tests/test_mrp_production_unreserve.py b/mrp_production_unreserve/tests/test_mrp_production_unreserve.py new file mode 100644 index 000000000..ef62d7365 --- /dev/null +++ b/mrp_production_unreserve/tests/test_mrp_production_unreserve.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests.common import TransactionCase + + +class TestMrpProductionUnreserve(TransactionCase): + def setUp(self, *args, **kwargs): + super(TestMrpProductionUnreserve, self).setUp(*args, **kwargs) + self.production_model = self.env['mrp.production'] + self.bom_model = self.env['mrp.bom'] + self.product_model = self.env['product.product'] + self.quant_model = self.env['stock.quant'] + + # Create products, BoM and MO: + self.product = self.product_model.create({ + 'name': 'Test Product', + 'type': 'product', + }) + self.component_a = self.product_model.create({ + 'name': 'Component A', + 'type': 'product', + }) + self.component_b = self.product_model.create({ + 'name': 'Component B', + 'type': 'product', + }) + self.test_bom = self.bom_model.create({ + 'product_tmpl_id': self.product.product_tmpl_id.id, + 'product_qty': 1, + 'code': 'TEST', + 'bom_line_ids': [ + (0, 0, { + 'product_id': self.component_a.id, + 'product_qty': 2, + }), + (0, 0, { + 'product_id': self.component_b.id, + 'product_qty': 1, + }) + ], + }) + self.test_mo = self.production_model.create({ + 'product_id': self.product.id, + 'product_qty': 5.0, + 'product_uom': self.product.uom_id.id, + 'bom_id': self.test_bom.id, + }) + # Create Stock for components: + wh_main = self.browse_ref('stock.warehouse0') + stock_location_id = wh_main.lot_stock_id.id + self.quant_model.create({ + 'product_id': self.component_a.id, + 'location_id': stock_location_id, + 'qty': 10.0, + }) + self.quant_model.create({ + 'product_id': self.component_b.id, + 'location_id': stock_location_id, + 'qty': 5.0, + }) + + def test_mo_unreserve(self): + self.test_mo._compute_unreserve_visible() + self.assertFalse(self.test_mo.unreserve_visible) + self.assertFalse(self.test_mo.move_lines) + self.test_mo.action_confirm() + self.test_mo.action_assign() + for l in self.test_mo.move_lines: + self.assertEqual(l.state, 'assigned') + self.test_mo._compute_unreserve_visible() + self.assertTrue(self.test_mo.unreserve_visible) + self.test_mo.button_unreserve() + for l in self.test_mo.move_lines: + self.assertEqual(l.state, 'confirmed')