mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[11.0][REW] mrp_mto_with_stock:
* logic for reservable stock moved to _adjust_procure_method. * hook for mto_with_stock condition. * fix bug with unit_factor and add tests for it. * update module dependencies. * move configuration field to operations group. * propagate origin if existing MO.
This commit is contained in:
committed by
Jordi Ballester Alomar
parent
e9633c9139
commit
4edc52c717
@@ -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',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="stock.view_template_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_stock_inventory" position="after">
|
||||
<group name="operations" position="inside">
|
||||
<field name="mrp_mts_mto_location_ids" widget="many2many_tags"
|
||||
options="{'no_create': True}"/>
|
||||
</field>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Add a repository url and branch if you need a forked version
|
||||
product-attribute
|
||||
purchase-workflow
|
||||
stock-logistics-warehouse
|
||||
|
||||
Reference in New Issue
Block a user