[ADD] stock_available

Generic module to compute the stock quantity available to promise using several implementations.
stock_available_immediatly is changed to become the first optional implementation.
Cherry pick of commit 0b060f619f from the v7 branch
This commit is contained in:
Lionel Sausin (Numérigraphe)
2015-02-27 13:43:56 +01:00
committed by Lionel Sausin
parent 3bde29529d
commit cb186fe179
10 changed files with 478 additions and 124 deletions

View File

@@ -0,0 +1,24 @@
# -*- 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
from . import res_config
from .product import _product_available_fnct

View File

@@ -0,0 +1,42 @@
# -*- 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': 'Stock available to promise',
'version': '2.0',
'author': u'Numérigraphe',
'category': 'Warehouse',
'depends': ['stock'],
'description': """
Stock available to promise
==========================
This module proposes several options to compute the quantity available to
promise for each product.
This quantity is based on the projected stock and, depending on the
configuration, it can account for various data such as sales quotations or
immediate production capacity.
This can be configured in the menu Settings > Configuration > Warehouse.
""",
'license': 'AGPL-3',
'data': [
'product_view.xml',
'res_config_view.xml',
]
}

View File

@@ -0,0 +1,98 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * stock_available
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-30 16:42+0000\n"
"PO-Revision-Date: 2014-07-30 16:42+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
#: field:product.product,immediately_usable_qty:0
msgid "Available to promise"
msgstr "Disponible à la vente"
#. module: stock_available
#: view:product.product:0
msgid "Available to promise:"
msgstr "Disponible à la vente:"
#. module: stock_available
#: field:stock.config.settings,module_stock_available_sale:0
msgid "Exclude goods already in sale quotations"
msgstr "Exclure les marchandises qui sont déjà dans les devis"
#. module: stock_available
#: field:stock.config.settings,module_stock_available_immediately:0
msgid "Exclude incoming goods"
msgstr "Exclure les receptions attendues"
#. module: stock_available
#: field:stock.config.settings,module_stock_available_mrp:0
msgid "Include the production potential"
msgstr "Inclure la production potentielle"
#. module: stock_available
#: code:_description:0
#: model:ir.model,name:stock_available.model_product_product
#, python-format
msgid "Product"
msgstr "Article"
#. module: stock_available
#: view:stock.config.settings:0
msgid "Stock available to promise"
msgstr "Stock disponible à la vente"
#. module: stock_available
#: help:product.product,immediately_usable_qty:0
msgid "Stock for this Product that can be safely proposed for sale to Customers.\n"
"The definition of this value can be configured to suit your needs"
msgstr "Stock de cet article qui peut sans risque être proposé à la vente aux clients.\n"
"La définition de cette valeur est paramétrable selon vos besoins"
#. module: stock_available
#: help:stock.config.settings,module_stock_available_mrp:0
msgid "This will add the quantities of goods that can be immediately manufactured, to the quantities available to promise.\n"
"This installs the module stock_available_mrp.\n"
"If the module mrp is not installed, this will install it too"
msgstr "Ceci ajoute les quantités de marchandises qui peuvent être immédiatement fabriquées, aux quantitiés disponibles à la vente.\n"
"Ceci installe le module stock_available_mrp.\n"
"Si le module mrp n'est pas encore installé, il le sera aussi"
#. module: stock_available
#: help:stock.config.settings,module_stock_available_immediately:0
msgid "This will subtract incoming quantities from the quantities available to promise.\n"
"This installs the module stock_available_immediately."
msgstr "Ceci soustrait les réceptions attendues des quantitiés disponibles à la vente.\n"
"Ceci installe le module stock_available_immediately."
#. module: stock_available
#: help:stock.config.settings,module_stock_available_sale:0
msgid "This will subtract quantities from the sale quotations from the quantities available to promise.\n"
"This installs the modules stock_available_sale.\n"
"If the modules sale and sale_delivery_date are not installed, this will install them too"
msgstr "Ceci soustrait les quantités des devis de vente des quantitiés disponibles à la vente.\n"
"Ceci installe le modules stock_available_sale.\n"
"Si les modules sale et sale_delivery_date ne sont pas encore installés, ils le seront également"
#. module: stock_available
#: view:product.product:0
msgid "red:immediately_usable_qty<0;blue:immediately_usable_qty>=0 and state in ('draft', 'end', 'obsolete');black:immediately_usable_qty>=0 and state not in ('draft', 'end', 'obsolete')"
msgstr "red:immediately_usable_qty<0;blue:immediately_usable_qty>=0 and state in ('draft', 'end', 'obsolete');black:immediately_usable_qty>=0 and state not in ('draft', 'end', 'obsolete')"
#. module: stock_available
#: code:_description:0
#: model:ir.model,name:stock_available.model_stock_config_settings
#, python-format
msgid "stock.config.settings"
msgstr "stock.config.settings"

