[MOV] move addons out of __unported__ (they remain not installable)

This commit is contained in:
Stéphane Bidoul
2015-10-13 16:59:05 +02:00
committed by Florian da Costa
parent 8bb6375f92
commit de4a5dfa16
8 changed files with 387 additions and 0 deletions

View 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.

View 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

View 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
}

View 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."

View 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 ""

View 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."),
}

View 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>

View 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)