diff --git a/mrp_production_raw_material_procurement_group/README.rst b/mrp_production_raw_material_procurement_group/README.rst new file mode 100644 index 000000000..a54288624 --- /dev/null +++ b/mrp_production_raw_material_procurement_group/README.rst @@ -0,0 +1,77 @@ +.. 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 + +=============================================== +Create Procurement Group On Manufacturing Order +=============================================== + +This module adds ability to group manufacturing order's raw material moves, and +other moves created by it's pull flow, by given procurement group. + +Installation +============ + +To install this module, you need to: + +1. Clone the branch 9.0 of the repository https://github.com/OCA/manufacture +2. Add the path to this repository in your configuration (addons-path) +3. Update the module list +4. Go to menu *Setting -> Modules -> Local Modules* +5. Search For *Create Procurement Group On Manufacturing Order* +6. Install the module + + +Usage +===== + +To use this module, you need to: + +1. Go to menu *Manufacturing -> Manufacturing -> Manufacturing Orders* +2. Create manufacturing order. Do not forget to fill *Raw Material Procurement Group* +3. When MO is confirmed. Raw material moves with *make to order* procurement method, and it's + other moves created by it's pull flow, will be grouped based on *Raw Material Procurement Group* + given + + +.. 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 + + +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 +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Andhitia Rama +* Lois Rilo + +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_raw_material_procurement_group/__init__.py b/mrp_production_raw_material_procurement_group/__init__.py new file mode 100644 index 000000000..dfd08accc --- /dev/null +++ b/mrp_production_raw_material_procurement_group/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/mrp_production_raw_material_procurement_group/__openerp__.py b/mrp_production_raw_material_procurement_group/__openerp__.py new file mode 100644 index 000000000..a993be2e9 --- /dev/null +++ b/mrp_production_raw_material_procurement_group/__openerp__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Create Procurement Group On Manufacturing Order", + "version": "9.0.1.0.0", + "category": "Manufacturing", + "website": "https://opensynergy-indonesia.com/", + "author": "OpenSynergy Indonesia, Eficent, Odoo Community Association (" + "OCA)", + "license": "AGPL-3", + "installable": True, + "depends": [ + "mrp", + ], + "data": [ + "views/mrp_production_views.xml", + ], +} diff --git a/mrp_production_raw_material_procurement_group/models/__init__.py b/mrp_production_raw_material_procurement_group/models/__init__.py new file mode 100644 index 000000000..3b25f06f2 --- /dev/null +++ b/mrp_production_raw_material_procurement_group/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import mrp_production diff --git a/mrp_production_raw_material_procurement_group/models/mrp_production.py b/mrp_production_raw_material_procurement_group/models/mrp_production.py new file mode 100644 index 000000000..85fd68ab0 --- /dev/null +++ b/mrp_production_raw_material_procurement_group/models/mrp_production.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# Copyright 2016 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + raw_material_procurement_group_id = fields.Many2one( + string="Raw Material Procurement Group", + comodel_name="procurement.group", + readonly=True, + copy=False, + states={ + "draft": [("readonly", False)], + }, + ) + auto_create_procurement_group = fields.Boolean( + string="Auto Create Procurement Group", + readonly=True, + states={ + "draft": [("readonly", False)], + }, + ) + + @api.multi + def action_confirm(self): + for mo in self: + mo._create_procurement_group() + return super(MrpProduction, self).action_confirm() + + @api.multi + def _prepare_procurement_group(self): + return {"name": self.name} + + @api.multi + def _create_procurement_group(self): + self.ensure_one() + obj_group = self.env[ + "procurement.group"] + if self.auto_create_procurement_group and \ + not self.raw_material_procurement_group_id: + self.raw_material_procurement_group_id = obj_group.create( + self._prepare_procurement_group()) + + @api.model + def _make_consume_line_from_data( + self, + production, product, uom_id, qty): + + move_id = super(MrpProduction, self)._make_consume_line_from_data( + production, product, uom_id, qty) + obj_move = self.env["stock.move"] + move = obj_move.browse(move_id) + if move.procure_method == "make_to_stock": + return move_id + if not production.raw_material_procurement_group_id: + return move_id + move.write( + {"group_id": production.raw_material_procurement_group_id.id}) + return move_id + + @api.model + def _create_previous_move(self, move_id, product, source_location_id, + dest_location_id): + move_id2 = super(MrpProduction, self)._create_previous_move( + move_id, product, source_location_id, dest_location_id) + move1 = self.env['stock.move'].browse(move_id) + move2 = self.env['stock.move'].browse(move_id2) + if move1.raw_material_production_id.raw_material_procurement_group_id: + move2.group_id = \ + move1.raw_material_production_id.\ + raw_material_procurement_group_id + return move2.id diff --git a/mrp_production_raw_material_procurement_group/static/description/icon.png b/mrp_production_raw_material_procurement_group/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/mrp_production_raw_material_procurement_group/static/description/icon.png differ diff --git a/mrp_production_raw_material_procurement_group/tests/__init__.py b/mrp_production_raw_material_procurement_group/tests/__init__.py new file mode 100644 index 000000000..0d87d0677 --- /dev/null +++ b/mrp_production_raw_material_procurement_group/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_mrp_production diff --git a/mrp_production_raw_material_procurement_group/tests/test_mrp_production.py b/mrp_production_raw_material_procurement_group/tests/test_mrp_production.py new file mode 100644 index 000000000..7c1a804dd --- /dev/null +++ b/mrp_production_raw_material_procurement_group/tests/test_mrp_production.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests.common import TransactionCase + + +class MrpProductionCase(TransactionCase): + + def setUp(self, *args, **kwargs): + super(MrpProductionCase, self).setUp(*args, **kwargs) + + self.obj_group = self.env["procurement.group"] + self.obj_mo = self.env["mrp.production"] + + self.warehouse1 = self.env.ref("stock.warehouse0") + self.warehouse2 = self.env["stock.warehouse"].create({ + "name": "X Warehouse", + "code": "X WH", + "reception_steps": "one_step", + "delivery_steps": "ship_only", + "resupply_from_wh": False, + "default_resupply_wh_id": False, + }) + self.loc_stock_2 = self.warehouse2.lot_stock_id + self.loc_production = self.env.ref( + "stock.location_production") + self.route = self.env["stock.location.route"].create({ + "name": "X Route", + "sequence": 1, + "product_selectable": True, + }) + self.rule = self.env["procurement.rule"].create({ + "name": "X Production", + "location_id": self.loc_production.id, + "location_src_id": self.loc_stock_2.id, + "procure_method": "make_to_order", + "picking_type_id": self.warehouse2.int_type_id.id, + "action": "move", + "sequence": 1, + "route_id": self.route.id, + }) + self.product1 = self.env.ref("product.product_product_18") + self.product_raw1 = self.env.ref("product.product_product_17") + self.product_raw1.write({ + "route_ids": [(4, self.route.id)], + }) + self.bom1 = self.env.ref("mrp.mrp_bom_3") + self.bom1.write({ + "routing_id": False}) + + def _create_procurement_group( + self, name): + res = { + "name": name, + "move_type": "direct" + } + return self.obj_group.create(res) + + def _create_mo( + self, + product=False, + bom=False, + src_loc=False, + dest_loc=False, + group=False, + qty=10.0, + auto=False): + if not product: + product = self.product1 + uom = product.uom_id + if not bom: + bom = self.bom1 + if not src_loc: + src_loc = self.loc_stock_2 + if not dest_loc: + dest_loc = self.loc_stock_2 + res = { + "product_id": product.id, + "bom_id": bom.id, + "location_src_id": src_loc.id, + "location_dest_id": dest_loc.id, + "raw_material_procurement_group_id": group, + "product_qty": qty, + "product_uom": uom.id, + "auto_create_procurement_group": auto, + } + return self.obj_mo.create(res) + + def test_no_create_group(self): + # Create MO + mo = self._create_mo() + # Click confirm button + mo.signal_workflow("button_confirm") + for raw in mo.move_lines: + self.assertEqual( + raw.location_id, + self.loc_stock_2) + self.assertEqual( + raw.location_dest_id, + self.loc_production) + self.assertEqual( + raw.procure_method, + "make_to_order") + self.assertEqual( + len(raw.group_id), + 0) + + def test_create_group_manual(self): + group1 = self._create_procurement_group( + "X 001") + # Create MO + mo = self._create_mo( + group=group1.id) + # Click confirm button + mo.signal_workflow("button_confirm") + for raw in mo.move_lines: + self.assertEqual( + raw.location_id, + self.loc_stock_2) + self.assertEqual( + raw.location_dest_id, + self.loc_production) + self.assertEqual( + raw.procure_method, + "make_to_order") + self.assertEqual( + raw.group_id, + group1) + + def test_create_group_auto(self): + mo = self._create_mo( + group=False, + auto=True) + # Click confirm button + mo.signal_workflow("button_confirm") + for raw in mo.move_lines: + self.assertEqual( + raw.location_id, + self.loc_stock_2) + self.assertEqual( + raw.location_dest_id, + self.loc_production) + self.assertEqual( + raw.procure_method, + "make_to_order") + self.assertEqual( + raw.group_id, + mo.raw_material_procurement_group_id) + + def test_copy_mo(self): + # Create MO + group1 = self._create_procurement_group( + "X 001") + mo = self._create_mo( + group=group1.id) + mo2 = mo.copy() + + self.assertNotEqual( + mo.raw_material_procurement_group_id, + mo2.raw_material_procurement_group_id) + + self.assertEqual( + mo2.raw_material_procurement_group_id.id, False) + + def test_group_name(self): + # Create MO + mo = self._create_mo(auto=True) + # Click confirm button + mo.signal_workflow("button_confirm") + + self.assertNotEqual( + mo.raw_material_procurement_group_id.id, False) + + self.assertEqual( + mo.raw_material_procurement_group_id.name, mo.name) + + def test_routing(self): + location = self.env['stock.location'].create({ + 'name': 'Shop floor', + }) + routing = self.env['mrp.routing'].create({ + 'name': 'test', + 'location_id': location.id, + }) + self.bom1.write({ + "routing_id": routing.id}) + # Create MO + mo = self._create_mo(auto=True) + mo2 = self._create_mo(auto=True) + # Click confirm button + mo.signal_workflow("button_confirm") + mo2.signal_workflow("button_confirm") + + # Check that the pickings are split by procurement group + pick = self.env['stock.picking'].search( + [('group_id', '=', mo.raw_material_procurement_group_id.id)]) + self.assertNotEqual(pick, False) + + pick2 = self.env['stock.picking'].search( + [('group_id', '=', mo2.raw_material_procurement_group_id.id)]) + self.assertNotEqual(pick2, False) diff --git a/mrp_production_raw_material_procurement_group/views/mrp_production_views.xml b/mrp_production_raw_material_procurement_group/views/mrp_production_views.xml new file mode 100644 index 000000000..c30b6d7af --- /dev/null +++ b/mrp_production_raw_material_procurement_group/views/mrp_production_views.xml @@ -0,0 +1,21 @@ + + + + + + + mrp.production form + mrp.production + + + + + + + + + + + +