From fe31fa5e582d4e5ecaf62f9199c92273c498565c Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Wed, 29 Apr 2015 17:36:21 +0200 Subject: [PATCH] refactore creating a new route, a new procurement action and separate procurement rules to handle the mts+mto scenario --- stock_dynamic_mto/__init__.py | 1 - stock_dynamic_mto/pull_rule_view.xml | 18 -- stock_mts_mto_rule/README.rst | 62 +++++++ stock_mts_mto_rule/__init__.py | 1 + stock_mts_mto_rule/__openerp__.py | 38 ++++ stock_mts_mto_rule/data/stock_data.xml | 17 ++ stock_mts_mto_rule/model/__init__.py | 5 + .../model}/procurement.py | 75 ++++---- .../model/rule.py | 38 ++-- stock_mts_mto_rule/model/warehouse.py | 166 ++++++++++++++++++ stock_mts_mto_rule/view/pull_rule.xml | 21 +++ stock_mts_mto_rule/view/warehouse.xml | 17 ++ 12 files changed, 386 insertions(+), 73 deletions(-) delete mode 100644 stock_dynamic_mto/__init__.py delete mode 100644 stock_dynamic_mto/pull_rule_view.xml create mode 100644 stock_mts_mto_rule/README.rst create mode 100644 stock_mts_mto_rule/__init__.py create mode 100644 stock_mts_mto_rule/__openerp__.py create mode 100644 stock_mts_mto_rule/data/stock_data.xml create mode 100644 stock_mts_mto_rule/model/__init__.py rename {stock_dynamic_mto => stock_mts_mto_rule/model}/procurement.py (52%) rename stock_dynamic_mto/__openerp__.py => stock_mts_mto_rule/model/rule.py (64%) create mode 100644 stock_mts_mto_rule/model/warehouse.py create mode 100644 stock_mts_mto_rule/view/pull_rule.xml create mode 100644 stock_mts_mto_rule/view/warehouse.xml diff --git a/stock_dynamic_mto/__init__.py b/stock_dynamic_mto/__init__.py deleted file mode 100644 index 2cac6b055942..000000000000 --- a/stock_dynamic_mto/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import procurement diff --git a/stock_dynamic_mto/pull_rule_view.xml b/stock_dynamic_mto/pull_rule_view.xml deleted file mode 100644 index 43f252a93d5c..000000000000 --- a/stock_dynamic_mto/pull_rule_view.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - procurement.rule.dynamic.mto - procurement.rule - - - - - - - - - - diff --git a/stock_mts_mto_rule/README.rst b/stock_mts_mto_rule/README.rst new file mode 100644 index 000000000000..a3049f0d81d5 --- /dev/null +++ b/stock_mts_mto_rule/README.rst @@ -0,0 +1,62 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Stock MTS+MTO Rule +================== + +This module add a Make To Stock + Make to Order Route. + +If you choose the make to stock + make to order rule instead of the make to +order route, the creation of a purchase order will depend on the virtual stock. +There are 3 cases : + +1. The virtual stock of the product is 0 + => It will act exactly like the make to order route. + +2. The virtual stock is equal to the quantity ordered + => It will act exactly like a make to stock route + +3. The virtual stock is more than 0 but less than ordered quantity + => On part of the products will be taken from stock and a purchase order + will be created for the rest. So it will act like both make to order and + make to stock rule. + +Example : +We have a virtual stock of : 1 product A +A sale Order is made for 3 products A. +2 Procurements will be created : + +1. 1 with a make to stock rule and a quantity of 1 + +2. 1 with a make to order rule and a quantity of 2. + +After validation, a purchase order with 2 products will be created. + +Usage +===== + +You have to choose the mts+mto route on the product form. +You should not choose both mts+mto route and mto route. + +Credits +======= + +Contributors +------------ + +* Florian da Costa + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://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 http://odoo-community.org. diff --git a/stock_mts_mto_rule/__init__.py b/stock_mts_mto_rule/__init__.py new file mode 100644 index 000000000000..9186ee3ad24d --- /dev/null +++ b/stock_mts_mto_rule/__init__.py @@ -0,0 +1 @@ +from . import model diff --git a/stock_mts_mto_rule/__openerp__.py b/stock_mts_mto_rule/__openerp__.py new file mode 100644 index 000000000000..ea19cac08973 --- /dev/null +++ b/stock_mts_mto_rule/__openerp__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 Akretion (). +# +# 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 . +# +############################################################################## + +{'name': 'Stock MTS+MTO Rule', + 'version': '1.0', + 'author': 'Akretion,Odoo Community Association (OCA)', + 'website': 'http://www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Warehouse', + 'summary': 'Add a MTS+MTO route', + 'depends': ['stock', + ], + 'demo': [], + 'data': ['data/stock_data.xml', + 'view/pull_rule.xml', + 'view/warehouse.xml', + ], + 'installable': True, + } diff --git a/stock_mts_mto_rule/data/stock_data.xml b/stock_mts_mto_rule/data/stock_data.xml new file mode 100644 index 000000000000..8297d5a839a5 --- /dev/null +++ b/stock_mts_mto_rule/data/stock_data.xml @@ -0,0 +1,17 @@ + + + + + + + + Make To Order + Make To Stock + 5 + + + + + + diff --git a/stock_mts_mto_rule/model/__init__.py b/stock_mts_mto_rule/model/__init__.py new file mode 100644 index 000000000000..cb1cbd4ab287 --- /dev/null +++ b/stock_mts_mto_rule/model/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import rule +from . import warehouse +from . import procurement diff --git a/stock_dynamic_mto/procurement.py b/stock_mts_mto_rule/model/procurement.py similarity index 52% rename from stock_dynamic_mto/procurement.py rename to stock_mts_mto_rule/model/procurement.py index aa9d4b1bb634..b128c645898b 100644 --- a/stock_dynamic_mto/procurement.py +++ b/stock_mts_mto_rule/model/procurement.py @@ -19,14 +19,7 @@ # along with this program. If not, see . # ############################################################################### -from openerp import models, fields, api - - -class ProcurementRule(models.Model): - _inherit = 'procurement.rule' - - mts_pull_rule_id = fields.Many2one('procurement.rule', - string="MTS Rule") +from openerp import models, api class ProcurementOrder(models.Model): @@ -48,33 +41,51 @@ def get_mto_qty_to_order(self): return self.product_qty - qty_available return self.product_qty + @api.model + def _get_mts_mto_procurement(self, proc, rule, qty, uos_qty): + return { + 'name': rule.name, + 'origin': proc.rule_id.name, + 'product_qty': qty, + 'product_uos_qty': uos_qty, + 'rule_id': rule.id, + } + @api.model def _run(self, procurement): - uom_obj = self.env['product.uom'] - rule_id = procurement.rule_id - rule_mts = rule_id and rule_id.mts_pull_rule_id or False - if rule_mts: + if procurement.rule_id and \ + procurement.rule_id.action == 'split_procurement': + uom_obj = self.env['product.uom'] needed_qty = procurement.get_mto_qty_to_order() + rule = procurement.rule_id if needed_qty == 0.0: - procurement.write({'rule_id': rule_mts.id}) - return super(ProcurementOrder, self)._run(procurement) + mts_vals = self._get_mts_mto_procurement( + procurement, rule.mts_rule_id, procurement.product_qty, + procurement.product_uos_qty) + mts_proc = procurement.copy(mts_vals) + mts_proc.run() + elif needed_qty == procurement.product_qty: + mto_vals = self._get_mts_mto_procurement( + procurement, rule.mto_rule_id, procurement.product_qty, + procurement.product_uos_qty) + mto_proc = procurement.copy(mto_vals) + mto_proc.run() else: - if needed_qty != procurement.product_qty: - mts_qty = procurement.product_qty - needed_qty - mts_uos_qty = uom_obj._compute_qty( - procurement.product_uom.id, - mts_qty, - procurement.product_uos.id) - default_vals = { - 'product_qty': mts_qty, - 'product_uos_qty': mts_uos_qty, - } - uos_qty = procurement.product_uos_qty - update_vals = { - 'product_qty': needed_qty, - 'product_uos_qty': uos_qty - mts_uos_qty, - } - mts_proc = procurement.copy(default=default_vals) - mts_proc.run() - procurement.write(update_vals) + mts_qty = procurement.product_qty - needed_qty + mts_uos_qty = uom_obj._compute_qty( + procurement.product_uom.id, + mts_qty, + procurement.product_uos.id) + mts_vals = self._get_mts_mto_procurement( + procurement, rule.mts_rule_id, mts_qty, mts_uos_qty) + mts_proc = procurement.copy(mts_vals) + mts_proc.run() + + uos_qty = procurement.product_uos_qty + mto_vals = self._get_mts_mto_procurement( + procurement, rule.mto_rule_id, needed_qty, + uos_qty - mts_uos_qty) + + mto_proc = procurement.copy(mto_vals) + mto_proc.run() return super(ProcurementOrder, self)._run(procurement) diff --git a/stock_dynamic_mto/__openerp__.py b/stock_mts_mto_rule/model/rule.py similarity index 64% rename from stock_dynamic_mto/__openerp__.py rename to stock_mts_mto_rule/model/rule.py index 7f6b1970edc9..481ffedce5de 100644 --- a/stock_dynamic_mto/__openerp__.py +++ b/stock_mts_mto_rule/model/rule.py @@ -19,25 +19,19 @@ # along with this program. If not, see . # ############################################################################### -{ - "name": "Stock Dynamic MTO", - "version": "1.0", - "category": "Stock", - "description": """ -Stock Dynamic MTO -====================== -The purpose of the module is to give the possibility to a pull rule -make to order to act like a make to stock rule depending of the virtual -stock of a product. -""", - "license": "AGPL-3", - "author": "Akretion,Odoo Community Association (OCA)", - "website": "http://www.akretion.com/", - "depends": [ - "stock", - ], - "data": [ - "pull_rule_view.xml", - ], - "installable": True, -} +from openerp import models, api, fields +from openerp.tools.translate import _ + + +class ProcurementRule(models.Model): + _inherit = 'procurement.rule' + + mts_rule_id = fields.Many2one('procurement.rule', + string="MTS Rule") + mto_rule_id = fields.Many2one('procurement.rule', + string="MTO Rule") + + @api.model + def _get_action(self): + return [('split_procurement', _('Choose between MTS and MTO'))] + \ + super(ProcurementRule, self)._get_action() diff --git a/stock_mts_mto_rule/model/warehouse.py b/stock_mts_mto_rule/model/warehouse.py new file mode 100644 index 000000000000..b2b0b0b74fc7 --- /dev/null +++ b/stock_mts_mto_rule/model/warehouse.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). All Rights Reserved +# @author Florian DA COSTA +# +# 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 . +# +############################################################################### +from openerp import models, api, fields, exceptions +from openerp.tools.translate import _ + + +class Warehouse(models.Model): + _inherit = 'stock.warehouse' + + mto_mts_management = fields.Boolean( + 'Use MTO+MTS rules') + mts_mto_rule_id = fields.Many2one('procurement.rule', + 'MTO+MTS rule') + + @api.model + def _get_mts_mto_mto_rule(self, warehouse): + return { + 'name': self._format_routename(warehouse, _('MTS+MTO : MTO')), + 'route_id': False, + } + + @api.model + def _get_mts_mto_mts_rule(self, warehouse): + return { + 'name': self._format_routename(warehouse, _('MTS+MTO : MTS')), + 'route_id': False, + 'procure_method': 'make_to_stock', + } + + @api.model + def _get_mts_mto_rule(self, warehouse): + route_model = self.env['stock.location.route'] + try: + mts_mto_route = self.env.ref( + 'stock_mts_mto_rule.route_mto_mts') + except: + mts_mto_route = route_model.search([ + ('name', 'like', 'Make To Order + Make To Stock') + ]) + if not mts_mto_route: + raise exceptions.Warning(_( + 'Can\'t find any generic MTS+MTO route.')) + + return { + 'name': self._format_routename(warehouse, _('MTS+MTO')), + 'route_id': mts_mto_route.id, + 'action': 'split_procurement', + } + + @api.model + def create_mts_mto_rules(self, warehouse): + model_rule = warehouse.mto_pull_id + mts_pull_vals = self._get_mts_mto_mts_rule(warehouse) + mts_pull = model_rule.copy(mts_pull_vals) + mto_pull_vals = self._get_mts_mto_mto_rule(warehouse) + mto_pull_vals['mts_pull_rule_id'] = mts_pull.id + mto_pull = model_rule.copy(mto_pull_vals) + + mts_mto_pull_vals = self._get_mts_mto_rule(warehouse) + mts_mto_pull_vals.update({'mts_rule_id': mts_pull.id, + 'mto_rule_id': mto_pull.id}) + mts_mto_pull = model_rule.copy(mts_mto_pull_vals) + + res = { + 'mts_rule_id': mts_pull.id, + 'mto_rule_id': mto_pull.id, + 'mts_mto_rule_id': mts_mto_pull.id, + } + return res + + @api.multi + def create_routes(self, warehouse): + res = super(Warehouse, self).create_routes(warehouse) + if warehouse.mto_mts_management: + vals = self.create_mts_mto_rules(warehouse) + res['mts_mto_rule_id'] = vals.get('mts_mto_rule_id', False) + return res + + @api.multi + def write(self, vals): + if 'mto_mts_management' in vals: + if vals.get("mto_mts_management"): + for warehouse in self: + if not warehouse.mts_mto_rule_id: + rule_vals = self.create_mts_mto_rules(warehouse) + vals['mts_mto_rule_id'] = rule_vals.get( + 'mts_mto_rule_id', False) + else: + for warehouse in self: + if warehouse.mts_mto_rule_id: + warehouse.mts_mto_rule_id.mts_rule_id.unlink() + warehouse.mts_mto_rule_id.mto_rule_id.unlink() + warehouse.mts_mto_rule_id.unlink() + return super(Warehouse, self).write(vals) + + @api.model + def get_all_routes_for_wh(self, warehouse): + all_routes = super(Warehouse, self).get_all_routes_for_wh(warehouse) + if ( + warehouse.mto_mts_management and + warehouse.mts_mto_mto_rule_id.route_id + ): + all_routes += [warehouse.mts_mto_rule_id.route_id.id] + return all_routes + + @api.model + def _handle_renaming(self, warehouse, name, code): + res = super(Warehouse, self)._handle_renaming(warehouse, name, code) + + if warehouse.mts_mto_rule_id: + warehouse.mts_mto_rule_id.mts_rule_id.name = ( + warehouse.mts_mto_rule_id.mts_rule_id.name.replace( + warehouse.name, name, 1) + ) + warehouse.mts_mto_rule_id.mto_rule_id.name = ( + warehouse.mts_mto_rule_id.mto_rule_id.name.replace( + warehouse.name, name, 1) + ) + warehouse.mts_mto_rule_id.name = ( + warehouse.mts_mto_rule_id.name.replace( + warehouse.name, name, 1) + ) + return res + + @api.multi + def change_route(self, warehouse, new_reception_step=False, + new_delivery_step=False): + res = super(Warehouse, self).change_route( + warehouse, + new_reception_step=new_reception_step, + new_delivery_step=new_delivery_step) + + mts_mto_rule_id = warehouse.mts_mto_rule_id + if new_delivery_step and mts_mto_rule_id: + model_rule = warehouse.mto_pull_id + rule_ids = [ + mts_mto_rule_id.id, + mts_mto_rule_id.mts_rule_id.id, + mts_mto_rule_id.mto_rule_id.id + ] + pull_model = self.env['procurement.rule'] + vals = { + 'location_id': model_rule.location_id.id, + 'location_src_id': model_rule.location_src_id.id, + } + pull_model.write(rule_ids, vals) + return res diff --git a/stock_mts_mto_rule/view/pull_rule.xml b/stock_mts_mto_rule/view/pull_rule.xml new file mode 100644 index 000000000000..e21a59ff854a --- /dev/null +++ b/stock_mts_mto_rule/view/pull_rule.xml @@ -0,0 +1,21 @@ + + + + + procurement.rule.mts.mto + procurement.rule + + + + + + + + + + + diff --git a/stock_mts_mto_rule/view/warehouse.xml b/stock_mts_mto_rule/view/warehouse.xml new file mode 100644 index 000000000000..5dc891581adf --- /dev/null +++ b/stock_mts_mto_rule/view/warehouse.xml @@ -0,0 +1,17 @@ + + + + + + view_warehouse_inherited + stock.warehouse + + + + + + + + + +