refactore creating a new route, a new procurement action and separate procurement rules to handle the mts+mto scenario

This commit is contained in:
Florian da Costa
2015-04-29 17:36:21 +02:00
committed by Maik Steinfeld
parent a901d10550
commit 3744245a1e
10 changed files with 455 additions and 0 deletions

View File

@@ -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 <florian.dacosta@akretion.com>
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.

View File

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

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2015 Akretion (<http://www.akretion.com>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{'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,
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--
Procurement rules
-->
<record id="route_mto_mts" model='stock.location.route'>
<field name="name">Make To Order + Make To Stock</field>
<field name="sequence">5</field>
<field name="product_selectable" eval="True"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import rule
from . import warehouse
from . import procurement

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Module for OpenERP
# Copyright (C) 2015 Akretion (http://www.akretion.com). All Rights Reserved
# @author Florian DA COSTA <florian.dacosta@akretion.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
###############################################################################
from openerp import models, api
class ProcurementOrder(models.Model):
_inherit = 'procurement.order'
@api.multi
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)
virtual_available = proc_warehouse.product_id.virtual_available
qty_available = uom_obj._compute_qty(self.product_id.uom_id.id,
virtual_available,
self.product_uom.id)
if qty_available > 0:
if qty_available >= self.product_qty:
return 0.0
else:
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):
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:
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:
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)

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Module for OpenERP
# Copyright (C) 2015 Akretion (http://www.akretion.com). All Rights Reserved
# @author Florian DA COSTA <florian.dacosta@akretion.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
###############################################################################
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()

View File

@@ -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 <florian.dacosta@akretion.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
###############################################################################
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

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="view_procurement_rule_form_mto_mto" model="ir.ui.view">
<field name="name">procurement.rule.mts.mto</field>
<field name="model">procurement.rule</field>
<field name="inherit_id" ref="procurement.view_procurement_rule_form" />
<field name="arch" type="xml">
<field name="action" position="after">
<field name="mts_rule_id"
groups="stock.group_adv_location"
attrs="{'invisible': [('action', '!=', 'split_procurement')]}"/>
<field name="mto_rule_id"
groups="stock.group_adv_location"
attrs="{'invisible': [('action', '!=', 'split_procurement')]}"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_warehouse_inherited" model="ir.ui.view">
<field name="name">view_warehouse_inherited</field>
<field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='delivery_steps']" position="after">
<field name="mto_mts_management" />
</xpath>
</field>
</record>
</data>
</openerp>