[ADD] 12.0 stock_mts_mto_rule_mrp

This module allows using the split_procurement action on Manufacturing routes, especially when using 2-step manufacturing
This commit is contained in:
Alexandre Fayolle
2021-10-22 12:45:05 +02:00
parent f86008c501
commit b963308678
12 changed files with 296 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
==========================
Stock MTS+MTO Rule for MRP
==========================
This module adds support on mrp manufacturing orders for the MTS+MTO rule from stock_mts_mto_rule module (from https://github.com/OCA/stock-logistics-warehouse)
Configuration
=============
XXX
Usage
=====
XXX
Known issues
============
XXX
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/manufacture/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
=======
Contributors
------------
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
Do not contact contributors directly about support or help with technical issues.
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.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,19 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Stock MTS+MTO Rule for manufacturing',
'summary': 'Add support for MTS+MTO route on manufacturing',
'version': '12.0.1.0.0',
'development_status': 'Alpha',
'category': 'Warehouse',
'website': 'https://github.com/OCA/manufacture',
'author': 'Camptocamp,Odoo Community Association (OCA)',
'license': 'AGPL-3',
'application': False,
'installable': True,
'depends': [
'stock_mts_mto_rule',
'mrp'
],
'auto_install': True,
}

View File

@@ -0,0 +1 @@
from . import mrp_production

View File

@@ -0,0 +1,65 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, models
from odoo.tools import float_compare, float_is_zero
class MrpProduction(models.Model):
_inherit = "mrp.production"
@api.multi
def _adjust_procure_method(self):
# we call super()._adjust_procure_method first and then come back to
# check for split_procurement rules on the move.
super()._adjust_procure_method()
precision = self.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
for move in self.move_raw_ids:
product = move.product_id
routes = (
product.route_ids +
product.route_from_categ_ids +
move.warehouse_id.route_ids
)
# find if we have a "split_procurement" rule in the routes
split_rule = self.env["stock.rule"].search(
[
("route_id", "in", [x.id for x in routes]),
("location_src_id", "=", move.location_id.id),
("location_id", "=", move.location_dest_id.id),
("action", "=", "split_procurement")
],
limit=1
)
if split_rule:
product_qty = move.product_uom_qty
uom = move.product_id.uom_id
needed_qty = split_rule.get_mto_qty_to_order(
move.product_id, product_qty, uom, values=None
)
if float_is_zero(needed_qty, precision_digits=precision):
# no additional product -> MTS
move.procure_method = split_rule.mts_rule_id.procure_method
elif float_compare(needed_qty, product_qty,
precision_digits=precision) == 0.0:
# no stock -> MTO
move.procure_method = split_rule.mto_rule_id.procure_method
else:
# partial MTS, remainder MTO
mts_qty = product_qty - needed_qty
mts_rule = split_rule.mts_rule_id
mto_rule = split_rule.mto_rule_id
move.update(
{
"procure_method": mts_rule.procure_method,
"product_uom_qty": mts_qty
}
)
# create the MTO move, attached to same MO
move.copy(
default={
"procure_method": mto_rule.procure_method,
"product_uom_qty": needed_qty
}
)

View File

@@ -0,0 +1,14 @@
To use the MTS + MTO rules for manufacturing on a warehouse:
1. configure the warehouse to use "Pick components and then manufacture (2 steps)"
2. Find the route "Pick components and then manufacture" of the
warehouse. There should be 2 rules: one to pull products from Stock and
bring it to PreProduction (using MTS), and one to pull products from
PreProduction and to bring it to Production (MTO). You need to create 2 new
rules:
* one route to pull products from PreProduction and bring it to Production (MTS), using the Manufacturing operation type
* one route to choose between MTS and MTO, with Operation Type:
Manufacturing, Source location PreProduction, Destination Production, MTS
rule: the one created in the previous step, MTO rule: the preexisting MTO
rule pulling from PreProduction to Production

View File

@@ -0,0 +1 @@
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>

View File

