mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[MOV] move addons out of __unported__ (they remain not installable)
This commit is contained in:
committed by
Florian da Costa
parent
8bb6375f92
commit
de4a5dfa16
48
stock_available_mrp/README.rst
Normal file
48
stock_available_mrp/README.rst
Normal file
@@ -0,0 +1,48 @@
|
||||
Consider the production potential is available to promise
|
||||
=========================================================
|
||||
|
||||
This module takes the potential quantities available for Products in account in
|
||||
the quantity available to promise, where the "Potential quantity" is the
|
||||
quantity that can be manufactured with the components immediately at hand.
|
||||
|
||||
Known issues
|
||||
============
|
||||
|
||||
The manufacturing delays are not taken into account : this module assumes that
|
||||
if you have components in stock goods, you can manufacture finished goods
|
||||
quickly enough.
|
||||
To avoid overestimating, **only the first level** of Bill of Materials is
|
||||
considered.
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
* include all levels of BoM, using `bom_explode`. @gdgellatly gave an example
|
||||
of how to do it here: https://github.com/OCA/stock-logistics-warehouse/pull/5#issuecomment-66902191
|
||||
Ideally, we will want to take manufacturing delays into account: we can't
|
||||
promiss goods to customers if they want them delivered earlier that we can
|
||||
make them
|
||||
* add an option (probably as a sub-module) to consider all raw materials as
|
||||
available if they can be bought from the suppliers in time for the
|
||||
manufacturing.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Contributors
|
||||
------------
|
||||
* Loïc Bellier (Numérigraphe) <lb@numerigraphe.com>
|
||||
* Lionel Sausin (Numérigraphe) <ls@numerigraphe.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.
|
||||
21
stock_available_mrp/__init__.py
Normal file
21
stock_available_mrp/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
|
||||
#
|
||||
# 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 . import product
|
||||
35
stock_available_mrp/__openerp__.py
Normal file
35
stock_available_mrp/__openerp__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
|
||||
#
|
||||
# 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': 'Consider the production potential is available to promise',
|
||||
'version': '2.0',
|
||||
"author": u"Numérigraphe,Odoo Community Association (OCA)",
|
||||
'category': 'Hidden',
|
||||
'depends': ['stock_available', 'mrp'],
|
||||
'data': [
|
||||
'product_view.xml',
|
||||
],
|
||||
'test': [
|
||||
'test/potential_qty.yml',
|
||||
],
|
||||
'license': 'AGPL-3',
|
||||
'installable': False
|
||||
}
|
||||
34
stock_available_mrp/i18n/fr.po
Normal file
34
stock_available_mrp/i18n/fr.po
Normal file
@@ -0,0 +1,34 @@
|
||||
# Translation of OpenERP Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * stock_available_mrp
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 7.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-07-30 19:29+0000\n"
|
||||
"PO-Revision-Date: 2014-07-30 19:29+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: field:product.product,potential_qty:0
|
||||
msgid "Potential"
|
||||
msgstr "Potentiel"
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: code:_description:0
|
||||
#: model:ir.model,name:stock_available_mrp.model_product_product
|
||||
#, python-format
|
||||
msgid "Product"
|
||||
msgstr "Article"
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: help:product.product,potential_qty:0
|
||||
msgid "Quantity of this Product that could be produced using the materials already at hand, following a single level of the Bills of Materials."
|
||||
msgstr "Quantité de cet article que l'on pourrait produire en utilisant les produits déjà disponibles, en suivant un seul niveau de nomenclature."
|
||||
|
||||
34
stock_available_mrp/i18n/stock_available_mrp.pot
Normal file
34
stock_available_mrp/i18n/stock_available_mrp.pot
Normal file
@@ -0,0 +1,34 @@
|
||||
# Translation of OpenERP Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * stock_available_mrp
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 7.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-07-30 19:41+0000\n"
|
||||
"PO-Revision-Date: 2014-07-30 19:41+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: field:product.product,potential_qty:0
|
||||
msgid "Potential"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: code:_description:0
|
||||
#: model:ir.model,name:stock_available_mrp.model_product_product
|
||||
#, python-format
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_mrp
|
||||
#: help:product.product,potential_qty:0
|
||||
msgid "Quantity of this Product that could be produced using the materials already at hand, following a single level of the Bills of Materials."
|
||||
msgstr ""
|
||||
|
||||
126
stock_available_mrp/product.py
Normal file
126
stock_available_mrp/product.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# This module is copyright (C) 2014 Numérigraphe SARL. All Rights Reserved.
|
||||
#
|
||||
# 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 SUPERUSER_ID
|
||||
from openerp.osv import orm, fields
|
||||
import openerp.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class product_product(orm.Model):
|
||||
"""Add the computation for the stock available to promise"""
|
||||
_inherit = 'product.product'
|
||||
|
||||
def _product_available(self, cr, uid, ids, field_names=None, arg=False,
|
||||
context=None):
|
||||
"""Quantity available to promise based on components at hand."""
|
||||
# Compute the core quantities
|
||||
res = super(product_product, self)._product_available(
|
||||
cr, uid, ids, field_names=field_names, arg=arg, context=context)
|
||||
# If we didn't get a field_names list, there's nothing to do
|
||||
if field_names is None:
|
||||
return res
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
# Prepare an alternative context without 'uom', to avoid cross-category
|
||||
# conversions when reading the available stock of components
|
||||
if 'uom' in context:
|
||||
context_wo_uom = context.copy()
|
||||
del context_wo_uom['uom']
|
||||
else:
|
||||
context_wo_uom = context
|
||||
|
||||
# Compute the production capacity
|
||||
if any([f in field_names
|
||||
for f in ['potential_qty', 'immediately_usable_qty']]):
|
||||
# Compute the potential qty from BoMs with components available
|
||||
bom_obj = self.pool['mrp.bom']
|
||||
to_uom = 'uom' in context and self.pool['product.uom'].browse(
|
||||
cr, SUPERUSER_ID, context['uom'], context=context)
|
||||
|
||||
for product in self.browse(cr, uid, ids, context=context):
|
||||
# _bom_find() returns a single BoM id.
|
||||
# We will not check any other BoM for this product
|
||||
bom_id = bom_obj._bom_find(cr, SUPERUSER_ID, product.id,
|
||||
product.uom_id.id)
|
||||
if bom_id:
|
||||
min_qty = self._compute_potential_qty_from_bom(
|
||||
cr, uid, bom_id, to_uom or product.uom_id,
|
||||
context=context)
|
||||
|
||||
if 'potential_qty' in field_names:
|
||||
res[product.id]['potential_qty'] += min_qty
|
||||
if 'immediately_usable_qty' in field_names:
|
||||
res[product.id]['immediately_usable_qty'] += min_qty
|
||||
|
||||
return res
|
||||
|
||||
def _compute_potential_qty_from_bom(self, cr, uid, bom_id, to_uom,
|
||||
context=None):
|
||||
"""Compute the potential qty from BoMs with components available"""
|
||||
bom_obj = self.pool['mrp.bom']
|
||||
uom_obj = self.pool['product.uom']
|
||||
if context is None:
|
||||
context = {}
|
||||
if 'uom' in context:
|
||||
context_wo_uom = context.copy()
|
||||
del context_wo_uom['uom']
|
||||
else:
|
||||
context_wo_uom = context
|
||||
min_qty = False
|
||||
# Browse ignoring the UoM context to avoid cross-category conversions
|
||||
bom = bom_obj.browse(
|
||||
cr, uid, [bom_id], context=context_wo_uom)[0]
|
||||
|
||||
# store id of final product uom
|
||||
|
||||
for component in bom.bom_lines:
|
||||
# qty available in BOM line's UoM
|
||||
# XXX use context['uom'] instead?
|
||||
stock_component_qty = uom_obj._compute_qty_obj(
|
||||
cr, uid,
|
||||
component.product_id.uom_id,
|
||||
component.product_id.virtual_available,
|
||||
component.product_uom)
|
||||
# qty we can produce with this component, in the BoM's UoM
|
||||
bom_uom_qty = (stock_component_qty // component.product_qty
|
||||
) * bom.product_qty
|
||||
# Convert back to the reporting default UoM
|
||||
stock_product_uom_qty = uom_obj._compute_qty_obj(
|
||||
cr, uid, bom.product_uom, bom_uom_qty,
|
||||
to_uom)
|
||||
if min_qty is False:
|
||||
min_qty = stock_product_uom_qty
|
||||
elif stock_product_uom_qty < min_qty:
|
||||
min_qty = stock_product_uom_qty
|
||||
if min_qty < 0.0:
|
||||
min_qty = 0.0
|
||||
return min_qty
|
||||
|
||||
_columns = {
|
||||
'potential_qty': fields.function(
|
||||
_product_available, method=True, multi='qty_available',
|
||||
type='float',
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'),
|
||||
string='Potential',
|
||||
help="Quantity of this Product that could be produced using "
|
||||
"the materials already at hand, following a single level "
|
||||
"of the Bills of Materials."),
|
||||
}
|
||||
19
stock_available_mrp/product_view.xml
Normal file
19
stock_available_mrp/product_view.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Add the quantity available to promise in the product form -->
|
||||
<record id="view_product_form_potential_qty" model="ir.ui.view">
|
||||
<field name="name">product.form.potential_qty</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="type">form</field>
|
||||
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form" />
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='virtual_available']" position="after">
|
||||
<field name="potential_qty"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
70
stock_available_mrp/test/potential_qty.yml
Normal file
70
stock_available_mrp/test/potential_qty.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
- Test the computation of the potential quantity on product_product_16, a product with several multi-line BoMs
|
||||
|
||||
- Create a UoM in the category of PCE
|
||||
- !record {model: product.uom, id: thousand}:
|
||||
name: Thousand
|
||||
factor: 0.001
|
||||
rounding: 0.0001
|
||||
uom_type: bigger
|
||||
category_id: product.product_uom_categ_unit
|
||||
|
||||
- Receive enough of the first component to run the BoM 1000x, and check that the potential is unchanged
|
||||
- !python {model: mrp.bom}: |
|
||||
bom = self.browse(
|
||||
cr, uid,
|
||||
self._bom_find(
|
||||
cr, uid, ref('product.product_product_16'),
|
||||
ref('product.product_uom_unit')))
|
||||
assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
|
||||
initial_qty = bom.product_id.potential_qty
|
||||
component = bom.bom_lines[0]
|
||||
assert component.product_uom.category_id.id == ref('product.product_uom_categ_unit'), "The first component's UoM is in the wrong category can't test"
|
||||
self.pool['stock.move'].create(
|
||||
cr, uid,
|
||||
{
|
||||
'name': 'Receive first component',
|
||||
'product_id': component.product_id.id,
|
||||
'product_qty': component.product_qty * 1000.0,
|
||||
'product_uom': component.product_id.uom_id.id,
|
||||
'location_id': ref('stock.stock_location_suppliers'),
|
||||
'location_dest_id': ref('stock.stock_location_stock'),
|
||||
'state': 'done',
|
||||
})
|
||||
# Re-read the potential quantity
|
||||
bom.refresh()
|
||||
new_qty = bom.product_id.potential_qty
|
||||
assert new_qty == initial_qty, "Receiving a single component should not change the potential qty (%s instead of %s)" % (new_qty, initial_qty)
|
||||
|
||||
- Receive enough of all the components to run the BoM 1000x and check that the potential is correct
|
||||
- !python {model: mrp.bom}: |
|
||||
# Select a BoM for product_product_16
|
||||
bom = self.browse(
|
||||
cr, uid,
|
||||
self._bom_find(
|
||||
cr, uid, ref('product.product_product_16'),
|
||||
ref('product.product_uom_unit')))
|
||||
assert len(bom.bom_lines)>1, "The test BoM has a single line, two or more are needed for the test"
|
||||
initial_qty = bom.product_id.potential_qty
|
||||
for component in bom.bom_lines:
|
||||
assert component.product_uom.category_id.id == ref('product.product_uom_categ_unit'), "The first component's UoM is in the wrong category, can't test"
|
||||
self.pool['stock.move'].create(
|
||||
cr, uid,
|
||||
{
|
||||
'name': 'Receive all components',
|
||||
'product_id': component.product_id.id,
|
||||
'product_qty': component.product_qty * 1000.0,
|
||||
'product_uom': component.product_id.uom_id.id,
|
||||
'location_id': ref('stock.stock_location_suppliers'),
|
||||
'location_dest_id': ref('stock.stock_location_stock'),
|
||||
'state': 'done',
|
||||
})
|
||||
# Re-read the potential quantity
|
||||
bom.refresh()
|
||||
new_qty = bom.product_id.potential_qty
|
||||
right_qty = initial_qty + bom.product_qty * 1000.0
|
||||
assert new_qty == right_qty, "The potential qty is incorrect after receiveing all the components (%s instead of %s)" % (new_qty, right_qty)
|
||||
# Re-read the potential quantity with a different UoM in the context
|
||||
new_qty = self.browse(
|
||||
cr, uid, bom.id, context={'uom': ref('thousand')}).product_id.potential_qty
|
||||
right_qty = initial_qty / 1000.0 + bom.product_qty
|
||||
assert abs(new_qty - right_qty) < 0.0001, "The potential qty is incorrect with another UoM in the context (%s instead of %s)" % (new_qty, right_qty)
|
||||
Reference in New Issue
Block a user