From 1cd1a123cb046a9f571f5a9251078f3229a3a2f0 Mon Sep 17 00:00:00 2001 From: oihane Date: Wed, 23 Sep 2015 12:46:09 +0200 Subject: [PATCH] [IMP] mrp_bom_version: Some improvements * When passing back to draft mantain active according to company definition * SQL sentence for state update in post-init-hook. * _bom_find with context state so that only those in state=active are taken into account * Tests * api.one deprecated [IMP] Little required changes [IMP] from api.one to api.multi [IMP] New tests added --- mrp_bom_version/README.rst | 64 ++++--------- mrp_bom_version/__init__.py | 18 ++-- mrp_bom_version/__openerp__.py | 35 +++---- mrp_bom_version/i18n/es.po | 2 +- mrp_bom_version/models/__init__.py | 6 +- mrp_bom_version/models/mrp_bom.py | 90 ++++++++---------- mrp_bom_version/models/res_company.py | 14 +++ mrp_bom_version/models/res_config.py | 25 ++++- mrp_bom_version/tests/__init__.py | 6 +- mrp_bom_version/tests/test_mrp_bom_version.py | 92 +++++++++++++------ mrp_bom_version/views/mrp_bom_view.xml | 46 ++++------ mrp_bom_version/views/res_config_view.xml | 21 ++++- 12 files changed, 219 insertions(+), 200 deletions(-) create mode 100644 mrp_bom_version/models/res_company.py diff --git a/mrp_bom_version/README.rst b/mrp_bom_version/README.rst index 92b4515ab..b38a8b333 100644 --- a/mrp_bom_version/README.rst +++ b/mrp_bom_version/README.rst @@ -1,20 +1,9 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg :alt: License: AGPL-3 -Module name -=========== - -mrp_bom_version, MRP - BoM version - -Short Description ------------------ - -This module is intended to add a version control to BoM (Bill of Materials) and to allow to control his life cycle. - -[es_ES] Este módulo tiene como objetivo el añadir el control de versiones a la LdM (Lista de Materiales) y permitir el control de su ciclo de vida. - -Long Description ----------------- +================= +MRP - BoM Version +================= This module provides a state in the BoM whether to allow their use in manufacturing, to do the following states are defined: @@ -26,7 +15,8 @@ manufacturing, to do the following states are defined: lines, and the new field Active, for false default when you create a new BoM. The "active" state may be passed back to state "draft", if we mark the new field "Allow re-edit the BoM list", this new field is defined in - *Configuration > Configuration > Manufacturing*. + *Configuration > Configuration > Manufacturing*. You can configure there also + if those BoM will continue with active check marked as True or not. The active state may move to state "Historical". * **Historical**: This is the last state of the LdM, you can not change any field on the form. @@ -37,49 +27,33 @@ to be unique. * **New version** : By clicking the button version, current BOM is moved to historical state, - and a new BOM is creating based on this but with version number +1 and + and a new BOM is creating based on this but with version number +1 and changing state to draft -Installation -============ - -To install this module, you need to: -** to do ** - -Configuration -============= - -To configure this module, you need to: -** to do ** Usage ===== -** to do ** +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/188/8.0 -Installation -============ -** to do ** +Bug Tracker +=========== -Configuration -============= +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 smashing it by providing a detailed and welcomed feedback +`here `_. -To configure this module, you need to: -** to do ** - -Usage -===== - -To use this module, you need to: -** to do ** Credits ======= -* odooMRP Project, www.odoomrp.com - Contributors ------------ - -** to do ** +* Pedro M. Baeza +* Ana Juaristi +* Alfredo de la Fuente +* Oihane Crucelaegui diff --git a/mrp_bom_version/__init__.py b/mrp_bom_version/__init__.py index b5a8708e0..f2dc1b552 100644 --- a/mrp_bom_version/__init__.py +++ b/mrp_bom_version/__init__.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import models -from openerp import SUPERUSER_ID -def set_bom_inactive(cr, registry): - """Set all draft or historical state BoMs inactive.""" - mrp_bom_obj = registry['mrp.bom'] - mrp_bom_ids = mrp_bom_obj.search(cr, SUPERUSER_ID, - [('active', '=', True)]) - for mrp_bom in mrp_bom_obj.browse(cr, SUPERUSER_ID, mrp_bom_ids): - mrp_bom_obj.write(cr, SUPERUSER_ID, mrp_bom.id, {'state': 'active'}) +def set_active_bom_active_state(cr, registry): + """Set those active BoMs to state 'active'""" + cr.execute("""UPDATE mrp_bom + SET state = 'active' + WHERE active = True""") diff --git a/mrp_bom_version/__openerp__.py b/mrp_bom_version/__openerp__.py index 5491ead05..e348c9bbf 100644 --- a/mrp_bom_version/__openerp__.py +++ b/mrp_bom_version/__openerp__.py @@ -1,35 +1,22 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# Copyright (c) -# 2015 Serv. Tec. Avanzados - Pedro M. Baeza (http://www.serviciosbaeza.com) -# 2015 AvanzOsc (http://www.avanzosc.es) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# -*- coding: utf-8 -*- +# (c) 2015 Alfredo de la Fuente - AvanzOSC +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + { "name": "MRP - BoM version", - "version": "1.0", + "summary": "BoM versioning", + "version": "8.0.1.0.0", + "license": "AGPL-3", "author": "OdooMRP team," "AvanzOSC," "Serv. Tecnol. Avanzados - Pedro M. Baeza", "website": "http://www.odoomrp.com", "contributors": [ - "Pedro M. Baeza ", "Ana Juaristi ", "Alfredo de la Fuente ", + "Oihane Crucelaegui ", ], "category": "Manufacturing", "depends": [ @@ -42,5 +29,5 @@ "views/mrp_bom_view.xml", ], "installable": True, - "post_init_hook": "set_bom_inactive", + "post_init_hook": "set_active_bom_active_state", } diff --git a/mrp_bom_version/i18n/es.po b/mrp_bom_version/i18n/es.po index 7c64b9ac7..31933bf3d 100644 --- a/mrp_bom_version/i18n/es.po +++ b/mrp_bom_version/i18n/es.po @@ -88,7 +88,7 @@ msgstr "Estado" #. module: mrp_bom_version #: field:mrp.bom,state:0 msgid "Status" -msgstr "Estatus" +msgstr "Estado" #. module: mrp_bom_version #: help:mrp.config.settings,group_mrp_bom_state:0 diff --git a/mrp_bom_version/models/__init__.py b/mrp_bom_version/models/__init__.py index 9f3a5c8da..7ed2e275b 100644 --- a/mrp_bom_version/models/__init__.py +++ b/mrp_bom_version/models/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import mrp_bom +from . import res_company from . import res_config diff --git a/mrp_bom_version/models/mrp_bom.py b/mrp_bom_version/models/mrp_bom.py index 111391f8c..a8161e590 100644 --- a/mrp_bom_version/models/mrp_bom.py +++ b/mrp_bom_version/models/mrp_bom.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + from openerp import models, fields, api @@ -78,85 +78,75 @@ class MrpBom(models.Model): @api.multi def button_draft(self): + self.ensure_one() + self.active = (self.company_id.active_draft if self.company_id else + self.env.user.company_id.active_draft) self.state = 'draft' @api.multi def button_new_version(self): self.ensure_one() new_bom = self._copy_bom() - self._update_bom_state_after_copy() + self.button_historical() return { 'type': 'ir.actions.act_window', 'view_type': 'form, tree', 'view_mode': 'form', 'res_model': 'mrp.bom', 'res_id': new_bom.id, - 'target': 'new', + 'target': 'current', } def _copy_bom(self): new_bom = self.copy({ 'version': self.version + 1, - 'active': True, + 'active': (self.company_id.active_draft if self.company_id else + self.env.user.company_id.active_draft), 'parent_bom': self.id, }) return new_bom - def _update_bom_state_after_copy(self): - self.write({ - 'active': False, - 'state': 'historical', - 'historical_date': fields.Date.today(), - }) - - @api.one + @api.multi def button_activate(self): + self.ensure_one() self.write({ 'active': True, 'state': 'active' }) - @api.one + @api.multi def button_historical(self): + self.ensure_one() self.write({ 'active': False, 'state': 'historical', 'historical_date': fields.Date.today() }) + def search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False): + """Add search argument for field type if the context says so. This + should be in old API because context argument is not the last one. + """ + if context is None: + context = {} + search_state = context.get('state', False) + if search_state: + args += [('state', '=', search_state)] + return super(MrpBom, self).search( + cr, uid, args, offset=offset, limit=limit, order=order, + context=context, count=count) -class MrpProduction(models.Model): - _inherit = 'mrp.production' - - def product_id_change(self, cr, uid, ids, product_id, product_qty=0, - context=None): - bom_obj = self.pool['mrp.bom'] - product_obj = self.pool['product.product'] - res = super(MrpProduction, self).product_id_change( - cr, uid, ids, product_id=product_id, product_qty=product_qty, - context=context) - if product_id: - res['value'].update({'bom_id': False}) - product_tmpl_id = product_obj.browse( - cr, uid, product_id, context=context).product_tmpl_id.id - domain = [('state', '=', 'active'), - '|', - ('product_id', '=', product_id), - '&', - ('product_id', '=', False), - ('product_tmpl_id', '=', product_tmpl_id) - ] - domain = domain + ['|', ('date_start', '=', False), - ('date_start', '<=', fields.Datetime.now()), - '|', ('date_stop', '=', False), - ('date_stop', '>=', fields.Datetime.now())] - bom_ids = bom_obj.search(cr, uid, domain, context=context) - bom_id = 0 - min_seq = 0 - for bom in bom_obj.browse(cr, uid, bom_ids, context=context): - if min_seq == 0 or bom.sequence < min_seq: - min_seq = bom.sequence - bom_id = bom.id - if bom_id > 0: - res['value'].update({'bom_id': bom_id}) - return res + @api.model + def _bom_find( + self, product_tmpl_id=None, product_id=None, properties=None): + """ Finds BoM for particular product and product uom. + @param product_tmpl_id: Selected product. + @param product_uom: Unit of measure of a product. + @param properties: List of related properties. + @return: False or BoM id. + """ + bom_id = super(MrpBom, self.with_context(state='active'))._bom_find( + product_tmpl_id=product_tmpl_id, product_id=product_id, + properties=properties) + return bom_id diff --git a/mrp_bom_version/models/res_company.py b/mrp_bom_version/models/res_company.py new file mode 100644 index 000000000..93a1157bc --- /dev/null +++ b/mrp_bom_version/models/res_company.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import fields, models + + +class ResCompany(models.Model): + _inherit = 'res.company' + + active_draft = fields.Boolean( + string='Keep re-editing BoM active', + help='This will allow you to define if those BoM passed back to draft' + ' are still activated or not', default=False) diff --git a/mrp_bom_version/models/res_config.py b/mrp_bom_version/models/res_config.py index cef8ad797..b8ce0d8d1 100644 --- a/mrp_bom_version/models/res_config.py +++ b/mrp_bom_version/models/res_config.py @@ -1,14 +1,31 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## -from openerp import models, fields +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import fields, models class MrpConfigSettings(models.TransientModel): _inherit = 'mrp.config.settings' + def _default_company_id(self): + return self.env.user.company_id + + def _default_has_default_company(self): + count = self.env['res.company'].search_count([]) + return bool(count == 1) + + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=_default_company_id) + has_default_company = fields.Boolean( + string='Has default company', readonly=True, + default=_default_has_default_company) group_mrp_bom_version = fields.Boolean( string='Allow to re-edit BoMs', implied_group='mrp_bom_version.group_mrp_bom_version', help='The active state may be passed back to state draft') + active_draft = fields.Boolean( + string='Keep re-editing BoM active', + help='This will allow you to define if those BoM passed back to draft' + ' are still activated or not', related='company_id.active_draft') diff --git a/mrp_bom_version/tests/__init__.py b/mrp_bom_version/tests/__init__.py index 2bfc72d61..12e4e76f0 100644 --- a/mrp_bom_version/tests/__init__.py +++ b/mrp_bom_version/tests/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Oihane Crucelaegui - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + from . import test_mrp_bom_version diff --git a/mrp_bom_version/tests/test_mrp_bom_version.py b/mrp_bom_version/tests/test_mrp_bom_version.py index af39a7772..c3dffde30 100644 --- a/mrp_bom_version/tests/test_mrp_bom_version.py +++ b/mrp_bom_version/tests/test_mrp_bom_version.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Alfredo de la Fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + import openerp.tests.common as common @@ -9,33 +9,71 @@ class TestMrpBomVersion(common.TransactionCase): def setUp(self): super(TestMrpBomVersion, self).setUp() - self.mrp_bom_model = self.env['mrp.bom'] - vals = {"product_tmpl_id": + self.bom_model = self.env['mrp.bom'] + self.company = self.env.ref('base.main_company') + vals = { + 'company_id': self.company.id, + 'product_tmpl_id': self.env.ref('product.product_product_11_product_template').id, - "active": True, - "bom_line_ids": + 'bom_line_ids': [(0, 0, {'product_id': self.env.ref('product.product_product_5').id}), (0, 0, {'product_id': self.env.ref('product.product_product_6').id})], - } - self.new_mrp_bom = self.mrp_bom_model.create(vals) + } + self.mrp_bom = self.bom_model.create(vals) - def test_mrp_bom_version(self): - self.assertEqual(self.new_mrp_bom.state, 'draft', - "No 'draft' state for MRP new BoM") - self.assertEqual(self.new_mrp_bom.version, 1, - "Incorrect version for MRP new BoM") - self.new_mrp_bom.button_activate() - self.assertEqual(self.new_mrp_bom.active, True, - "Incorrect active field for MRP new BoM, after" - " activation") - self.assertEqual(self.new_mrp_bom.state, 'active', - "No 'active' state for MRP new BoM, after activation") - self.new_mrp_bom.button_historical() - self.assertEqual(self.new_mrp_bom.active, False, - "Incorrect active field for MRP new BoM, after" - " historification") - self.assertEqual(self.new_mrp_bom.state, 'historical', - "No 'historical' state for MRP new BoM, after" - " activation") + def test_mrp_bom(self): + self.assertEqual( + self.mrp_bom.state, 'draft', "New BoM must be in state 'draft'") + self.assertEqual( + self.mrp_bom.version, 1, 'Incorrect version for new BoM') + self.assertFalse( + self.mrp_bom.active, 'New BoMs must be created inactive') + self.mrp_bom.button_activate() + self.assertTrue( + self.mrp_bom.active, 'Incorrect activation, check must be True') + self.assertEqual( + self.mrp_bom.state, 'active', + "Incorrect state, it should be 'active'") + self.mrp_bom.button_historical() + self.assertFalse( + self.mrp_bom.active, 'Check must be False, after historification') + self.assertEqual( + self.mrp_bom.state, 'historical', + "Incorrect state, it should be 'historical'") + + def test_mrp_bom_back2draft_default(self): + self.mrp_bom.button_activate() + self.mrp_bom.button_draft() + self.assertFalse( + self.mrp_bom.active, 'Check must be False, default in company') + + def test_mrp_bom_back2draft_active(self): + self.company.active_draft = True + self.mrp_bom.button_activate() + self.mrp_bom.button_draft() + self.assertTrue( + self.mrp_bom.active, 'Check must be True, as set in company') + + def test_mrp_bom_versioning(self): + self.mrp_bom.button_activate() + self.mrp_bom.button_new_version() + self.assertFalse( + self.mrp_bom.active, + 'Check must be False, it must have been historified') + self.assertEqual( + self.mrp_bom.state, 'historical', + 'Incorrect state, it must have been historified') + new_boms = self.bom_model.search( + [('parent_bom', '=', self.mrp_bom.id)]) + for new_bom in new_boms: + self.assertEqual( + new_bom.version, self.mrp_bom.version + 1, + 'New BoM version must be +1 from origin BoM version') + self.assertEqual( + new_bom.active, self.company.active_draft, + 'It does not match active draft check state set in company') + self.assertEqual( + new_bom.state, 'draft', + "New version must be created in 'draft' state") diff --git a/mrp_bom_version/views/mrp_bom_view.xml b/mrp_bom_version/views/mrp_bom_view.xml index 0acbb8b3c..f802cc849 100644 --- a/mrp_bom_version/views/mrp_bom_view.xml +++ b/mrp_bom_version/views/mrp_bom_view.xml @@ -5,7 +5,7 @@ ['|',('active','=',True),('active','=',False)] - + mrp.bom.tree.parent.view mrp.bom @@ -16,8 +16,8 @@ - - mrp.bom.state.tree.view + + mrp.bom.version.tree mrp.bom @@ -26,60 +26,46 @@ -