From 1624c00c71f5089ce25fccd7aee960a2e79114e5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 17 Feb 2013 23:02:37 +0100 Subject: [PATCH 1/4] [ADD] Improved reordering rules on sales stats --- stock_reord_rule/__init__.py | 22 +++++++ stock_reord_rule/__openerp__.py | 38 +++++++++++++ stock_reord_rule/cron_data.xml | 15 +++++ stock_reord_rule/security/ir.model.access.csv | 2 + stock_reord_rule/stock_reord_rule.py | 57 +++++++++++++++++++ stock_reord_rule/stock_reord_rule_view.xml | 22 +++++++ 6 files changed, 156 insertions(+) create mode 100644 stock_reord_rule/__init__.py create mode 100644 stock_reord_rule/__openerp__.py create mode 100644 stock_reord_rule/cron_data.xml create mode 100644 stock_reord_rule/security/ir.model.access.csv create mode 100644 stock_reord_rule/stock_reord_rule.py create mode 100644 stock_reord_rule/stock_reord_rule_view.xml diff --git a/stock_reord_rule/__init__.py b/stock_reord_rule/__init__.py new file mode 100644 index 000000000..94a0cab3c --- /dev/null +++ b/stock_reord_rule/__init__.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Vehicle management for OpenERP +# Copyright (C) 2012 Sergio Corato () +# +# 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 . +# +############################################################################## + +import stock_reord_rule diff --git a/stock_reord_rule/__openerp__.py b/stock_reord_rule/__openerp__.py new file mode 100644 index 000000000..e288e4a48 --- /dev/null +++ b/stock_reord_rule/__openerp__.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Improved reordering rules for OpenERP +# Copyright (C) 2012 Sergio Corato () +# +# 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 . +# +############################################################################## + +{ + 'name': 'Improved reordering rules', + 'version': '0.1', + 'category': 'Tools', + 'description': """ + This module allows to improve reordering rules of stock module.""", + 'author': 'Sergio Corato', + 'website': 'http://www.icstools.it', + 'depends': ['procurement','sale',], + 'init_xml': [], + 'update_xml': ['stock_reord_rule_view.xml',], + 'demo_xml' : [], + 'data': ['cron_data.xml',], + 'images': [], + 'active': False, + 'installable': True, +} diff --git a/stock_reord_rule/cron_data.xml b/stock_reord_rule/cron_data.xml new file mode 100644 index 000000000..630c5f409 --- /dev/null +++ b/stock_reord_rule/cron_data.xml @@ -0,0 +1,15 @@ + + + + + Update quantity for reordering rules + 1 + days + -1 + + + + + + + diff --git a/stock_reord_rule/security/ir.model.access.csv b/stock_reord_rule/security/ir.model.access.csv new file mode 100644 index 000000000..0a6c192eb --- /dev/null +++ b/stock_reord_rule/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" + diff --git a/stock_reord_rule/stock_reord_rule.py b/stock_reord_rule/stock_reord_rule.py new file mode 100644 index 000000000..ab42a81ae --- /dev/null +++ b/stock_reord_rule/stock_reord_rule.py @@ -0,0 +1,57 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Automatic Stock Procurement by days for OpenERP +# Copyright (C) 2012 Sergio Corato () +# +# 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 . +# +############################################################################## + +from osv import osv, fields + +class stock_warehouse_orderpoint(osv.osv): + _inherit = "stock.warehouse.orderpoint" + + def _qty_orderpoint_days(self, cr, uid, ids, context=None): + """Calculate quantity to create warehouse stock for n days of sales. + integer (( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)""" + + res = {} + obj_product = self.pool.get('product.product') + product_ids = tuple(obj_product.search(cr, uid, [])) + if len(product_ids) > 1: + sql= """SELECT sol.product_id AS product_id, (sum( product_uos_qty )/pp.days_stats*(1+pp.forecast_gap/100) * pp.days_warehouse) AS quantity FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id JOIN product_product pp ON pp.id = sol.product_id WHERE sol.state in ('done','confirmed') AND sol.product_id IN {product_ids} AND date_order > (date(now()) - pp.days_stats) GROUP BY sol.product_uom, sol.product_id, pp.days_stats, pp.forecast_gap, pp.days_warehouse;""".format(product_ids=product_ids) + cr.execute(sql) + sql_res = cr.fetchall() + for val in sql_res: + if val: + reord_rules_ids = self.search(cr, uid, [('product_id','=', val[0])]) + if reord_rules_ids: + res['product_max_qty'] = val[1] + self.write(cr, uid, reord_rules_ids, res) + return True + +stock_warehouse_orderpoint() + +class product_product(osv.osv): + _inherit = "product.product" + + _columns = { + 'days_warehouse': fields.integer('Nr of days of warehouse stock'), + 'days_stats':fields.integer('Nr of days on which calculate stats'), + 'forecast_gap':fields.float('Forecast gap%', digits=(6,int(3))), + } + +product_product() diff --git a/stock_reord_rule/stock_reord_rule_view.xml b/stock_reord_rule/stock_reord_rule_view.xml new file mode 100644 index 000000000..d832da340 --- /dev/null +++ b/stock_reord_rule/stock_reord_rule_view.xml @@ -0,0 +1,22 @@ + + + + + + product.normal.form_procurement_by_days + product.product + + + + + + + + + + + + + + + From ea20e8120c5baf652e1afe3e99ba8747313e6ea6 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Feb 2013 23:21:19 +0100 Subject: [PATCH 2/4] [FIX] Code and description review --- stock_reord_rule/__openerp__.py | 15 +++++--- stock_reord_rule/stock_reord_rule.py | 41 +++++++++++----------- stock_reord_rule/stock_reord_rule_view.xml | 6 ++-- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/stock_reord_rule/__openerp__.py b/stock_reord_rule/__openerp__.py index e288e4a48..ab864702f 100644 --- a/stock_reord_rule/__openerp__.py +++ b/stock_reord_rule/__openerp__.py @@ -21,17 +21,22 @@ { 'name': 'Improved reordering rules', - 'version': '0.1', + 'version': '0.2', 'category': 'Tools', 'description': """ - This module allows to improve reordering rules of stock module.""", + This module allows to improve reordering rules of stock module. + + It works forecasting the stock needed per product for n days of sales, with the next formula: + (( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse) + where: + - days_stats = days on wich calculate sales stats; + - forecast_gap = forecast of increase/decrease on sales (%); + - days_warehouse = days of stock to keep in the warehouse.""", 'author': 'Sergio Corato', 'website': 'http://www.icstools.it', 'depends': ['procurement','sale',], - 'init_xml': [], - 'update_xml': ['stock_reord_rule_view.xml',], 'demo_xml' : [], - 'data': ['cron_data.xml',], + 'data': ['stock_reord_rule_view.xml','cron_data.xml',], 'images': [], 'active': False, 'installable': True, diff --git a/stock_reord_rule/stock_reord_rule.py b/stock_reord_rule/stock_reord_rule.py index ab42a81ae..57682073d 100644 --- a/stock_reord_rule/stock_reord_rule.py +++ b/stock_reord_rule/stock_reord_rule.py @@ -19,39 +19,38 @@ # ############################################################################## -from osv import osv, fields +from openerp.osv import orm, fields -class stock_warehouse_orderpoint(osv.osv): +class stock_warehouse_orderpoint(orm.Model): _inherit = "stock.warehouse.orderpoint" def _qty_orderpoint_days(self, cr, uid, ids, context=None): """Calculate quantity to create warehouse stock for n days of sales. - integer (( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)""" + (( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)""" - res = {} obj_product = self.pool.get('product.product') - product_ids = tuple(obj_product.search(cr, uid, [])) - if len(product_ids) > 1: - sql= """SELECT sol.product_id AS product_id, (sum( product_uos_qty )/pp.days_stats*(1+pp.forecast_gap/100) * pp.days_warehouse) AS quantity FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id JOIN product_product pp ON pp.id = sol.product_id WHERE sol.state in ('done','confirmed') AND sol.product_id IN {product_ids} AND date_order > (date(now()) - pp.days_stats) GROUP BY sol.product_uom, sol.product_id, pp.days_stats, pp.forecast_gap, pp.days_warehouse;""".format(product_ids=product_ids) - cr.execute(sql) - sql_res = cr.fetchall() - for val in sql_res: + product_ids = tuple(obj_product.search(cr, uid, [], context=context)) + sql= """SELECT sol.product_id AS product_id, + (sum( product_uos_qty )/pp.days_stats*(1+pp.forecast_gap/100) * pp.days_warehouse) + AS quantity FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id + JOIN product_product pp ON pp.id = sol.product_id WHERE sol.state in ('done','confirmed') + AND sol.product_id IN %s AND date_order > (date(now()) - pp.days_stats) + GROUP BY sol.product_uom, sol.product_id, pp.days_stats, pp.forecast_gap, + pp.days_warehouse;""" + cr.execute(sql, (product_ids,)) + sql_res = cr.fetchall() + for val in sql_res: if val: - reord_rules_ids = self.search(cr, uid, [('product_id','=', val[0])]) + reord_rules_ids = self.search(cr, uid, [('product_id', '=', val[0])], context=context) if reord_rules_ids: - res['product_max_qty'] = val[1] - self.write(cr, uid, reord_rules_ids, res) + self.write(cr, uid, reord_rules_ids, {'product_max_qty': val[1]}, context=context) return True -stock_warehouse_orderpoint() - -class product_product(osv.osv): +class product_product(orm.Model): _inherit = "product.product" _columns = { - 'days_warehouse': fields.integer('Nr of days of warehouse stock'), - 'days_stats':fields.integer('Nr of days on which calculate stats'), - 'forecast_gap':fields.float('Forecast gap%', digits=(6,int(3))), + 'days_warehouse': fields.integer('Days of needed warehouse stock'), + 'days_stats': fields.integer('Days of sale statistics'), + 'forecast_gap': fields.float('Expected sales variation (percent +/-)', digits=(6,3)), } - -product_product() diff --git a/stock_reord_rule/stock_reord_rule_view.xml b/stock_reord_rule/stock_reord_rule_view.xml index d832da340..9d347a529 100644 --- a/stock_reord_rule/stock_reord_rule_view.xml +++ b/stock_reord_rule/stock_reord_rule_view.xml @@ -9,9 +9,9 @@ - - - + + + From 6723007a541217a6862374973d343fdc0d44f605 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Feb 2013 21:58:33 +0100 Subject: [PATCH 3/4] [FIX] Improved SQL query to search only product of type product --- stock_reord_rule/stock_reord_rule.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stock_reord_rule/stock_reord_rule.py b/stock_reord_rule/stock_reord_rule.py index 57682073d..f5441ddec 100644 --- a/stock_reord_rule/stock_reord_rule.py +++ b/stock_reord_rule/stock_reord_rule.py @@ -33,13 +33,16 @@ class stock_warehouse_orderpoint(orm.Model): sql= """SELECT sol.product_id AS product_id, (sum( product_uos_qty )/pp.days_stats*(1+pp.forecast_gap/100) * pp.days_warehouse) AS quantity FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id - JOIN product_product pp ON pp.id = sol.product_id WHERE sol.state in ('done','confirmed') + JOIN product_product pp ON pp.id = sol.product_id + JOIN product_template pt ON pt.id = pp.product_tmpl_id + WHERE sol.state in ('done','confirmed') AND pt.type = 'product' AND sol.product_id IN %s AND date_order > (date(now()) - pp.days_stats) GROUP BY sol.product_uom, sol.product_id, pp.days_stats, pp.forecast_gap, pp.days_warehouse;""" cr.execute(sql, (product_ids,)) sql_res = cr.fetchall() - for val in sql_res: + if sql_res: + for val in sql_res: if val: reord_rules_ids = self.search(cr, uid, [('product_id', '=', val[0])], context=context) if reord_rules_ids: From 5775e3c8c99686fbc1bf4d7696528da7e159227b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Feb 2013 22:34:58 +0100 Subject: [PATCH 4/4] [IMP] Improved usage description --- stock_reord_rule/__openerp__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stock_reord_rule/__openerp__.py b/stock_reord_rule/__openerp__.py index ab864702f..75adefbde 100644 --- a/stock_reord_rule/__openerp__.py +++ b/stock_reord_rule/__openerp__.py @@ -31,7 +31,15 @@ where: - days_stats = days on wich calculate sales stats; - forecast_gap = forecast of increase/decrease on sales (%); - - days_warehouse = days of stock to keep in the warehouse.""", + - days_warehouse = days of stock to keep in the warehouse. + + Usage: + insert days_stats, forecast_gap and days_warehouse vars in product form + and create a reordering rule for the same product, without inserting nothing (neither maximum or + minimum quantity are required). The cron job will be executed daily and will update the maximum + quantity in the reordering rule (you can force it to start changing the date and hour of + execution). + This module doesn't need purchase module to work, but it's useful with that module.'""", 'author': 'Sergio Corato', 'website': 'http://www.icstools.it', 'depends': ['procurement','sale',],