diff --git a/mrp_bom_version/models/mrp_bom.py b/mrp_bom_version/models/mrp_bom.py index 2298f8be2..dbc692a97 100644 --- a/mrp_bom_version/models/mrp_bom.py +++ b/mrp_bom_version/models/mrp_bom.py @@ -3,6 +3,7 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from openerp import models, fields, api +from openerp.tools import config class MrpBom(models.Model): @@ -24,14 +25,29 @@ class MrpBom(models.Model): parent = parent.parent_bom self.old_versions = old_version + def _default_active(self): + """Needed for preserving normal flow when testing other modules.""" + res = False + if config['test_enable']: + res = not bool(self.env.context.get('test_mrp_bom_version')) + return res + + def _default_state(self): + """Needed for preserving normal flow when testing other modules.""" + res = 'active' + if (config['test_enable'] and + self.env.context.get('test_mrp_bom_version')): + res = 'draft' + return res + active = fields.Boolean( - string='Active', default=False, readonly=True, - states={'draft': [('readonly', False)]}) + default=_default_active, + readonly=True, states={'draft': [('readonly', False)]}) historical_date = fields.Date(string='Historical Date', readonly=True) state = fields.Selection( selection=[('draft', 'Draft'), ('active', 'Active'), ('historical', 'Historical')], string='State', - index=True, readonly=True, default='draft', copy=False) + index=True, readonly=True, default=_default_state, copy=False) product_tmpl_id = fields.Many2one( readonly=True, states={'draft': [('readonly', False)]}) product_id = fields.Many2one( diff --git a/mrp_bom_version/tests/test_mrp_bom_version.py b/mrp_bom_version/tests/test_mrp_bom_version.py index 62a159284..6a9dd4d1c 100644 --- a/mrp_bom_version/tests/test_mrp_bom_version.py +++ b/mrp_bom_version/tests/test_mrp_bom_version.py @@ -10,7 +10,8 @@ class TestMrpBomVersion(common.TransactionCase): def setUp(self): super(TestMrpBomVersion, self).setUp() self.parameter_model = self.env['ir.config_parameter'] - self.bom_model = self.env['mrp.bom'] + self.bom_model = self.env['mrp.bom'].with_context( + test_mrp_bom_version=True) self.company = self.env.ref('base.main_company') vals = { 'company_id': self.company.id, diff --git a/mrp_hook/README.rst b/mrp_hook/README.rst index 20ead183a..f14f9f8c6 100644 --- a/mrp_hook/README.rst +++ b/mrp_hook/README.rst @@ -1,24 +1,35 @@ +.. 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 + =============================== Hooks for enabling advanced MRP =============================== Technical module that provides the proper framework infrastructure (hooks, fallback, etc) to enable advanced functionality in the manufacturing area, -as https://github.com/odoo/odoo/pull/8885 hasn't been accepted for v8. +as https://github.com/odoo/odoo/pull/8885 hasn't been accepted for v8: -* Hooks in *_bom_explode* method for returning dictionary for consume and +* Hooks in *_bom_explode* method to return a dictionary for consumption and workcenter lines. -* Provide product and template. +* Provide product and template on *_bom_find*. -By itself it doesn't provide anything, but serves as base for others modules -to develop its features. +Usage +===== + +By itself it doesn't provide anything visible, but serves as base for others +modules to develop its features. + +.. 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/8.0 Known issues / Roadmap ====================== * This module fully overwrites _bom_explode method, so any other module - inheriting this method should take that into account. -* On v9, this module can be removed. + inheriting this method should take this into account. +* On v9, this module can be removed, as the hooks have been integrated. Credits ======= @@ -27,3 +38,24 @@ Contributors ------------ * Pedro M. Baeza + +Images +------ + +* Original Odoo MRP icon +* Thanks to https://openclipart.org/detail/151441/lifting-hook + +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_hook/__init__.py b/mrp_hook/__init__.py index 721dbe492..2919fd270 100644 --- a/mrp_hook/__init__.py +++ b/mrp_hook/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# (c) 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import models diff --git a/mrp_hook/__openerp__.py b/mrp_hook/__openerp__.py index 7c573e9ae..26e815891 100644 --- a/mrp_hook/__openerp__.py +++ b/mrp_hook/__openerp__.py @@ -5,10 +5,13 @@ { "name": "MRP Hooks", "version": "8.0.1.0.0", + "license": "AGPL-3", "category": "Hidden", "author": "OdooMRP team, " "AvanzOSC, " - "Serv. Tecnol. Avanzados - Pedro M. Baeza", + "Serv. Tecnol. Avanzados - Pedro M. Baeza, " + "Antiun Ingeniería, " + "Odoo Community Association (OCA), ", "website": "http://www.odoomrp.com", "contributors": [ "Pedro M. Baeza ", @@ -16,7 +19,8 @@ "depends": [ "mrp", ], - "data": [ + "images": [ + "images/image.png" ], "installable": True } diff --git a/mrp_hook/i18n/es.po b/mrp_hook/i18n/es.po index 779847fce..bc5071d94 100644 --- a/mrp_hook/i18n/es.po +++ b/mrp_hook/i18n/es.po @@ -26,7 +26,7 @@ msgstr "Lista de material" #: code:addons/mrp_hook/models/mrp_bom.py:119 #, python-format msgid "BoM \"%s\" contains a BoM line with a product recursion: \"%s\"." -msgstr "" +msgstr "La LdM \"%s\" contiene una línea con un producto recursivo: \"%s\"." #. module: mrp_hook #: code:addons/mrp_hook/models/mrp_bom.py:147 @@ -34,4 +34,6 @@ msgstr "" msgid "" "BoM \"%s\" contains a phantom BoM line but the product \"%s\" does not have " "any BoM defined." +"La LdM \"%s\" contiene una línea fantasma pero el producto \"%s\" no tiene " +"ninguna LdM definida." msgstr "" diff --git a/mrp_hook/images/image.png b/mrp_hook/images/image.png new file mode 100644 index 000000000..6290840b1 Binary files /dev/null and b/mrp_hook/images/image.png differ diff --git a/mrp_hook/images/image.svg b/mrp_hook/images/image.svg new file mode 100644 index 000000000..74f0df758 --- /dev/null +++ b/mrp_hook/images/image.svg @@ -0,0 +1,81 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/mrp_hook/models/mrp_bom.py b/mrp_hook/models/mrp_bom.py index 8035d77c9..c64055558 100644 --- a/mrp_hook/models/mrp_bom.py +++ b/mrp_hook/models/mrp_bom.py @@ -2,7 +2,7 @@ # (c) 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from openerp import models, api, tools, _ +from openerp import api, models, tools, _ from openerp.exceptions import Warning as UserError from openerp.addons.product import _common @@ -66,17 +66,12 @@ class MrpBom(models.Model): def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None, context=None): - return bom._bom_explode( - product, factor, properties=properties, level=level, - routing_id=routing_id, previous_products=previous_products, - master_bom=master_bom) - - @api.v8 - def _bom_explode(self, product, factor, properties=None, level=0, - routing_id=False, previous_products=None, - master_bom=None): """ Finds Products and Work Centers for related BoM for manufacturing order. + + Verbatim copy of core method extracting the hooks already merged on + v9 branch. + @param bom: BoM of particular product template. @param product: Select a particular variant of the BoM. If False use BoM without variants. @@ -91,6 +86,15 @@ class MrpBom(models.Model): @return: result: List of dictionaries containing product details. result2: List of dictionaries containing Work Center details. """ + return bom._bom_explode( + product, factor, properties=properties, level=level, + routing_id=routing_id, previous_products=previous_products, + master_bom=master_bom) + + @api.v8 + def _bom_explode(self, product, factor, properties=None, level=0, + routing_id=False, previous_products=None, + master_bom=None): self.ensure_one() bom = self uom_obj = self.env["product.uom"] diff --git a/mrp_hook/static/description/icon.png b/mrp_hook/static/description/icon.png new file mode 100644 index 000000000..c6a65c2c6 Binary files /dev/null and b/mrp_hook/static/description/icon.png differ diff --git a/mrp_hook/static/description/icon.svg b/mrp_hook/static/description/icon.svg new file mode 100644 index 000000000..0ce23bbdd --- /dev/null +++ b/mrp_hook/static/description/icon.svg @@ -0,0 +1,74 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/mrp_hook/tests/__init__.py b/mrp_hook/tests/__init__.py new file mode 100644 index 000000000..f0770fd12 --- /dev/null +++ b/mrp_hook/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015 Pedro M. Baeza +# © 2015 Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import test_mrp_hook diff --git a/mrp_hook/tests/test_mrp_hook.py b/mrp_hook/tests/test_mrp_hook.py new file mode 100644 index 000000000..9d94e98a8 --- /dev/null +++ b/mrp_hook/tests/test_mrp_hook.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# © 2015 Avanzosc +# © 2015 Pedro M. Baeza - Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import openerp.tests.common as common +from openerp.exceptions import Warning as UserError + + +class TestMrpHook(common.TransactionCase): + def setUp(self): + super(TestMrpHook, self).setUp() + self.product_model = self.env['product.product'] + self.product = self.product_model.create({'name': 'Test product'}) + self.raw_product = self.product_model.create({'name': 'Raw product'}) + self.bom = self.env['mrp.bom'].create( + { + 'name': self.product.name, + 'product_id': self.product.id, + 'product_tmpl_id': self.product.product_tmpl_id.id, + 'bom_line_ids': [ + (0, 0, {'product_id': self.raw_product.id, + 'type': 'normal', + 'product_qty': 2})] + }) + + def test_bom_explode_normal(self): + products = self.bom._bom_explode(self.product, 1)[0] + self.assertEqual(len(products), 1) + self.assertEqual(products[0]['product_id'], self.raw_product.id) + self.assertEqual(products[0]['product_qty'], 2) + + def test_bom_explode_phantom_error(self): + self.bom.bom_line_ids[0].type = 'phantom' + with self.assertRaises(UserError): + self.bom._bom_explode(self.product, 1) + + def test_bom_explode_phantom(self): + self.bom.bom_line_ids[0].type = 'phantom' + raw_product_2 = self.product_model.create({'name': 'Raw product 2'}) + self.env['mrp.bom'].create( + { + 'name': self.raw_product.name, + 'product_id': self.raw_product.id, + 'product_tmpl_id': self.raw_product.product_tmpl_id.id, + 'bom_line_ids': [ + (0, 0, {'product_id': raw_product_2.id, + 'type': 'normal', + 'product_qty': 3})] + }) + products = self.bom._bom_explode(self.product, 1)[0] + self.assertEqual(len(products), 1) + self.assertEqual(products[0]['product_id'], raw_product_2.id) + self.assertEqual(products[0]['product_qty'], 6) diff --git a/mrp_operations_extension/README.rst b/mrp_operations_extension/README.rst index 56cd686c0..526e2cf74 100644 --- a/mrp_operations_extension/README.rst +++ b/mrp_operations_extension/README.rst @@ -1,16 +1,104 @@ -MRP operations extension module -=============================== +.. 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 + +================================== +Manufacturing Operations Extension +================================== This module adds: -- New table to store operations to avoid typing them again. -- Adds a relation from WorkcenterLines to BoM Lists. -- Adds a relation from WorkcenterLines to Manufacturing Orders in Scheduled/Consumed/Finished Products. -- Adds a relation between Routing Work Center Lines and Work Center extra Info. +* New table to store operations to avoid typing them again. +* Adds a relation from workcenter lines to Bill of Materials (BoM) lists. +* Adds a relation between workcenter lines and Manufacturing Orders in + Scheduled/Consumed/Finished products. +* Adds a relation between workcenter lines and BoM lists. +* Allows to set specific times per routing in addition to workcenter times. +* Controls the availability of material in operations +* Controls operation beginning in previous operations are not finished. -Controls the availability of material in operations, and controls operation -start with respect to previous. +Installation +============ +Once the module is installed, it transforms all existing routings to: + +* Mark the last operation as the one where the production is done. +* Create a workcenter routing line (new model for allowing more than one + workcenter per operation) for the current selected workcenter. + +Configuration +============= + +Go to *Settings > Manufacturing* and activate "Manage routings and work orders" +for handling the features of this module. + +Usage +===== + +You can go to *Manufacturing > Configuration > Operations* to add templates +of the operations you do on your manufacturing process defining: + +* Possible workcenters to use. +* Number of operators involved. +* Extended description. + +This operation template can then be selected when you create routing lines +in *Manufacturing > Products > Routings* as the default initial data for that +line. + +In this screen, you are now allowed to put more than one possible work +centers, and you can also select if the capacity data of the work center +is specific for that route or the general one. + +You can also define here in which of the lines the production is going to be +made, in order to prevent to do it before reaching this operation. This is done +marking the check "Produce here" in the routing line. + +One last possibility for the routing lines is to set if you require to finish +previous operations before allowing to start this one, which is done marking +the check "Previous operations finished". + +Once defined the routing, you can select the routing on your BoMs, going to +*Manufacturing > Products > Bill of Materials*, and select for each component +in which routing operation you are going to consume the material. + +Finally, having all set, we can create a manufacturing order with a BoM and +a routing selected. Clicking on "Compute Data" button on "Work Orders" or +"Scheduled Products" tab, we will preview which work orders and products we +will need for this manufacturing. + +Clicking on a work order line, we can see in detail the times involved for that +operation and the raw materials that are going to be consumed on it. + +We can start the manufacturing process clicking on "Confirm Production" button. +Then, we can control each operation start from the "Work Orders" tab of the +manufacturing order, or go to *Manufacturing > Manufacturing > Work Orders* +to see them directly. We will only be allowed to consume the materials that +has been assigned to the corresponding work order, and to produce when the +operation with the "Produce here" flag is set. + +.. 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/8.0 + +Known issues +============ + +* *hr_timesheet* is a big dependency for only catching the employee cost when + selecting operators in the workcenter. A glue module for this operation + would be desired. + +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 smashing it by providing a detailed and welcomed `feedback +`_. Credits ======= @@ -23,3 +111,25 @@ Contributors * Oihane Crucelaegui * Pedro M. Baeza * Ana Juaristi +* Rafael Blasco + +Images +------ + +* Original Odoo MRP icon +* Thanks to https://openclipart.org/detail/151831/work-bench + +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_operations_extension/__init__.py b/mrp_operations_extension/__init__.py index c06d7820b..3fff55640 100644 --- a/mrp_operations_extension/__init__.py +++ b/mrp_operations_extension/__init__.py @@ -1,33 +1,8 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Avanzosc +# (c) 2015 Pedro M. Baeza - Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + from . import models from . import wizard -from openerp import api, SUPERUSER_ID - - -def create_default_routing_workcenter_line(cr): - with api.Environment.manage(): - env = api.Environment(cr, SUPERUSER_ID, {}) - routing_wcs = env['mrp.routing.workcenter'].search( - [('op_wc_lines', '=', False)]) - for routing_wc in routing_wcs: - routing_wc.op_wc_lines = [ - (0, 0, {'workcenter': routing_wc.workcenter_id, - 'default': True, - 'custom_data': False})] - - -def post_init_hook(cr, pool): - """ Set do_production on the last workcenter line of each routing """ - cr.execute( - """ - UPDATE mrp_routing_workcenter SET do_production = TRUE - WHERE id IN ( - SELECT ( - SELECT id FROM mrp_routing_workcenter WHERE routing_id = mr.id - ORDER BY sequence DESC, id DESC LIMIT 1) - FROM mrp_routing mr); - """) - create_default_routing_workcenter_line(cr) +from .hooks import post_init_hook diff --git a/mrp_operations_extension/__openerp__.py b/mrp_operations_extension/__openerp__.py index 43e01edf2..ccd188823 100644 --- a/mrp_operations_extension/__openerp__.py +++ b/mrp_operations_extension/__openerp__.py @@ -1,29 +1,19 @@ - -# -*- encoding: utf-8 -*- -############################################################################## -# -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. -# -############################################################################## +# -*- coding: utf-8 -*- +# (c) 2014-2015 Avanzosc +# (c) 2014-2015 Pedro M. Baeza +# (c) 2015 Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { - "name": "MRP Operations Extension", + "name": "Manufacturing Operations Extension", "version": "8.0.2.0.0", "category": "Manufacturing", + "license": "AGPL-3", "author": "OdooMRP team, " "AvanzOSC, " - "Serv. Tecnol. Avanzados - Pedro M. Baeza", + "Serv. Tecnol. Avanzados - Pedro M. Baeza, " + "Antiun Ingeniería S.L., " + "Odoo Community Association (OCA)", "website": "http://www.odoomrp.com", "contributors": [ "Daniel Campos ", @@ -35,7 +25,6 @@ "depends": [ "mrp_operations", "mrp_hook", - "stock", "hr_timesheet", ], "data": [ @@ -50,6 +39,15 @@ "security/ir.model.access.csv", "security/mrp_operations_extension_security.xml", ], + "images": [ + 'images/operation.png', + 'images/routing.png', + 'images/routing_line.png', + 'images/bom.png', + 'images/manufacturing_order.png', + 'images/work_order.png', + 'images/work_order2.png', + ], "demo": [ "demo/res_partner_demo.xml", "demo/hr_employee_demo.xml", diff --git a/mrp_operations_extension/data/mrp_operations_extension_data.xml b/mrp_operations_extension/data/mrp_operations_extension_data.xml index 4416a296d..086c3d426 100644 --- a/mrp_operations_extension/data/mrp_operations_extension_data.xml +++ b/mrp_operations_extension/data/mrp_operations_extension_data.xml @@ -5,7 +5,7 @@ Work Center mrp.workcenter - + Work Order mrp.production.workcenter.line diff --git a/mrp_operations_extension/hooks.py b/mrp_operations_extension/hooks.py new file mode 100644 index 000000000..bcb6255e9 --- /dev/null +++ b/mrp_operations_extension/hooks.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# © 2015 Pedro M. Baeza - Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import api, SUPERUSER_ID + + +def create_default_routing_workcenter_line(cr): + with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + routing_wcs = env['mrp.routing.workcenter'].search( + [('op_wc_lines', '=', False)]) + for routing_wc in routing_wcs: + routing_wc.op_wc_lines = [ + (0, 0, {'workcenter': routing_wc.workcenter_id, + 'default': True, + 'custom_data': False})] + + +def post_init_hook(cr, pool): + """ Set do_production on the last workcenter line of each routing """ + cr.execute( + """ + UPDATE mrp_routing_workcenter SET do_production = TRUE + WHERE id IN ( + SELECT ( + SELECT id FROM mrp_routing_workcenter WHERE routing_id = mr.id + ORDER BY sequence DESC, id DESC LIMIT 1) + FROM mrp_routing mr); + """) + create_default_routing_workcenter_line(cr) diff --git a/mrp_operations_extension/i18n/en_US.po b/mrp_operations_extension/i18n/en_US.po deleted file mode 100644 index 38feabaff..000000000 --- a/mrp_operations_extension/i18n/en_US.po +++ /dev/null @@ -1,553 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * mrp_operations_extension -# -# Translators: -msgid "" -msgstr "" -"Project-Id-Version: odoomrp-wip (8.0)\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-07 10:43+0000\n" -"PO-Revision-Date: 2015-09-10 16:39+0000\n" -"Last-Translator: <>\n" -"Language-Team: English (United States) (http://www.transifex.com/oca/odoomrp-wip-8-0/language/en_US/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: en_US\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#. module: mrp_operations_extension -#: field:mrp.workcenter,op_number:0 -msgid "# Operators" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,op_number:0 -#: field:mrp.routing.operation,op_number:0 -msgid "# operators" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Actual Production Date" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_production.py:36 -#, python-format -msgid "At least one work order must have checked 'Produce here'" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_bom -msgid "Bill of Material" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -msgid "Cancel" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -msgid "Cancel Order" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,capacity_per_cycle:0 -msgid "Capacity per cycle" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_bom.py:71 -#, python-format -msgid "Changing Routing" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_bom.py:72 -#, python-format -msgid "" -"Changing routing will cause to change the operation in which each component " -"will be consumed, by default it is set the first one of the routing" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Check Availability" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,code:0 -msgid "Code" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.actions.act_window,name:mrp_operations_extension.act_mrp_work_order_consume -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -msgid "Consume" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -#: selection:mrp.work.order.produce,mode:0 -msgid "Consume & Produce" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -msgid "Consume Lines" -msgstr "" - -#. module: mrp_operations_extension -#: selection:mrp.work.order.produce,mode:0 -msgid "Consume Only" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.bom.line,operation:0 -msgid "Consumed in" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,create_uid:0 -#: field:mrp.routing.operation,create_uid:0 -#: field:mrp.work.order.produce,create_uid:0 -msgid "Created by" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,create_date:0 -#: field:mrp.routing.operation,create_date:0 -#: field:mrp.work.order.produce,create_date:0 -msgid "Created on" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,default:0 -msgid "Default" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.routing.workcenter:mrp_operations_extension.mrp_routing_workcenter_tree_view_inh -msgid "Default workcenter" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,description:0 -msgid "Description" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Duration" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,time_efficiency:0 -msgid "Efficiency factor" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Extra Information" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,final_product:0 -msgid "Final Product to Stock" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Force Reservation" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,id:0 field:mrp.routing.operation,id:0 -#: field:mrp.work.order.produce,id:0 -msgid "ID" -msgstr "" - -#. module: mrp_operations_extension -#: help:mrp.routing.workcenter,do_production:0 -msgid "" -"If enabled, the production and movement to stock of the final products will " -"be done in this operation. There can be only one operation per route with " -"this check marked." -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Information" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,write_uid:0 -#: field:mrp.routing.operation,write_uid:0 -#: field:mrp.work.order.produce,write_uid:0 -msgid "Last Updated by" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,write_date:0 -#: field:mrp.routing.operation,write_date:0 -#: field:mrp.work.order.produce,write_date:0 -msgid "Last Updated on" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,lot_id:0 -msgid "Lot" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_operation_workcenter -msgid "MRP Operation Workcenter" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_routing_operation -msgid "MRP Routing Operation" -msgstr "MRP Routing Operation" - -#. module: mrp_operations_extension -#: field:mrp.config.settings,group_mrp_workers:0 -msgid "Manage operators in work centers " -msgstr "" - -#. module: mrp_operations_extension -#: model:res.groups,name:mrp_operations_extension.group_mrp_workers -msgid "Manufacturing Operators" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_production -msgid "Manufacturing Order" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -msgid "Materials" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_production.py:170 -#, python-format -msgid "Missing materials to start the production" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,mode:0 -msgid "Mode" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.production.workcenter.line,move_lines:0 -msgid "Moves" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,name:0 -msgid "Name" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.workcenter,operation:0 -msgid "Operation" -msgstr "Operation" - -#. module: mrp_operations_extension -#: model:ir.ui.menu,name:mrp_operations_extension.mrp_routing_menu -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_future_calendar -msgid "Operations" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.workcenter,op_avg_cost:0 -msgid "Operator average hour cost" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,op_avg_cost:0 -msgid "Operator avg. hour cost" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.workcenter:mrp_operations_extension.mrp_workcenter_form_view_inh -#: field:mrp.workcenter,operators:0 -msgid "Operators" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,picking_type_id:0 -#: field:mrp.routing.workcenter,picking_type_id:0 -msgid "Picking Type" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Planned Date" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.workcenter,op_wc_lines:0 -msgid "Possible work centers for this operation" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.routing.workcenter:mrp_operations_extension.mrp_routing_workcenter_form_view_inh -msgid "Possible workcenters" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.workcenter,post_op_product:0 -msgid "Post-operation costing product" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.workcenter,pre_op_product:0 -msgid "Pre-operation costing product" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing,previous_operations_finished:0 -#: field:mrp.routing.workcenter,previous_operations_finished:0 -msgid "Previous operations finished" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_production.py:166 -#, python-format -msgid "Previous operations not finished" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.actions.act_window,name:mrp_operations_extension.act_mrp_work_order_produce -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -msgid "Produce" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.production.workcenter.line,do_production:0 -#: field:mrp.routing.workcenter,do_production:0 -msgid "Produce here" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,product_id:0 -msgid "Product" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -#: field:mrp.production.workcenter.line,product_line:0 -msgid "Product Lines" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_product_produce_line -msgid "Product Produce Consume lines" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_form_view_inh -#: view:mrp.production.workcenter.line:mrp_operations_extension.workcenter_line_inh_form_view -msgid "Product to Produce" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_production_product_line -msgid "Production Scheduled Product" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,consume_lines:0 -msgid "Products Consumed" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,steps:0 -msgid "Relevant Steps" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_routing -msgid "Routing" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.actions.act_window,name:mrp_operations_extension.mrp_routing_operation_action -#: view:mrp.routing.operation:mrp_operations_extension.rountig_operation_form -#: view:mrp.routing.operation:mrp_operations_extension.rountig_operation_tree -msgid "Routing Operation" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.workcenter:mrp_operations_extension.mrp_workcenter_form_view_inh -#: field:mrp.workcenter,rt_operations:0 -msgid "Routing Operations" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.production.workcenter.line,routing_wc_line:0 -msgid "Routing WC Line" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,routing_workcenter:0 -msgid "Routing workcenter" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,product_qty:0 -msgid "Select Quantity" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_stock_move -msgid "Stock Move" -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_routing.py:55 -#, python-format -msgid "There must be one and only one line set as default." -msgstr "" - -#. module: mrp_operations_extension -#: code:addons/mrp_operations_extension/models/mrp_routing.py:19 -#, python-format -msgid "" -"There must be one and only one operation with 'Produce here' check marked." -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.production.workcenter.line,time_start:0 -msgid "Time Start" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.production.workcenter.line,time_stop:0 -msgid "Time Stop" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,time_stop:0 -msgid "Time after prod." -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,time_start:0 -msgid "Time before prod." -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,time_cycle:0 -msgid "Time for 1 cycle (hours)" -msgstr "" - -#. module: mrp_operations_extension -#: help:mrp.operation.workcenter,time_cycle:0 -msgid "Time in hours for doing one cycle." -msgstr "" - -#. module: mrp_operations_extension -#: help:mrp.operation.workcenter,time_stop:0 -msgid "Time in hours for the cleaning." -msgstr "" - -#. module: mrp_operations_extension -#: help:mrp.operation.workcenter,time_start:0 -msgid "Time in hours for the setup." -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -msgid "To Consume" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.routing.workcenter:mrp_operations_extension.mrp_routing_workcenter_tree_view_inh -msgid "Total" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.work.order.produce,track_production:0 -msgid "Track production" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_workcenter -msgid "Work Center" -msgstr "Work Center" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_routing_workcenter -msgid "Work Center Usage" -msgstr "" - -#. module: mrp_operations_extension -#: model:ir.model,name:mrp_operations_extension.model_mrp_production_workcenter_line -#: field:mrp.production.product.line,work_order:0 -#: model:res.request.link,name:mrp_operations_extension.req_link_mrp_workcenter -#: field:stock.move,work_order:0 -msgid "Work Order" -msgstr "Work Order" - -#. module: mrp_operations_extension -#: field:mrp.routing.operation,workcenters:0 -msgid "Work centers" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.product.produce.line,work_produce_id:0 -msgid "Work produce id" -msgstr "" - -#. module: mrp_operations_extension -#: field:mrp.operation.workcenter,workcenter:0 -msgid "Workcenter" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.routing.operation:mrp_operations_extension.rountig_operation_form -msgid "Workcenters" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -msgid "draft,startworking" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.production:mrp_operations_extension.mrp_production_operation_buttons_form_view -msgid "oe_highlight" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_consume_wizard -#: view:mrp.work.order.produce:mrp_operations_extension.view_mrp_product_produce_wizard -msgid "or" -msgstr "" - -#. module: mrp_operations_extension -#: view:mrp.routing.workcenter:mrp_operations_extension.mrp_routing_workcenter_form_view_inh -msgid "{'invisible': [('op_wc_lines', '!=', [])]}" -msgstr "" diff --git a/mrp_operations_extension/i18n/es.po b/mrp_operations_extension/i18n/es.po index 33511c027..a94daf42c 100644 --- a/mrp_operations_extension/i18n/es.po +++ b/mrp_operations_extension/i18n/es.po @@ -227,7 +227,7 @@ msgstr "Operación de la ruta" #. module: mrp_operations_extension #: field:mrp.config.settings,group_mrp_workers:0 -msgid "Manage operators in work centers " +msgid "Manage operators in work centers" msgstr "Gestionar operadores en los centros de trabajo" #. module: mrp_operations_extension @@ -280,12 +280,12 @@ msgstr "Operaciones" #. module: mrp_operations_extension #: field:mrp.workcenter,op_avg_cost:0 -msgid "Operator average hour cost" +msgid "Operator average hourly cost" msgstr "Coste hora medio del operador" #. module: mrp_operations_extension #: field:mrp.operation.workcenter,op_avg_cost:0 -msgid "Operator avg. hour cost" +msgid "Operator average hourly cost" msgstr "Coste hora medio operador" #. module: mrp_operations_extension diff --git a/mrp_operations_extension/images/bom.png b/mrp_operations_extension/images/bom.png new file mode 100644 index 000000000..76aaa4034 Binary files /dev/null and b/mrp_operations_extension/images/bom.png differ diff --git a/mrp_operations_extension/images/manufacturing_order.png b/mrp_operations_extension/images/manufacturing_order.png new file mode 100644 index 000000000..2eab7b3f1 Binary files /dev/null and b/mrp_operations_extension/images/manufacturing_order.png differ diff --git a/mrp_operations_extension/images/operation.png b/mrp_operations_extension/images/operation.png new file mode 100644 index 000000000..39ce1a3c8 Binary files /dev/null and b/mrp_operations_extension/images/operation.png differ diff --git a/mrp_operations_extension/images/routing.png b/mrp_operations_extension/images/routing.png new file mode 100644 index 000000000..3643567f8 Binary files /dev/null and b/mrp_operations_extension/images/routing.png differ diff --git a/mrp_operations_extension/images/routing_line.png b/mrp_operations_extension/images/routing_line.png new file mode 100644 index 000000000..33f122f67 Binary files /dev/null and b/mrp_operations_extension/images/routing_line.png differ diff --git a/mrp_operations_extension/images/work_order.png b/mrp_operations_extension/images/work_order.png new file mode 100644 index 000000000..818e7e7a2 Binary files /dev/null and b/mrp_operations_extension/images/work_order.png differ diff --git a/mrp_operations_extension/images/work_order2.png b/mrp_operations_extension/images/work_order2.png new file mode 100644 index 000000000..4e7b75058 Binary files /dev/null and b/mrp_operations_extension/images/work_order2.png differ diff --git a/mrp_operations_extension/models/__init__.py b/mrp_operations_extension/models/__init__.py index 9df65920a..4e59efee2 100644 --- a/mrp_operations_extension/models/__init__.py +++ b/mrp_operations_extension/models/__init__.py @@ -2,10 +2,10 @@ ############################################################################## # For copyright and license notices, see __openerp__.py file in root directory ############################################################################## -from . import mrp_routing -from . import mrp_production from . import mrp_bom -from . import mrp_workcenter +from . import mrp_production +from . import mrp_routing from . import mrp_routing_operation -from . import stock_move +from . import mrp_workcenter from . import res_config +from . import stock_move diff --git a/mrp_operations_extension/models/mrp_bom.py b/mrp_operations_extension/models/mrp_bom.py index d680123ff..d397d1a73 100644 --- a/mrp_operations_extension/models/mrp_bom.py +++ b/mrp_operations_extension/models/mrp_bom.py @@ -16,12 +16,10 @@ class MrpBom(models.Model): cycle = int(math.ceil(factor / (wc_use.cycle_nbr or 1))) hour = wc_use.hour_nbr * cycle default_wc_line = wc_use.op_wc_lines.filtered(lambda r: r.default) - if default_wc_line.custom_data: - time_start = default_wc_line.time_start - time_stop = default_wc_line.time_stop - else: - time_start = default_wc_line.workcenter.time_start - time_stop = default_wc_line.workcenter.time_stop + data_source = (default_wc_line if default_wc_line.custom_data else + default_wc_line.workcenter) + time_start = data_source.time_start + time_stop = data_source.time_stop res.update({ 'cycle': cycle, 'hour': hour, @@ -44,15 +42,16 @@ class MrpBom(models.Model): def onchange_routing_id(self): for line in self.bom_line_ids: line.operation = self.routing_id.workcenter_lines[:1] + res = {} if self.routing_id: - return {'warning': { - 'title': _('Changing Routing'), - 'message': _("Changing routing will cause to change the" - " operation in which each component will be" - " consumed, by default it is set the first" - " one of the routing") - }} - return {} + res['warning'] = { + 'title': _('Changing Routing'), + 'message': _( + "Changing routing will cause to change the operation in " + "which each component will be consumed, by default it is " + "set the first one of the routing") + } + return res class MrpBomLine(models.Model): diff --git a/mrp_operations_extension/models/mrp_production.py b/mrp_operations_extension/models/mrp_production.py index d95ea3f38..cf1bc9b6b 100644 --- a/mrp_operations_extension/models/mrp_production.py +++ b/mrp_operations_extension/models/mrp_production.py @@ -9,31 +9,20 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' workcenter_lines = fields.One2many(readonly=False) - date_planned = fields.Datetime(states={'draft': [('readonly', False)], - 'confirmed': [('readonly', False)], - 'ready': [('readonly', False)]}) - - def _get_minor_sequence_operation(self, operations): - return min(operations, key=lambda x: x.sequence) - - @api.model - def _moves_assigned(self): - res = super(MrpProduction, self)._moves_assigned() - if res: - return True - operation = self._get_minor_sequence_operation(self.workcenter_lines) - assigned_moves, no_assigned_products = \ - self._get_operation_moves(operation, state='assigned') - return no_assigned_products == [] + date_planned = fields.Datetime( + states={'draft': [('readonly', False)], + 'confirmed': [('readonly', False)], + 'ready': [('readonly', False)]}) @api.multi def action_confirm(self): - res = super(MrpProduction, self).action_confirm() - if (self.routing_id and - not any([x.do_production for x in self.workcenter_lines])): - raise exceptions.Warning( - _("At least one work order must have checked 'Produce here'")) - return res + for prod in self: + if (prod.workcenter_lines and + not len(prod.workcenter_lines.filtered('do_production'))): + raise exceptions.Warning( + _("At least one work order must have checked 'Produce " + "here'")) + return super(MrpProduction, self).action_confirm() @api.multi def _action_compute_lines(self, properties=None): @@ -68,11 +57,11 @@ class MrpProductionWorkcenterLine(models.Model): _inherit = 'mrp.production.workcenter.line' @api.one - def _ready_materials(self): + def _compute_is_material_ready(self): self.is_material_ready = True if self.product_line: - moves = self.env['stock.move'].search([('work_order', '=', - self.id)]) + moves = self.env['stock.move'].search( + [('work_order', '=', self.id)]) self.is_material_ready = not any( x not in ('assigned', 'cancel', 'done') for x in moves.mapped('state')) @@ -95,7 +84,7 @@ class MrpProductionWorkcenterLine(models.Model): move_lines = fields.One2many( comodel_name='stock.move', inverse_name='work_order', string='Moves') is_material_ready = fields.Boolean( - string='Materials Ready', compute="_ready_materials") + string='Materials Ready', compute="_compute_is_material_ready") possible_workcenters = fields.Many2many( comodel_name="mrp.workcenter", compute="_compute_possible_workcenters") workcenter_id = fields.Many2one( @@ -109,38 +98,25 @@ class MrpProductionWorkcenterLine(models.Model): def force_assign(self): self.move_lines.force_assign() - @api.multi - def _load_mo_date_planned(self, production, date_planned): - if date_planned < production.date_planned: - production.write({'date_planned': date_planned}) - return True - return False - @api.model def create(self, vals): - production_obj = self.env['mrp.production'] - dp = vals.get('date_planned', False) - production_id = vals.get('production_id', False) - if dp and production_id: - production = production_obj.browse(production_id) - self._load_mo_date_planned(production, dp) + if vals.get('date_planned') and vals.get('production_id'): + production = self.env['mrp.production'].browse( + vals['production_id']) + if vals['date_planned'] < production.date_planned: + production.date_planned = vals['date_planned'] return super(MrpProductionWorkcenterLine, self).create(vals) @api.multi - def write(self, vals, update=False): - if vals.get('date_planned', False): - dp = vals.get('date_planned') - update = self._load_mo_date_planned(self.production_id, dp) - res = super(MrpProductionWorkcenterLine, self).write(vals, - update=update) - return res - def check_minor_sequence_operations(self): + self.ensure_one() seq = self.sequence + res = True for operation in self.production_id.workcenter_lines: if operation.sequence < seq and operation.state != 'done': - return False - return True + res = False + break + return res def check_operation_moves_state(self, states): for move_line in self.move_lines: @@ -149,13 +125,14 @@ class MrpProductionWorkcenterLine(models.Model): return True def action_start_working(self): - if self.routing_wc_line.previous_operations_finished and \ - not self.check_minor_sequence_operations(): - raise exceptions.Warning(_("Previous operations not finished")) - if not self.check_operation_moves_state(['assigned', 'cancel', - 'done']): - raise exceptions.Warning( - _("Missing materials to start the production")) - if self.production_id.state in ('confirmed', 'ready'): - self.production_id.state = 'in_production' + for workorder in self: + if workorder.routing_wc_line.previous_operations_finished and \ + not workorder.check_minor_sequence_operations(): + raise exceptions.Warning(_("Previous operations not finished")) + if not workorder.check_operation_moves_state( + ['assigned', 'cancel', 'done']): + raise exceptions.Warning( + _("Missing materials to start the production")) + if workorder.production_id.state in ('confirmed', 'ready'): + workorder.production_id.state = 'in_production' return super(MrpProductionWorkcenterLine, self).action_start_working() diff --git a/mrp_operations_extension/models/mrp_routing.py b/mrp_operations_extension/models/mrp_routing.py index 0b5ea28ff..07f359d1d 100644 --- a/mrp_operations_extension/models/mrp_routing.py +++ b/mrp_operations_extension/models/mrp_routing.py @@ -12,25 +12,15 @@ class MrpRouting(models.Model): @api.one @api.constrains('workcenter_lines') def _check_produce_operation(self): - if not self.workcenter_lines: - return - num_produce = sum([x.do_production for x in self.workcenter_lines]) - if num_produce != 1: + num_produce = len(self.workcenter_lines.filtered('do_production')) + if num_produce != 1 and self.workcenter_lines: raise Warning(_("There must be one and only one operation with " "'Produce here' check marked.")) - previous_operations_finished = fields.Boolean( - string='Previous operations finished') - class MrpRoutingWorkcenter(models.Model): _inherit = 'mrp.routing.workcenter' - def default_previous_operations_finished(self): - if self.routing_id: - self.previous_operations_finished = \ - self.routing_id.previous_operations_finished - operation = fields.Many2one('mrp.routing.operation', string='Operation') op_wc_lines = fields.One2many( 'mrp.operation.workcenter', 'routing_workcenter', @@ -41,17 +31,14 @@ class MrpRoutingWorkcenter(models.Model): "products will be done in this operation. There can be only one " "operation per route with this check marked.") previous_operations_finished = fields.Boolean( - string='Previous operations finished', - default=default_previous_operations_finished) + string='Previous operations finished') picking_type_id = fields.Many2one('stock.picking.type', 'Picking Type', domain=[('code', '=', 'outgoing')]) @api.constrains('op_wc_lines') def _check_default_op_wc_lines(self): - if not self.op_wc_lines: - return - num_default = len([x for x in self.op_wc_lines if x.default]) - if num_default != 1: + num_default = len(self.op_wc_lines.filtered('default')) + if num_default != 1 and self.op_wc_lines: raise Warning( _('There must be one and only one line set as default.')) @@ -84,16 +71,11 @@ class MrpRoutingWorkcenter(models.Model): @api.one @api.onchange('op_wc_lines') def onchange_lines_default(self): - for line in self.op_wc_lines: - if line.default: - self.workcenter_id = line.workcenter - if line.custom_data: - self.cycle_nbr = line.capacity_per_cycle - self.hour_nbr = line.time_cycle - else: - self.cycle_nbr = line.workcenter.capacity_per_cycle - self.hour_nbr = line.workcenter.time_cycle - break + line = self.op_wc_lines.filtered('default')[:1] + self.workcenter_id = line.workcenter + data_source = line if line.custom_data else line.workcenter + self.cycle_nbr = data_source.capacity_per_cycle + self.hour_nbr = data_source.time_cycle class MrpOperationWorkcenter(models.Model): @@ -111,15 +93,15 @@ class MrpOperationWorkcenter(models.Model): 'mrp.routing.workcenter', 'Routing workcenter', required=True) time_efficiency = fields.Float('Efficiency factor') capacity_per_cycle = fields.Float('Capacity per cycle') - time_cycle = fields.Float('Time for 1 cycle (hours)', - help="Time in hours for doing one cycle.") - time_start = fields.Float('Time before prod.', - help="Time in hours for the setup.") - time_stop = fields.Float('Time after prod.', - help="Time in hours for the cleaning.") + time_cycle = fields.Float( + string='Time for 1 cycle (hours)', help="Duration for one cycle.") + time_start = fields.Float( + string='Time before prod.', help="Duration for the setup.") + time_stop = fields.Float( + string='Time after prod.', help="Duartion for the cleaning.") op_number = fields.Integer('# operators', default='0') op_avg_cost = fields.Float( - string='Operator avg. hour cost', + string='Operator average hourly cost', digits=dp.get_precision('Product Price')) default = fields.Boolean('Default') diff --git a/mrp_operations_extension/models/mrp_workcenter.py b/mrp_operations_extension/models/mrp_workcenter.py index 81a17fb68..5fa467eb4 100644 --- a/mrp_operations_extension/models/mrp_workcenter.py +++ b/mrp_operations_extension/models/mrp_workcenter.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# (c) 2015 Avanzosc +# (c) 2015 Pedro M. Baeza - Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + from openerp import models, fields, api from openerp.addons import decimal_precision as dp @@ -11,25 +12,26 @@ class MrpWorkcenter(models.Model): @api.one @api.onchange('operators') - def _operators_number_avg_cost(self): + def onchange_operators(self): self.op_number = len(self.operators) num_oper = 0 op_avg_cost = 0.0 - for ope in self.operators: - if (ope.employee_ids[0] and ope.employee_ids[0].product_id): + for op in self.operators: + if op.employee_ids[:1].product_id: num_oper += 1 - op_avg_cost += ope.employee_ids[0].product_id.standard_price + op_avg_cost += op.employee_ids[:1].product_id.standard_price self.op_avg_cost = op_avg_cost / (num_oper or 1) - pre_op_product = fields.Many2one('product.product', - string='Pre-operation costing product') - post_op_product = fields.Many2one('product.product', - string='Post-operation costing product') + pre_op_product = fields.Many2one( + 'product.product', string='Pre-operation costing product') + post_op_product = fields.Many2one( + 'product.product', string='Post-operation costing product') rt_operations = fields.Many2many( 'mrp.routing.operation', 'mrp_operation_workcenter_rel', 'workcenter', 'operation', 'Routing Operations') - operators = fields.Many2many('res.users', 'mrp_wc_operator_rel', - 'workcenter_id', 'operator_id', 'Operators') + operators = fields.Many2many( + 'res.users', 'mrp_wc_operator_rel', 'workcenter_id', 'operator_id', + string='Operators') op_number = fields.Integer(string='# Operators') - op_avg_cost = fields.Float(string='Operator average hour cost', + op_avg_cost = fields.Float(string='Operator average hourly cost', digits=dp.get_precision('Product Price')) diff --git a/mrp_operations_extension/models/res_config.py b/mrp_operations_extension/models/res_config.py index d1fe450e0..6433919f1 100644 --- a/mrp_operations_extension/models/res_config.py +++ b/mrp_operations_extension/models/res_config.py @@ -2,12 +2,12 @@ ############################################################################## # For copyright and license notices, see __openerp__.py file in root directory ############################################################################## -from openerp import models, fields +from openerp import fields, models class MrpConfigSettings(models.TransientModel): _inherit = 'mrp.config.settings' group_mrp_workers = fields.Boolean( - string='Manage operators in work centers ', + string='Manage operators in work centers', implied_group='mrp_operations_extension.group_mrp_workers') diff --git a/mrp_operations_extension/static/description/icon.png b/mrp_operations_extension/static/description/icon.png new file mode 100644 index 000000000..2606127c7 Binary files /dev/null and b/mrp_operations_extension/static/description/icon.png differ diff --git a/mrp_operations_extension/static/description/icon.svg b/mrp_operations_extension/static/description/icon.svg new file mode 100644 index 000000000..f950a7e09 --- /dev/null +++ b/mrp_operations_extension/static/description/icon.svg @@ -0,0 +1,151 @@ + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/mrp_operations_extension/tests/test_mrp_operations_extension.py b/mrp_operations_extension/tests/test_mrp_operations_extension.py index 2e2096ad1..f18e4f472 100644 --- a/mrp_operations_extension/tests/test_mrp_operations_extension.py +++ b/mrp_operations_extension/tests/test_mrp_operations_extension.py @@ -1,30 +1,169 @@ # -*- coding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## +# © 2015 Avanzosc +# © 2015 Pedro M. Baeza +# © 2015 Antiun Ingeniería +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + import openerp.tests.common as common -from openerp import workflow +from openerp.exceptions import ValidationError, Warning as UserError class TestMrpOperationsExtension(common.TransactionCase): - def setUp(self): super(TestMrpOperationsExtension, self).setUp() + self.workcenter = self.env['mrp.workcenter'].create( + {'name': 'Test work center', + 'op_number': 1, + 'capacity_per_cycle': 5, + 'time_cycle': 1.0} + ) + self.operation = self.env['mrp.routing.operation'].create( + {'name': 'Test operation', + 'op_number': 2, + 'workcenters': [(6, 0, self.workcenter.ids)]}) + self.routing = self.env['mrp.routing'].create( + {'name': 'Test routing'}) + self.routing_workcenter = self.env['mrp.routing.workcenter'].create( + { + 'name': 'Test routing line', + 'do_production': True, + 'workcenter_id': self.workcenter.id, + 'routing_id': self.routing.id, + }) + self.operation_workcenter = ( + self.env['mrp.operation.workcenter'].create( + { + 'workcenter': self.workcenter.id, + 'routing_workcenter': self.routing_workcenter.id, + 'default': True, + })) self.production_model = self.env['mrp.production'] self.work_order_produce_model = self.env['mrp.work.order.produce'] self.produce_line_model = self.env['mrp.product.produce.line'] - self.production = self.production_model.browse( - self.env.ref('mrp_operations_extension.mrp_production_opeext').id) - self.production_case1 = self.production.copy() + self.production = self.env.ref( + 'mrp_operations_extension.mrp_production_opeext') + + def test_check_do_production_mrp_routing(self): + # None of the workcenter lines have the check marked + with self.assertRaises(ValidationError): + self.routing.workcenter_lines = [ + (1, self.routing_workcenter.id, {'do_production': False})] + + def test_check_do_production_mrp_routing_2(self): + # Two workcenter lines have the check marked + with self.assertRaises(ValidationError): + self.routing.workcenter_lines = [(0, 0, { + 'name': 'Other test routing line', + 'do_production': True, + 'workcenter_id': self.workcenter.id, + })] + + def test_check_default_mrp_routing_workcenter(self): + # None of the operation workcenters have the check marked + with self.assertRaises(ValidationError): + self.routing_workcenter.op_wc_lines = [ + (1, self.operation_workcenter.id, {'default': False})] + + def test_check_default_mrp_routing_workcenter_2(self): + # Two operation workcenters have the check marked + with self.assertRaises(ValidationError): + self.routing_workcenter.op_wc_lines = [(0, 0, { + 'default': True, + 'routing_workcenter': self.routing_workcenter.id, + 'workcenter': self.workcenter.id, + })] + + def test_onchange_operation_mrp_routing_workcenter(self): + with self.env.do_in_onchange(): + record = self.env['mrp.routing.workcenter'].new() + record.operation = self.operation.id + record.onchange_operation() + self.assertEqual(record.name, self.operation.name) + self.assertEqual(len(record.op_wc_lines), 1) + self.assertEqual(record.op_wc_lines[0].op_number, 2) + + def test_onchange_workcenter_mrp_operation_workcenter(self): + with self.env.do_in_onchange(): + record = self.env['mrp.operation.workcenter'].new() + record.workcenter = self.workcenter.id + record.onchange_workcenter() + self.assertEqual( + record.capacity_per_cycle, self.workcenter.capacity_per_cycle) + self.assertEqual( + record.time_efficiency, self.workcenter.time_efficiency) + self.assertEqual(record.op_number, self.workcenter.op_number) + + def test_onchange_operators_mrp_workcenter(self): + user = self.env['res.users'].create({ + 'name': 'Test user', + 'login': 'test', + }) + cost_product = self.env['product.product'].create( + {'name': 'Cost product', + 'standard_price': 15.0}) + self.env['hr.employee'].create({ + 'name': 'Test employee', + 'user_id': user.id, + 'product_id': cost_product.id}) + with self.env.do_in_onchange(): + record = self.env['mrp.workcenter'].new() + record.operators = [(6, 0, user.ids)] + record.onchange_operators() + self.assertEqual(record.op_number, 1) + self.assertEqual(record.op_avg_cost, 15.0) + + def test_onchange_routing_mrp_bom(self): + with self.env.do_in_onchange(): + record = self.env['mrp.bom'].new() + record.bom_line_ids = self.env['mrp.bom.line'].new() + record.routing_id = self.routing.id + res = record.onchange_routing_id() + self.assertTrue(res['warning']) + + def test_start_workorder_without_material(self): + self.production.signal_workflow('button_confirm') + workorder = self.production.workcenter_lines[0] + with self.assertRaises(UserError): + # Missing materials to start the production + workorder.action_start_working() + # In this case it should work + workorder.action_assign() + workorder.force_assign() + workorder.action_start_working() + + def test_start_second_workorder(self): + self.production.signal_workflow('button_confirm') + self.production.workcenter_lines[1].routing_wc_line.\ + previous_operations_finished = True + with self.assertRaises(UserError): + # Previous operations not finished + self.production.workcenter_lines[1].action_start_working() + + def test_create_workcenter_minor_date(self): + date_planned = '1900-01-01 00:00:00' + self.env['mrp.production.workcenter.line'].create( + {'name': 'Test', + 'date_planned': date_planned, + 'workcenter_id': self.workcenter.id, + 'production_id': self.production.id}) + self.assertEqual(self.production.date_planned, date_planned) + + def test_confirm_production_without_do_production_flag(self): + self.production.action_compute() + self.production.workcenter_lines.write({'do_production': False}) + with self.assertRaises(UserError): + # At least one work order must have checked 'Produce here' + self.production.action_confirm() def test_confirm_production_operation_extension_case1(self): - workflow.trg_validate( - self.uid, 'mrp.production', self.production_case1.id, - 'button_confirm', self.cr) - self.production_case1.force_production() - for line in self.production_case1.workcenter_lines: - workflow.trg_validate(self.uid, 'mrp.production.workcenter.line', - line.id, 'button_start_working', self.cr) + self.production.signal_workflow('button_confirm') + self.assertFalse(self.production.workcenter_lines[0].is_material_ready) + self.production.force_production() + for line in self.production.workcenter_lines: + self.assertTrue(line.is_material_ready) + self.assertEqual(len(line.possible_workcenters), + len(line.routing_wc_line.op_wc_lines)) + line.signal_workflow('button_start_working') self.assertEqual( line.state, 'startworking', 'Error work center line not in start working state') @@ -44,9 +183,7 @@ class TestMrpOperationsExtension(common.TransactionCase): consume.with_context(active_id=line.id).do_consume() else: consume.with_context(active_id=line.id).do_consume_produce() - workflow.trg_validate( - self.uid, 'mrp.production.workcenter.line', line.id, - 'button_done', self.cr) + line.signal_workflow('button_done') self.assertEqual( line.state, 'done', 'Error work center line not in done state') diff --git a/mrp_operations_extension/views/mrp_production_view.xml b/mrp_operations_extension/views/mrp_production_view.xml index 011b30852..8404851c6 100644 --- a/mrp_operations_extension/views/mrp_production_view.xml +++ b/mrp_operations_extension/views/mrp_production_view.xml @@ -80,7 +80,7 @@ - + diff --git a/mrp_operations_extension/views/mrp_routing_view.xml b/mrp_operations_extension/views/mrp_routing_view.xml index 2b87918e3..873cd94ea 100644 --- a/mrp_operations_extension/views/mrp_routing_view.xml +++ b/mrp_operations_extension/views/mrp_routing_view.xml @@ -5,17 +5,6 @@ {'readonly_by_pass': True} - - mrp.routing.form - mrp.routing - - - - - - - - mrp.routing.workcenter.tree.inh mrp.routing.workcenter diff --git a/mrp_operations_extension/wizard/mrp_work_order_produce.py b/mrp_operations_extension/wizard/mrp_work_order_produce.py index ed3b5ff2d..1615f8347 100644 --- a/mrp_operations_extension/wizard/mrp_work_order_produce.py +++ b/mrp_operations_extension/wizard/mrp_work_order_produce.py @@ -31,17 +31,6 @@ class MrpWorkOrderProduce(models.TransientModel): prod = self._get_product_id() return prod and (prod.track_all or prod.track_production) or False - def do_produce(self, cr, uid, ids, context=None): - work_line = self.pool['mrp.production.workcenter.line'].browse( - cr, uid, context.get("active_id"), context=context) - production_id = work_line.production_id.id - assert production_id - data = self.browse(cr, uid, ids[0], context=context) - self.pool['mrp.production'].action_produce( - cr, uid, production_id, data.product_qty, - data.mode, data, context=context) - return {} - def do_consume(self, cr, uid, ids, context=None): work_line = self.pool['mrp.production.workcenter.line'].browse( cr, uid, context.get("active_id"), context=context) @@ -87,7 +76,7 @@ class MrpWorkOrderProduce(models.TransientModel): new_consume_lines.append([0, False, consume]) return {'value': {'consume_lines': new_consume_lines}} - def _get_product_qty(self): + def _default_product_qty(self): """ To obtain product quantity @param self: The object pointer. @param cr: A database cursor @@ -98,18 +87,16 @@ class MrpWorkOrderProduce(models.TransientModel): work_line = self.env['mrp.production.workcenter.line'].browse( self.env.context.get("active_id")) prod = work_line.production_id - done = 0.0 - for move in prod.move_created_ids2: - if move.product_id == prod.product_id: - if not move.scrapped: - done += move.product_qty + moves = prod.move_created_ids2.filtered( + lambda x: x.product_id == prod.product_id and not x.scrapped) + done = sum(moves.mapped('product_qty')) return (prod.product_qty - done) or prod.product_qty - product_id = fields.Many2one('product.product', - string='Product', default=_get_product_id) - product_qty = fields.Float('Select Quantity', - digits=(12, 6), required=True, - default=_get_product_qty) + product_id = fields.Many2one( + 'product.product', string='Product', default=_get_product_id) + product_qty = fields.Float( + string='Select Quantity', digits=(12, 6), required=True, + default=_default_product_qty) mode = fields.Selection([('consume_produce', 'Consume & Produce'), ('consume', 'Consume Only')], string='Mode', required=True,