diff --git a/mrp_mto_with_stock/__manifest__.py b/mrp_mto_with_stock/__manifest__.py index fb12f2dff..23cb051fc 100644 --- a/mrp_mto_with_stock/__manifest__.py +++ b/mrp_mto_with_stock/__manifest__.py @@ -9,11 +9,15 @@ "author": "John Walsh, Eficent, Odoo Community Association (OCA)", "website": "https://odoo-community.org/", "category": "Manufacturing", - "version": "11.0.1.0.0", + "version": "11.0.2.0.0", "license": "AGPL-3", "application": False, "installable": True, - "depends": ["stock", "sale", "purchase", "mrp"], + "depends": [ + "stock", + "mrp", + "stock_available_unreserved", + ], "data": [ 'views/product_template_view.xml', 'views/stock_warehouse.xml', diff --git a/mrp_mto_with_stock/models/mrp_production.py b/mrp_mto_with_stock/models/mrp_production.py index 118f3ca2a..73ad9012b 100644 --- a/mrp_mto_with_stock/models/mrp_production.py +++ b/mrp_mto_with_stock/models/mrp_production.py @@ -1,20 +1,19 @@ -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # Copyright 2015 John Walsh # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, models from odoo.exceptions import UserError import copy -import logging -_logger = logging.getLogger(__name__) class MrpProduction(models.Model): _inherit = 'mrp.production' - def _get_procurement_group_data(self, move): - return {'partner_id': move.partner_id.id, - 'name': '{0}:{1}'.format(self.name, move.product_id.name)} + def _mto_with_stock_condition(self, move): + """Extensibility-enhancer method for modifying the scenarios when + MTO/MTS method should apply.""" + return move.location_id in move.product_id.mrp_mts_mto_location_ids @api.multi def action_assign(self): @@ -34,28 +33,9 @@ class MrpProduction(models.Model): new_move = False qty_to_procure = 0.0 if move.state in ('partially_available', 'confirmed') \ - and move.location_id in \ - move.product_id.mrp_mts_mto_location_ids \ - and not mto_with_no_move_dest_id: - # We have to split the move because we can't have - # a part of the move that have ancestors and not the - # other else it won't ever be reserved. - qty_to_procure = ( - move.product_uom_qty - move.reserved_availability) - if qty_to_procure < move.product_uom_qty: - move._do_unreserve() - new_move_id = move._split( - qty_to_procure, - restrict_partner_id=move.restrict_partner_id) - new_move = move_obj.browse(new_move_id) - move._action_assign() - else: - new_move = move - elif move.state in ('partially_available', 'confirmed') \ and move.procure_method == 'make_to_stock' \ and mto_with_no_move_dest_id and \ - move.location_id in \ - move.product_id.mrp_mts_mto_location_ids: + self._mto_with_stock_condition(move): qty_to_procure = production.get_mto_qty_to_procure(move) if qty_to_procure > 0.0: new_move = move @@ -66,6 +46,55 @@ class MrpProduction(models.Model): mto_with_no_move_dest_id) return res + @api.multi + def _adjust_procure_method(self): + """When configured as MTO/MTS manufacturing location, if there is + stock available unreserved, use it and procure the remaining.""" + res = super()._adjust_procure_method() + warehouse = self.location_src_id.get_warehouse() + mto_with_no_move_dest_id = warehouse.mrp_mto_mts_forecast_qty + for move in self.move_raw_ids: + if not self._mto_with_stock_condition(move): + continue + new_move = False + qty_to_procure = 0.0 + if not mto_with_no_move_dest_id: + # We have to split the move because we can't have + # a part of the move that have ancestors and not the + # other else it won't ever be reserved. + qty_to_procure = min( + move.product_uom_qty - + move.product_id.qty_available_not_res, + move.product_uom_qty) + if 0.0 < qty_to_procure < move.product_uom_qty: + # we need to adjust the unit_factor of the stock moves + # to split correctly the load of each one. + ratio = qty_to_procure / move.product_uom_qty + new_move = move.copy({ + 'product_uom_qty': qty_to_procure, + 'procure_method': 'make_to_order', + 'unit_factor': move.unit_factor * ratio, + }) + move.write({ + 'product_uom_qty': + move.product_uom_qty - qty_to_procure, + 'unit_factor': move.unit_factor * (1 - ratio), + }) + move._action_confirm() + move._action_assign() + elif qty_to_procure > 0.0: + new_move = move + else: + # If we don't need to procure, we reserve the qty + # for this move so it won't be available for others, + # which would generate planning issues. + move._action_confirm() + move._action_assign() + if new_move: + self.run_procurement( + new_move, qty_to_procure, mto_with_no_move_dest_id) + return res + @api.multi def run_procurement(self, move, qty, mto_with_no_move_dest_id): self.ensure_one() @@ -75,8 +104,7 @@ class MrpProduction(models.Model): # And the previous move generated now. if mto_with_no_move_dest_id: values.pop('move_dest_ids', None) - origin = '{0}:{1}'.format(self.name, move.product_id.name) + \ - ':MTO -> Production' + origin = self.origin or move.origin values['route_ids'] = move.product_id.route_ids try: self.env['procurement.group'].run( diff --git a/mrp_mto_with_stock/tests/test_mrp_mto_with_stock.py b/mrp_mto_with_stock/tests/test_mrp_mto_with_stock.py index 24e3db02b..7a5b3187d 100644 --- a/mrp_mto_with_stock/tests/test_mrp_mto_with_stock.py +++ b/mrp_mto_with_stock/tests/test_mrp_mto_with_stock.py @@ -106,16 +106,14 @@ class TestMrpMtoWithStock(TransactionCase): 2) self._update_product_qty(self.subproduct2, self.stock_location_stock, 4) - - self.production = self.production_model.create( - self._get_production_vals()) - self._update_product_qty(self.subproduct_1_1, self.stock_location_stock, 50) + self.production = self.production_model.create( + self._get_production_vals()) + self.assertEqual(len(self.production.move_raw_ids), 3) + # Create MO and check it create sub assemblie MO. - self.production.action_assign() - self.assertEqual(self.production.state, 'confirmed') mo = self.production_model.search( [('origin', 'ilike', self.production.name)]) self.assertEqual(mo.product_qty, 3) @@ -147,6 +145,15 @@ class TestMrpMtoWithStock(TransactionCase): wizard = wizard_obj.create(wizard_vals) wizard.do_produce() + # Check that not extra moves were generated and qty's are ok: + self.assertEqual(len(self.production.move_raw_ids), 3) + for move in self.production.move_raw_ids: + if move.product_id == self.subproduct1 and \ + move.procure_method == 'make_to_order': + qty = 3.0 + else: + qty = 2.0 + self.assertEqual(move.quantity_done, qty) self.assertTrue(self.production.check_to_done) self.production.button_mark_done() diff --git a/mrp_mto_with_stock/views/product_template_view.xml b/mrp_mto_with_stock/views/product_template_view.xml index 5eff34d89..99d027ae0 100644 --- a/mrp_mto_with_stock/views/product_template_view.xml +++ b/mrp_mto_with_stock/views/product_template_view.xml @@ -10,10 +10,10 @@ product.template - + - + diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 16abec0ad..ab4a3dafd 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -2,3 +2,4 @@ # Add a repository url and branch if you need a forked version product-attribute purchase-workflow +stock-logistics-warehouse