diff --git a/stock_mts_mto_rule/model/procurement.py b/stock_mts_mto_rule/model/procurement.py index b128c645898b..cb563241f744 100644 --- a/stock_mts_mto_rule/model/procurement.py +++ b/stock_mts_mto_rule/model/procurement.py @@ -29,7 +29,8 @@ class ProcurementOrder(models.Model): def get_mto_qty_to_order(self): self.ensure_one() uom_obj = self.env['product.uom'] - proc_warehouse = self.with_context(warehouse=self.warehouse_id.id) + stock_location = self.warehouse_id.lot_stock_id.id + proc_warehouse = self.with_context(location=stock_location) virtual_available = proc_warehouse.product_id.virtual_available qty_available = uom_obj._compute_qty(self.product_id.uom_id.id, virtual_available, @@ -43,14 +44,33 @@ def get_mto_qty_to_order(self): @api.model def _get_mts_mto_procurement(self, proc, rule, qty, uos_qty): + origin = (proc.group_id and (proc.group_id.name + ":") or "") + \ + (proc.rule_id and proc.rule_id.name or proc.origin or "/") return { 'name': rule.name, - 'origin': proc.rule_id.name, + 'origin': origin, 'product_qty': qty, 'product_uos_qty': uos_qty, 'rule_id': rule.id, } + @api.model + def _check(self, procurement): + if procurement.rule_id and \ + procurement.rule_id.action == 'split_procurement': + if procurement.state == 'running': + return True + return super(ProcurementOrder, self)._check(procurement) + + @api.multi + def run(self, autocommit=False): + res = super(ProcurementOrder, self).run(autocommit=autocommit) + for proc in self: + if proc.rule_id and \ + proc.rule_id.action == 'split_procurement': + proc.check() + return res + @api.model def _run(self, procurement): if procurement.rule_id and \ diff --git a/stock_mts_mto_rule/model/warehouse.py b/stock_mts_mto_rule/model/warehouse.py index b2b0b0b74fc7..6f06ada0f238 100644 --- a/stock_mts_mto_rule/model/warehouse.py +++ b/stock_mts_mto_rule/model/warehouse.py @@ -27,28 +27,17 @@ class Warehouse(models.Model): _inherit = 'stock.warehouse' mto_mts_management = fields.Boolean( - 'Use MTO+MTS rules') + 'Use MTO+MTS rules', + help='If this new route is selected on product form view, a ' + 'purchase order will be created only if the virtual stock is ' + 'less than 0 else, the product will be taken from stocks') 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'] + pull_model = self.env['procurement.rule'] try: mts_mto_route = self.env.ref( 'stock_mts_mto_rule.route_mto_mts') @@ -60,55 +49,50 @@ def _get_mts_mto_rule(self, warehouse): raise exceptions.Warning(_( 'Can\'t find any generic MTS+MTO route.')) + if not warehouse.mto_pull_id: + raise exceptions.Warning(_( + 'Can\'t find MTO Rule on the warehouse')) + + mts_rules = pull_model.search( + [('location_src_id', '=', warehouse.lot_stock_id.id), + ('route_id', '=', warehouse.delivery_route_id.id)]) + if not mts_rules: + raise exceptions.Warning(_( + 'Can\'t find MTS Rule on the warehouse')) return { 'name': self._format_routename(warehouse, _('MTS+MTO')), 'route_id': mts_mto_route.id, 'action': 'split_procurement', + 'mto_rule_id': warehouse.mto_pull_id.id, + 'mts_rule_id': mts_rules[0].id, + 'warehouse_id': warehouse.id, + 'location_id': warehouse.mto_pull_id.location_id.id, + 'picking_type_id': warehouse.mto_pull_id.picking_type_id.id, } - @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): + pull_model = self.env['procurement.rule'] 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) + mts_mto_pull_vals = self._get_mts_mto_rule(warehouse) + mts_mto_pull = pull_model.create(mts_mto_pull_vals) + res['mts_mto_rule_id'] = mts_mto_pull.id return res @api.multi def write(self, vals): + pull_model = self.env['procurement.rule'] 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) + rule_vals = self._get_mts_mto_rule(warehouse) + mts_mto_pull = pull_model.create(rule_vals) + vals['mts_mto_rule_id'] = mts_mto_pull.id 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) @@ -117,7 +101,7 @@ 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 + warehouse.mts_mto_rule_id.route_id ): all_routes += [warehouse.mts_mto_rule_id.route_id.id] return all_routes @@ -127,14 +111,6 @@ 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) @@ -151,16 +127,11 @@ def change_route(self, warehouse, new_reception_step=False, 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) + warehouse.mts_mto_rule_id.location_id = ( + warehouse.mto_pull_id.location_id) + mts_rules = pull_model.search( + [('location_src_id', '=', warehouse.lot_stock_id.id), + ('route_id', '=', warehouse.delivery_route_id.id)]) + warehouse.mts_mto_rule_id.mts_rule_id = mts_rules[0].id return res diff --git a/stock_mts_mto_rule/tests/__init__.py b/stock_mts_mto_rule/tests/__init__.py new file mode 100644 index 000000000000..96aebab0da2f --- /dev/null +++ b/stock_mts_mto_rule/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import test_mto_mts_route diff --git a/stock_mts_mto_rule/tests/test_mto_mts_route.py b/stock_mts_mto_rule/tests/test_mto_mts_route.py new file mode 100644 index 000000000000..bc38de754416 --- /dev/null +++ b/stock_mts_mto_rule/tests/test_mto_mts_route.py @@ -0,0 +1,104 @@ +# Author: Florian da Costa +# Copyright 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 . +from openerp.tests.common import TransactionCase +from datetime import datetime + + +class TestMtoMtsRoute(TransactionCase): + + def test_standard_mto_route(self): + mto_route = self.env.ref('stock.route_warehouse0_mto') + self.product.route_ids = [(6, 0, [mto_route.id])] + self.procurement.run() + self.assertEqual(self.warehouse.mto_pull_id, + self.procurement.rule_id) + self.assertEqual('make_to_order', + self.procurement.move_ids[0].procure_method) + self.assertEqual(self.procurement.product_qty, + self.procurement.move_ids[0].product_uom_qty) + self.assertEqual('waiting', + self.procurement.move_ids[0].state) + + def test_standard_mts_route(self): + self.procurement.run() + self.assertEqual('make_to_stock', + self.procurement.move_ids[0].procure_method) + self.assertEqual(self.procurement.product_qty, + self.procurement.move_ids[0].product_uom_qty) + self.assertEqual('confirmed', + self.procurement.move_ids[0].state) + + def test_mts_mto_route_split(self): + mto_mts_route = self.env.ref('stock_mts_mto_rule.route_mto_mts') + self.product.route_ids = [(6, 0, [mto_mts_route.id])] + self.quant.qty = 1.0 + self.procurement.run() + moves = self.env['stock.move'].search( + [('group_id', '=', self.group.id)]) + self.assertEqual(2, len(moves)) + self.assertEqual(1.0, moves[0].product_uom_qty) + + def test_mts_mto_route_mts_only(self): + mto_mts_route = self.env.ref('stock_mts_mto_rule.route_mto_mts') + self.product.route_ids = [(6, 0, [mto_mts_route.id])] + self.quant.qty = 0.0 + self.procurement.run() + moves = self.env['stock.move'].search( + [('group_id', '=', self.group.id)]) + self.assertEqual(1, len(moves)) + self.assertEqual(2.0, moves[0].product_uom_qty) + self.assertEqual('make_to_order', + moves[0].procure_method) + + def test_mts_mto_route_mto_only(self): + mto_mts_route = self.env.ref('stock_mts_mto_rule.route_mto_mts') + self.product.route_ids = [(6, 0, [mto_mts_route.id])] + self.quant.qty = 3.0 + self.procurement.run() + moves = self.env['stock.move'].search( + [('group_id', '=', self.group.id)]) + self.assertEqual(1, len(moves)) + self.assertEqual(2.0, moves[0].product_uom_qty) + self.assertEqual('make_to_stock', + moves[0].procure_method) + + def setUp(self): + super(TestMtoMtsRoute, self).setUp() + self.warehouse = self.env.ref('stock.warehouse0') + self.warehouse.mto_mts_management = True + self.product = self.env.ref('product.product_product_4') + self.company_partner = self.env.ref('base.main_partner') + self.group = self.env['procurement.group'].create({ + 'name': 'test', + }) + self.procurement = self.env['procurement.order'].create({ + 'location_id': self.env.ref('stock.stock_location_customers').id, + 'product_id': self.product.id, + 'product_qty': 2.0, + 'product_uom': 1, + 'warehouse_id': self.warehouse.id, + 'priority': '1', + 'date_planned': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + 'name': self.product.name, + 'origin': 'test', + 'group_id': self.group.id, + }) + self.quant = self.env['stock.quant'].create({ + 'owner_id': self.company_partner.id, + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.product.id, + 'qty': 0.0, + })