View File

@@ -0,0 +1,92 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * stock_available
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-30 16:48+0000\n"
"PO-Revision-Date: 2014-07-30 16:48+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
#: field:product.product,immediately_usable_qty:0
msgid "Available to promise"
msgstr ""
#. module: stock_available
#: view:product.product:0
msgid "Available to promise:"
msgstr ""
#. module: stock_available
#: field:stock.config.settings,module_stock_available_sale:0
msgid "Exclude goods already in sale quotations"
msgstr ""
#. module: stock_available
#: field:stock.config.settings,module_stock_available_immediately:0
msgid "Exclude incoming goods"
msgstr ""
#. module: stock_available
#: field:stock.config.settings,module_stock_available_mrp:0
msgid "Include the production potential"
msgstr ""
#. module: stock_available
#: code:_description:0
#: model:ir.model,name:stock_available.model_product_product
#, python-format
msgid "Product"
msgstr ""
#. module: stock_available
#: view:stock.config.settings:0
msgid "Stock available to promise"
msgstr ""
#. module: stock_available
#: help:product.product,immediately_usable_qty:0
msgid "Stock for this Product that can be safely proposed for sale to Customers.\n"
"The definition of this value can be configured to suit your needs"
msgstr ""
#. module: stock_available
#: help:stock.config.settings,module_stock_available_mrp:0
msgid "This will add the quantities of goods that can be immediately manufactured, to the quantities available to promise.\n"
"This installs the module stock_available_mrp.\n"
"If the module mrp is not installed, this will install it too"
msgstr ""
#. module: stock_available
#: help:stock.config.settings,module_stock_available_immediately:0
msgid "This will subtract incoming quantities from the quantities available to promise.\n"
"This installs the module stock_available_immediately."
msgstr ""
#. module: stock_available
#: help:stock.config.settings,module_stock_available_sale:0
msgid "This will subtract quantities from the sale quotations from the quantities available to promise.\n"
"This installs the modules stock_available_sale.\n"
"If the modules sale and sale_delivery_date are not installed, this will install them too"
msgstr ""
#. module: stock_available
#: view:product.product:0
msgid "red:immediately_usable_qty<0;blue:immediately_usable_qty>=0 and state in ('draft', 'end', 'obsolete');black:immediately_usable_qty>=0 and state not in ('draft', 'end', 'obsolete')"
msgstr ""
#. module: stock_available
#: code:_description:0
#: model:ir.model,name:stock_available.model_stock_config_settings
#, python-format
msgid "stock.config.settings"
msgstr ""

100
stock_available/product.py Normal file
View File

@@ -0,0 +1,100 @@
# -*- 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.osv import orm, fields
import openerp.addons.decimal_precision as dp
# Expose the method as a function, like when the fields are defined,
# and use the pool to call the method from the other modules too.
def _product_available_fnct(self, cr, uid, ids, field_names=None, arg=False,
context=None):
return self.pool['product.product']._product_available(
cr, uid, ids, field_names=field_names, arg=arg, context=context)
class ProductProduct(orm.Model):
"""Add a field for the stock available to promise.
Useful implementations need to be installed through the Settings menu or by
installing one of the modules stock_available_*
"""
_inherit = 'product.product'
def __init__(self, pool, cr):
"""Use _product_available_fnct to compute all the quantities."""
# Doing this lets us change the function and not redefine fields
super(ProductProduct, self).__init__(pool, cr)
for coldef in self._columns.values():
if (isinstance(coldef, fields.function)
and coldef._multi == 'qty_available'):
coldef._fnct = _product_available_fnct
def _product_available(self, cr, uid, ids, field_names=None, arg=False,
context=None):
"""No-op implementation of the stock available to promise.
Must be overridden by another module that actually implement
computations.
The sub-modules MUST call super()._product_available BEFORE their own
computations
Side-effect warning: This method may change the list passed as the
field_names parameter, which will then alter the caller's state."""
# If we didn't get a field_names list, there's nothing to do
if field_names is None:
return super(ProductProduct, self)._product_available(
cr, uid, ids, field_names=field_names, arg=arg,
context=context)
if context is None:
context = {}
# Load virtual_available if it's not already asked for
# We need it to compute immediately_usable_qty
# We DO want to change the caller's list so we're NOT going to
# work on a copy of field_names.
if ('virtual_available' not in field_names
and 'immediately_usable_qty' in field_names):
field_names.append('virtual_available')
# Compute the core quantities
res = super(ProductProduct, self)._product_available(
cr, uid, ids, field_names=field_names, arg=arg, context=context)
# By default, available to promise = forecasted quantity
if ('immediately_usable_qty' in field_names):
for stock_qty in res.itervalues():
stock_qty['immediately_usable_qty'] = \
stock_qty['virtual_available']
return res
_columns = {
'immediately_usable_qty': fields.function(
_product_available_fnct, multi='qty_available',
type='float',
digits_compute=dp.get_precision('Product Unit of Measure'),
string='Available to promise',
help="Stock for this Product that can be safely proposed "
"for sale to Customers.\n"
"The definition of this value can be configured to suit "
"your needs"),
}