@@ -0,0 +1,5 @@
This module adds support on mrp manufacturing orders for the MTS+MTO rule from
stock_mts_mto_rule module (from https://github.com/OCA/stock-logistics-warehouse)
You can then configure a pick before manufacture route which will only pick if
there is no stock left in the source location of the manufacturing order.

View File

@@ -0,0 +1,6 @@
12.0.1.0.0 (2021-10-22)
~~~~~~~~~~~~~~~~~~~~~~~
**Features**
- Initial release

View File

@@ -0,0 +1 @@
Once the routes are configured, there is nothing special to do.

View File

@@ -0,0 +1 @@
from . import test_mto_mts_route_mrp

View File

@@ -0,0 +1,125 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import TransactionCase
from odoo.tests import tagged
@tagged('post_install', '-at_install')
class TestMtoMtsRouteMRP(TransactionCase):
def setUp(self):
super().setUp()
wh = self.env.ref("stock.warehouse0")
self.wh = wh
wh.manufacture_steps = "pbm" # 2 step manufacturing
route = self.env["stock.location.route"].search(
[("warehouse_ids", "=", wh.id),
("name", "like", "% Pick components and then manufacture")]
)
mto_rule = route.rule_ids.filtered(
lambda r: r.location_id.usage == "production"
)
mto_rule.name = "%s MTO" % mto_rule.name
mts_rule = mto_rule.copy({"name": mto_rule.name.replace("MTO", "MTS"),
"procure_method": "make_to_stock"})
# mts_mto_rule
self.env["stock.rule"].create(
{
"route_id": route.id,
"name": "choose MTS MTO",
"action": "split_procurement",
"mts_rule_id": mts_rule.id,
"mto_rule_id": mto_rule.id,
"picking_type_id": mts_rule.picking_type_id.id,
"location_id": mts_rule.location_id.id,
"location_src_id": mts_rule.location_src_id.id,
"warehouse_id": wh.id
}
)
self.stock_location = wh.lot_stock_id
self.preprod_location = wh.pbm_loc_id
self.finished_product = self.env["product.product"].create(
{
"name": "finished_product",
"type": "product",
"sale_line_warn": "no-message",
}
)
self.component_product = self.env["product.product"].create(
{
"name": "component",
"type": "product",
"sale_line_warn": "no-message",
}
)
self.bom = self.env["mrp.bom"].create(
{
"product_id": self.finished_product.id,
"product_tmpl_id": self.finished_product.product_tmpl_id.id,
"bom_line_ids": [
(
0,
0,
{
"product_id": self.component_product.id,
"product_qty": 2,
"product_uom_id": self.component_product.uom_id.id
}
)
]
}
)
def _create_mo(self):
manufacture = self.wh.manu_type_id
return self.env["mrp.production"].create(
{
"product_id": self.finished_product.id,
"bom_id": self.bom.id,
"product_qty": 1,
"product_uom_id": self.finished_product.uom_id.id,
"picking_type_id": self.wh.manu_type_id.id,
"location_src_id": manufacture.default_location_src_id.id,
"location_dest_id": manufacture.default_location_dest_id.id,
}
)
def test_stock_in_stock(self):
"""quantity in WH/Stock = 2 -> take 2 in stock to preprod"""
self.env["stock.quant"]._update_available_quantity(
self.component_product, self.stock_location, 2
)
mo = self._create_mo()
self.assertEqual(mo.move_raw_ids.procure_method, "make_to_order")
def test_stock_in_preprod(self):
"""quantity in WH/Preprod = 2 -> take 2 in preprod"""
self.env["stock.quant"]._update_available_quantity(
self.component_product, self.preprod_location, 2
)
mo = self._create_mo()
self.assertEqual(mo.move_raw_ids.procure_method, "make_to_stock")
def test_stock_in_preprod_and_in_stock(self):
"""quantity in WH/PreProd = 1 -> take 1 in preprod,
and fetch the rest from stock"""
self.env["stock.quant"]._update_available_quantity(
self.component_product, self.preprod_location, 1
)
mo = self._create_mo()
self.assertEqual(
len(
mo.move_raw_ids.filtered(
lambda r: r.procure_method == "make_to_stock"
)
),
1
)
self.assertEqual(
len(
mo.move_raw_ids.filtered(
lambda r: r.procure_method == "make_to_order"
)
),
1
)