View File

@@ -1,32 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
stock available_immediately for OpenERP
Author Guewen Baconnier. Copyright Camptocamp SA
Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record model="ir.ui.view" id="view_normal_stock_active_qty_form">
<field name="name">product.normal.stock.active.qty.form.inherit</field>
<record model="ir.ui.view" id="view_stock_available_form">
<field name="name">Quantity available to promise (form)</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.view_template_property_form"/>
<field name="arch" type="xml">
<field name="virtual_available" position="before">
<newline/>
<field name="outgoing_qty" class="oe_inline"/>
</field>
<field name="virtual_available" position="after">
<newline/>
<field name="immediately_usable_qty" />
</field>
</field>
</record>
<record model="ir.ui.view" id="product_product_tree_view">
<field name="name">product_immediately_usable.product_product_tree_view</field>
<record model="ir.ui.view" id="view_stock_available_tree">
<field name="name">Quantity available to promise (tree)</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.view_stock_product_template_tree"/>
<field name="arch" type="xml">
@@ -34,12 +22,22 @@
<tree position="attributes">
<attribute name="colors">red:immediately_usable_qty&lt;0;blue:immediately_usable_qty&gt;=0 and state in ('draft', 'end', 'obsolete');black:immediately_usable_qty&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute>
</tree>
<field name="virtual_available" position="replace">
<field name="virtual_available" position="after">
<field name="immediately_usable_qty" />
</field>
</data>
</field>
</record>
<record model="ir.ui.view" id="view_stock_available_kanban">
<field name="name">Quantity available to promise (kanban)</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.product_template_kanban_stock_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='virtual_available']/.." position="replace">
<li t-if="record.type.raw_value != 'service'">Available to promise: <field name="immediately_usable_qty"/> <field name="uom_id"/></li>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,34 @@
# -*- 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 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class StockConfig(orm.TransientModel):
"""Add options to easily install the submodules"""
_inherit = 'stock.config.settings'
_columns = {
'module_stock_available_immediately': fields.boolean(
'Exclude incoming goods',
help="This will subtract incoming quantities from the quantities "
"available to promise.\n"
"This installs the module stock_available_immediately."),
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_stock_configuration" model="ir.ui.view">
<field name="name">Stock settings: quantity available to promise</field>
<field name="model">stock.config.settings</field>
<field name="inherit_id" ref="stock.view_stock_config_settings" />
<field name="arch" type="xml">
<data>
<xpath expr="//group[last()]" position="after">
<group>
<label for="id" string="Stock available to promise" />
<div>
<div>
<field name="module_stock_available_immediately" class="oe_inline" />
<label for="module_stock_available_immediately" />
</div>
</div>
</group>
</xpath>
</data>
</field>
</record>
</data>
</openerp>

View File

@@ -20,21 +20,27 @@
#
#
{
"name": "Immediately Usable Stock Quantity",
"version": "1.0",
"depends": ["product", "stock", ],
"name": "Ignore planned receptions in quantity available to promise",
"version": "2.0",
"depends": ["stock_available"],
"author": "Camptocamp",
"license": "AGPL-3",
"description": """
Compute the immediately usable stock.
Immediately usable is computed : Quantity on Hand - Outgoing Stock.
"description": u"""
Ignore planned receptions in quantity available to promise
----------------------------------------------------------
Normally the quantity available to promise is based on the virtual stock,
which includes both planned outgoing and incoming goods.
This module will subtract the planned receptions from the quantity available to
promise.
Contributors
------------
* Author: Guewen Baconnier (Camptocamp SA)
* Sébastien BEAU (Akretion) <sebastien.beau@akretion.com>
* Lionel Sausin (Numérigraphe) <ls@numerigraphe.com>
""",
"website": "http://tinyerp.com/module_account.html",
"category": "Generic Modules/Stock",
"data": ["product_view.xml",
],
"active": False,
"category": "Hidden",
'installable': True
}

View File

@@ -19,106 +19,41 @@
#
##############################################################################
from openerp.addons import decimal_precision as dp
from openerp.osv import orm
from openerp.osv import orm, fields
class product_immediately_usable(orm.Model):
"""Subtract incoming qty from immediately_usable_qty
class ProductTemplate(orm.Model):
"""
Immediately usable quantity is : real stock - outgoing qty
"""
_inherit = 'product.template'
We don't need to override the function fields, the module stock_available
takes of it for us.
Side-effect warning: This method may change the list passed as the
field_names parameter, which will then alter the caller's state."""
_inherit = 'product.product'
def _product_available(self, cr, uid, ids, field_names=None,
arg=False, context=None):
res = super(ProductTemplate, self)._product_available(
cr, uid, ids, field_names, arg, context)
"""Ignore the incoming goods in the quantity available to promise"""
# If we didn't get a field_names list, there's nothing to do
if field_names is None or 'immediately_usable_qty' not in field_names:
return super(product_immediately_usable, self)._product_available(
cr, uid, ids, field_names=field_names, arg=arg,
context=context)
if 'immediately_usable_qty' in field_names:
for product_id, stock_qty in res.iteritems():
res[product_id]['immediately_usable_qty'] = \
stock_qty['qty_available'] - stock_qty['outgoing_qty']
# We need available and incoming quantities to compute
# immediately usable quantity.
# We DO want to change the caller's list so we're NOT going to
# work on a copy of field_names.
field_names.append('qty_available')
field_names.append('incoming_qty')
res = super(product_immediately_usable, self)._product_available(
cr, uid, ids, field_names=field_names, arg=arg, context=context)
for stock_qty in res.itervalues():
stock_qty['immediately_usable_qty'] -= \
stock_qty['incoming_qty']
return res
_columns = {
'qty_available': fields.function(
_product_available,
multi='qty_available',
type='float',
digits_compute=dp.get_precision('Product UoM'),
string='Quantity On Hand',
help="Current quantity of products.\n"
"In a context with a single Stock Location, this includes "
"goods stored at this Location, or any of its children.\n"
"In a context with a single Warehouse, this includes "
"goods stored in the Stock Location of this Warehouse, "
"or any "
"of its children.\n"
"In a context with a single Shop, this includes goods "
"stored in the Stock Location of the Warehouse of this Shop, "
"or any of its children.\n"
"Otherwise, this includes goods stored in any Stock Location "
"typed as 'internal'."),
'virtual_available': fields.function(
_product_available,
multi='qty_available',
type='float',
digits_compute=dp.get_precision('Product UoM'),
string='Quantity Available',
help="Forecast quantity (computed as Quantity On Hand "
"- Outgoing + Incoming)\n"
"In a context with a single Stock Location, this includes "
"goods stored at this Location, or any of its children.\n"
"In a context with a single Warehouse, this includes "
"goods stored in the Stock Location of this Warehouse, "
"or any "
"of its children.\n"
"In a context with a single Shop, this includes goods "
"stored in the Stock Location of the Warehouse of this Shop, "
"or any of its children.\n"
"Otherwise, this includes goods stored in any Stock Location "
"typed as 'internal'."),
'incoming_qty': fields.function(
_product_available,
multi='qty_available',
type='float',
digits_compute=dp.get_precision('Product UoM'),
string='Incoming',
help="Quantity of products that are planned to arrive.\n"
"In a context with a single Stock Location, this includes "
"goods arriving to this Location, or any of its children.\n"
"In a context with a single Warehouse, this includes "
"goods arriving to the Stock Location of this Warehouse, or "
"any of its children.\n"
"In a context with a single Shop, this includes goods "
"arriving to the Stock Location of the Warehouse of this "
"Shop, or any of its children.\n"
"Otherwise, this includes goods arriving to any Stock "
"Location typed as 'internal'."),
'outgoing_qty': fields.function(
_product_available,
multi='qty_available',
type='float',
digits_compute=dp.get_precision('Product UoM'),
string='Outgoing',
help="Quantity of products that are planned to leave.\n"
"In a context with a single Stock Location, this includes "
"goods leaving from this Location, or any of its children.\n"
"In a context with a single Warehouse, this includes "
"goods leaving from the Stock Location of this Warehouse, or "
"any of its children.\n"
"In a context with a single Shop, this includes goods "
"leaving from the Stock Location of the Warehouse of this "
"Shop, or any of its children.\n"
"Otherwise, this includes goods leaving from any Stock "
"Location typed as 'internal'."),
'immediately_usable_qty': fields.function(
_product_available,
digits_compute=dp.get_precision('Product UoM'),
type='float',
string='Immediately Usable',
multi='qty_available',
help="Quantity of products really available for sale."
"Computed as: Quantity On Hand - Outgoing."),
}