Merge lp:~openbig/bigconsulting/order_point_calcultor into lp:bigconsulting

Proposed by gpa(OpenERP)
Status: Superseded
Proposed branch: lp:~openbig/bigconsulting/order_point_calcultor
Merge into: lp:bigconsulting
Diff against target: 419 lines (+230/-130)
2 files modified
stock_minimum_calculator/stock_minimum_calculator_wizard.xml (+1/-1)
stock_minimum_calculator/wizard/stock_order_point_calculator.py (+229/-129)
To merge this branch: bzr merge lp:~openbig/bigconsulting/order_point_calcultor
Reviewer Review Type Date Requested Status
openbig Pending
Review via email: mp+32754@code.launchpad.net

This proposal supersedes a proposal from 2010-08-16.

This proposal has been superseded by a proposal from 2010-08-16.

Description of the change

improvement in order point calculator wizard bug 615927

To post a comment you must log in.
81. By gpa(OpenERP)

optimization in stock order point wizard

82. By gpa(OpenERP)

improvement for automatically increment in scanned quantity and real quantity

83. By gpa(OpenERP)

changes in first condition of date

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'stock_minimum_calculator/stock_minimum_calculator_wizard.xml'
2--- stock_minimum_calculator/stock_minimum_calculator_wizard.xml 2010-07-27 09:27:59 +0000
3+++ stock_minimum_calculator/stock_minimum_calculator_wizard.xml 2010-08-16 12:42:42 +0000
4@@ -9,7 +9,7 @@
5 id="id_set_seasonal_trend"/>
6 <wizard
7 string="Order Point Cacluator"
8- model="product.product"
9+ model="res.partner"
10 name="orderpoint.calculator"
11 menu="True"
12 id="id_order_point_cacluator"/>
13
14=== modified file 'stock_minimum_calculator/wizard/stock_order_point_calculator.py'
15--- stock_minimum_calculator/wizard/stock_order_point_calculator.py 2010-08-04 13:12:02 +0000
16+++ stock_minimum_calculator/wizard/stock_order_point_calculator.py 2010-08-16 12:42:42 +0000
17@@ -34,13 +34,9 @@
18 <field name="warehouse_id"/>
19 <field name="location_id"/>
20 <field name="seasonal_id"/>
21+ <field name="max_qty_factor"/>
22 <field name="delactive"/>
23 <field name="method_qty_calculation" colspan="4"/>
24- <group attrs="{'invisible':[('method_qty_calculation','=','his_cons_without_product')]}" colspan="4">
25- <separator string="Configuration of Base Consumptions Periods" colspan="4"/>
26- <field name="date_start"/>
27- <field name="date_stop"/>
28- </group>
29 </form>"""
30
31 orderpoint_calculator_fields = {
32@@ -70,24 +66,19 @@
33 'required': True,
34 'help':'Use this field as index. Reference index is Main Season with 1.00. If you want to change the orderpoint rules you are able to define new season with a seasonal index. This index should reference to 1.00. For example if you expect - 10% consumption for autumn season in comparition to main season enter 0.90.'
35 },
36-
37- 'date_start': {
38- 'string':'From Date',
39- 'type':'date'
40- },
41-
42- 'date_stop': {
43- 'string':'To Date',
44- 'type':'date',
45- 'default': lambda *a: time.strftime('%Y-%m-%d')
46+
47+ 'max_qty_factor': {
48+ 'string':'Max. Qty. Factor',
49+ 'type':'float',
50+ 'required': True
51 },
52
53 'method_qty_calculation':{
54 'string':"Method Calculation",
55 'type':'selection',
56- 'selection':[('his_cons_with_product', 'Calculate with historical consumptions of products'),
57+ 'selection':[('his_cons_with_product', 'Calculate with planning values'),
58 ('his_cons_without_product', 'Calculate with Out historical consumptions of product'),
59- ('hist_cons_and_open_sale', 'Calculate with historical consumptions of product and Open sales orders'),
60+ ('hist_cons_and_open_sale', 'Calculate with historical values'),
61 ],'required': True,
62 'default': lambda *a:'his_cons_with_product'
63 },
64@@ -116,16 +107,99 @@
65
66 return {'warehouse_id': warehouse_id, 'location_id':location_id,'date_start':from_date,'seasonal_id':seasonal_id}
67
68+
69+def _sale_consume_qty(self, cr, uid, product_id, from_date, to_date, state, context):
70+ """calculation of consumption of product from_date to to_date , where state is done """
71+
72+ pool = pooler.get_pool(cr.dbname)
73+
74+ sale_obj = pool.get('sale.order')
75+ sale_line_obj = pool.get("sale.order.line")
76+ consum_qty = 0.0
77+ sale_ids = sale_obj.search(cr,uid,[('date_order','>=',from_date),('date_order','<=',to_date), ('state','=',state)])
78+ if sale_ids:
79+ for sid in sale_ids:
80+ sale_line_id = sale_line_obj.search(cr,uid,[('order_id','=',sid),('product_id','=',product_id)])
81+ for line_id in sale_line_id:
82+ sale_line_data= sale_line_obj.browse(cr, uid, line_id, context=context).product_uom_qty
83+ consum_qty += sale_line_data
84+
85+ return consum_qty
86+
87+def _sale_open_qty(self, cr, uid, product_id, state, context):
88+ """Calculation of open sale orders"""
89+
90+ pool = pooler.get_pool(cr.dbname)
91+
92+ sale_obj = pool.get('sale.order')
93+ sale_line_obj = pool.get("sale.order.line")
94+ open_sale_qty = 0.0
95+ open_sale_ids = sale_obj.search(cr, uid, [('state','in',state)])
96+ if open_sale_ids:
97+ for sale_id in open_sale_ids:
98+ sale_open_line_ids = sale_line_obj.search(cr,uid,[('order_id','=',sale_id),('product_id','=',product_id)])
99+ for open_line_id in sale_open_line_ids:
100+ sale_line_data= sale_line_obj.browse(cr,uid,open_line_id)
101+ open_sale_qty += sale_line_data.product_uom_qty
102+
103+ return open_sale_qty
104+
105+def _purchase_open_qty(self, cr, uid, product_id, state, context):
106+ """ Calculation of open purchase orders """
107+
108+ pool = pooler.get_pool(cr.dbname)
109+
110+ purchase_obj = pool.get('purchase.order')
111+ purchase_order_line_obj = pool.get('purchase.order.line')
112+ open_purchase_qty = 0.0
113+ open_purchase_ids = purchase_obj.search(cr,uid,[('state','=',state)])
114+ if open_purchase_ids:
115+ for purchase_id in open_purchase_ids:
116+ open_purchase_line_ids= purchase_order_line_obj.search(cr,uid,[('order_id','=',purchase_id),('product_id','=',product_id)])
117+ for open_pur_id in open_purchase_line_ids:
118+ purchase_line_data = purchase_order_line_obj.browse(cr, uid, open_pur_id, context=context)
119+ open_purchase_qty += purchase_line_data.product_qty
120+
121+ return open_purchase_qty
122+
123+def _refund_qty(self, cr, uid, product_id, from_date, to_date, state, context):
124+ """ Calculation of the refund quantities of the prduct from the refund invoice, between from_date, to_date"""
125+
126+ pool = pooler.get_pool(cr.dbname)
127+
128+ account_invoice_obj = pool.get('account.invoice')
129+ account_invoice_line_obj = pool.get('account.invoice.line')
130+ refund_qty = 0.0
131+ refund_invoice_ids = account_invoice_obj.search(cr,uid,[('type','in',state),('date_invoice','>=',from_date),('date_invoice','<=',to_date)])
132+ for refund_id in refund_invoice_ids:
133+ invoie_line_ids = account_invoice_line_obj.search(cr,uid,[('invoice_id','=',refund_id),('product_id','=',product_id)])
134+ for invoice_line_id in invoie_line_ids:
135+ inovice_line_data = account_invoice_line_obj.browse(cr, uid, invoice_line_id, context=context).quantity
136+ refund_qty += inovice_line_data
137+
138+ return refund_qty
139+
140+
141+def _date_diff(self, cr, uid, product_id, from_date, to_date, context):
142+ """ Date difference between from_date and to_date """
143+
144+ first_date = time.mktime(time.strptime(from_date,'%Y-%m-%d %H:%M:%S'))
145+ last_date = time.mktime(time.strptime(to_date,'%Y-%m-%d %H:%M:%S'))
146+ diff_day = (last_date-first_date)/(3600*24)
147+ return diff_day
148+
149 def _do_calculate(self, cr, uid, data, context):
150 pool = pooler.get_pool(cr.dbname)
151
152 purchase_obj = pool.get('purchase.order')
153- sale_obj = pool.get('sale.order')
154+ purchase_order_line_obj = pool.get('purchase.order.line')
155 product_obj = pool.get('product.product')
156 supp_obj = pool.get('product.supplierinfo')
157 supp_cost_opt_obj = pool.get('supplier.cost.opti.lead.time')
158 min_rule_obj = pool.get('stock.warehouse.orderpoint')
159 seasonal_obj = pool.get('product.seasonal')
160+ stock_move_obj = pool.get('stock.move')
161+
162 ##### Calculation of purchase lead time
163 purchase_lead_time = 0.0
164 company = pool.get('res.users').browse(cr, uid, uid, context).company_id
165@@ -135,117 +209,143 @@
166 seasonal_id = data['form']['seasonal_id']
167 seasonal_data = seasonal_obj.browse(cr, uid, seasonal_id)
168 seasonal_factor = seasonal_data.seasonal_factor
169-
170- ##### Calculation of supplier lead time
171-
172- for id in data['ids']:
173- product_id = product_obj.browse(cr, uid, id, context)
174- supplier_lead_time = 0.0
175- if product_id:
176- for supp in product_id.seller_ids:
177- sup = supp_obj.browse(cr, uid, supp.id, context)
178- supplier_lead_time = sup.delay
179-
180- ###### Calculation of average_daily_consumption
181- date_start = data['form']['date_start']
182- date_stop = data['form']['date_stop']
183- average_daily_consumption = 0.0
184- sale_line_obj = pool.get("sale.order.line")
185- consum_qty = 0.0
186- sale_ids = sale_obj.search(cr,uid,[('date_order','>=',date_start),('date_order','<=',date_stop)])
187- if sale_ids:
188- for sid in sale_ids:
189- sale_line_id = sale_line_obj.search(cr,uid,[('order_id','=',sid),('product_id','=',product_id.id)])
190- for line_id in sale_line_id:
191- sale_line_data= sale_line_obj.browse(cr,uid,line_id)
192- consum_qty += sale_line_data.product_uom_qty
193- ###here consume quantity is total of selected product
194- ##################calculation of date difference between first and last sale order
195- sale_ids = sale_obj.search(cr,uid,[('date_order','>=',date_start),('date_order','<=',date_stop)], order='date_order asc')
196- if sale_ids:
197- start_sale_id = sale_ids[0]
198- last_sale_id = sale_ids[-1]
199- first_sale_date = sale_obj.browse(cr,uid,start_sale_id).date_order
200- last_sale_date = sale_obj.browse(cr,uid,last_sale_id).date_order
201- first_date = time.mktime(time.strptime(first_sale_date,'%Y-%m-%d'))
202- last_date = time.mktime(time.strptime(last_sale_date,'%Y-%m-%d'))
203- diff_day = (last_date-first_date)/(3600*24)
204- if diff_day > 0:
205- average_daily_consumption = consum_qty / diff_day
206- ###### Calculation of plan_average_daily_consumption
207- plan_average_daily_consumption = 0.0
208- if product_id.plan_avg_consume:
209- plan_average_daily_consumption = product_id.plan_avg_consume
210-
211- ####### get location #####
212- location_id = data['form']['location_id']
213-
214- ########## calculation historical consumption of products and open sales orders“
215- ### total sale qty with open state
216-
217- sale_qty = 0.0
218- open_sale_ids = sale_obj.search(cr, uid, [('state','in',['manual','progress'])])
219- if sale_ids:
220- for sale_id in open_sale_ids:
221- sale_open_line_ids= sale_line_obj.search(cr,uid,[('order_id','=',sale_id),('product_id','=',product_id.id)])
222- for open_line_id in sale_open_line_ids:
223- sale_line_data= sale_line_obj.browse(cr,uid,open_line_id)
224- sale_qty += sale_line_data.product_uom_qty
225- ###################
226- purchase_ids = purchase_obj.search(cr,uid,[])
227- if purchase_ids:
228- start_pur_id = min(purchase_ids)
229- last_pur_id = max(purchase_ids)
230-
231- ##################calculation of date difference between first and last purchase order
232- first_purchase_date = purchase_obj.browse(cr,uid,start_pur_id).date_order
233- last_purchase_date = purchase_obj.browse(cr,uid,last_pur_id).date_order
234- first_date = time.mktime(time.strptime(first_purchase_date,'%Y-%m-%d'))
235- last_date = time.mktime(time.strptime(last_purchase_date,'%Y-%m-%d'))
236- pur_diff_day = (last_date-first_date)/(3600*24)
237- product_max_qty = 0.0
238- product_min_qty = 0.0
239-
240- if data['form']['method_qty_calculation'] == 'his_cons_with_product':
241- product_min_qty = int(math.ceil(average_daily_consumption * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
242- product_max_qty = 2 * product_min_qty
243-
244- elif data['form']['method_qty_calculation'] == 'his_cons_without_product':
245- product_min_qty = int(math.ceil(plan_average_daily_consumption * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
246- product_max_qty = 2 * product_min_qty
247-
248- else:
249- try:
250- product_min_qty = int(math.ceil((consum_qty + sale_qty) / pur_diff_day * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
251- except Exception,e:
252- product_min_qty = 0.0
253- product_max_qty = 2 * product_min_qty
254-
255- if data['form']['delactive']:
256- stock_rule_ids = min_rule_obj.search(cr,uid,[('warehouse_id','=',data['form']['warehouse_id']),('product_id','=',product_id.id)])
257- for stock_rule_id in stock_rule_ids:
258- min_rule_obj.unlink(cr, uid, [stock_rule_id],context=context)
259-
260- mini_stock_rule_id = min_rule_obj.search(cr,uid,[('warehouse_id','=',data['form']['warehouse_id']),('product_id','=',product_id.id)])
261- if mini_stock_rule_id:
262- for stock_rule_id in mini_stock_rule_id:
263- min_rule_obj.write(cr, uid, [stock_rule_id],{'product_min_qty':product_min_qty,'product_max_qty':product_max_qty,},context=context)
264- else:
265- name = pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint.calculator')
266- pool.get('stock.warehouse.orderpoint').create(cr, uid, {
267- 'name': name,
268- 'active': True,
269- 'warehouse_id': data['form']['warehouse_id'],
270- 'location_id': location_id,
271- 'product_id': id,
272- 'product_min_qty': product_min_qty,
273- 'product_max_qty': product_max_qty,
274- 'qty_multiple':1,
275- 'product_uom':product_id.uom_id.id,
276- 'logic':'max'
277- } )
278-
279-
280+
281+ # max_qty_factor factor
282+ max_qty_factor = data['form']['max_qty_factor']
283+
284+ # 1. Calculation of the date date start in one year later than current date
285+ from_date = (datetime.now() - relativedelta(months=12)).strftime('%Y-%m-%d %H:%M:%S')
286+ to_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
287+
288+ # 2.1 first find the all the product which fullfill the criteria of the 2.1
289+ product_ids = product_obj.search(cr,uid,[('active','=',True),('type','=','product'),
290+ ('procure_method','=','make_to_stock'),
291+ ('supply_method','=','buy'),
292+ ('purchase_ok','=',True),
293+ ('state','not in',['end','obsolete']),
294+ ])
295+ ####### get location #####
296+ location_id = data['form']['location_id']
297+ ###########
298+
299+ #2. Second point find the products for the perticular suppliers
300+ for partner_id in data['ids']:
301+ filter_products = []
302+ for product_id in product_ids:
303+ product_data = product_obj.browse(cr, uid, product_id, context=context)
304+ for seller_id in product_data.seller_ids:
305+ if seller_id.name.id == partner_id:
306+ filter_products.append(product_id)
307+
308+ #3. Calculation of the first stock input date as first input
309+ for filter_product_id in filter_products:
310+ product_data = pool.get('product.product').browse(cr, uid, filter_product_id, context=context)
311+ supplier_lead_time = 0.0
312+
313+ # supplier_lead_time of the supplier delay
314+ for seller_id in product_data.seller_ids:
315+ if seller_id.name.id == partner_id:
316+ supplier_lead_time = seller_id.delay
317+
318+ stock_move_ids = stock_move_obj.search(cr, uid, [('product_id','=',filter_product_id)])
319+
320+ average_daily_consumption = 0.0
321+ first_stock_move_ids = []
322+ # Take the first input date of the stock move where stock move of incoming picking or stock move not have any picking
323+ for first_stock_id in stock_move_ids:
324+ first_data = stock_move_obj.browse(cr, uid, first_stock_id, context=context)
325+ if first_data.picking_id:
326+ if first_data.picking_id.type == 'in':
327+ first_stock_move_ids.append(first_stock_id)
328+ else:
329+ first_stock_move_ids.append(first_stock_id)
330+
331+ if first_stock_move_ids:
332+ first_stock_move_id = first_stock_move_ids[0]
333+ first_input = stock_move_obj.browse(cr, uid, first_stock_move_id, context=context).date_planned
334+
335+ # Case 1 if first input is less than from date
336+ if first_input < from_date:
337+ consum_qty = _sale_consume_qty(self, cr, uid, from_date, to_date, 'done', context)
338+ refund_qty = _refund_qty(self, cr, uid, filter_product_id, from_date, to_date, ['out_refund','in_refund'], context)
339+ open_sale_qty = _sale_open_qty(self, cr, uid, filter_product_id, from_date, to_date, ['manual','progress'], context)
340+ open_purchase_qty = _purchase_open_qty(self, cr, uid, filter_product_id, 'approved', context)
341+ pur_diff_day = _date_diff(self, cr, uid, filter_product_id, from_date, to_date, context)
342+ ### calculation of average_daily_consumption
343+ if pur_diff_day > 0:
344+ average_daily_consumption = (consum_qty - refund_qty + open_sale_qty - open_purchase_qty) / pur_diff_day
345+
346+ elif from_date < first_input and first_input < to_date:
347+ first_date = first_input
348+ consum_qty = _sale_consume_qty(self, cr, uid, filter_product_id, first_date, to_date, 'done', context)
349+ refund_qty = _refund_qty(self, cr, uid, filter_product_id, first_date, to_date, ['out_refund','in_refund'], context)
350+ open_sale_qty = _sale_open_qty(self, cr, uid, filter_product_id, ['manual','progress'], context)
351+ open_purchase_qty = _purchase_open_qty(self, cr, uid, filter_product_id, 'approved', context)
352+ pur_diff_day = _date_diff(self, cr, uid, filter_product_id, first_date, to_date, context)
353+
354+ ### calculation of average_daily_consumption
355+ if pur_diff_day>0:
356+ average_daily_consumption = (consum_qty - refund_qty + open_sale_qty - open_purchase_qty) / pur_diff_day
357+ else:
358+ open_sale_qty = _sale_open_qty(self, cr, uid, filter_product_id, ['manual','progress'], context)
359+ open_purchase_qty = _purchase_open_qty(self, cr, uid, filter_product_id, 'approved', context)
360+
361+ # calculation of first open purchase order date
362+ open_purchase_ids = purchase_obj.search(cr,uid,[('state','=','approved')])
363+ first_open_purchase_order = min(open_purchase_ids)
364+ fst_open_pur_date = purchase_obj.browse(cr, uid, first_open_purchase_order, context=context).date_order
365+
366+ # Days difference between to date and first purchase date
367+ pur_to_date = time.mktime(time.strptime(to_date,'%Y-%m-%d %H:%M:%S'))
368+ pur_open_date = time.mktime(time.strptime(fst_open_pur_date,'%Y-%m-%d'))
369+ pur_diff_day = (pur_to_date-pur_open_date)/(3600*24)
370+
371+ if pur_diff_day>0:
372+ average_daily_consumption = (open_sale_qty - open_purchase_qty)/pur_diff_day
373+
374+ ###### Calculation of plan_average_daily_consumption
375+ plan_average_daily_consumption = 0.0
376+ plan_average_daily_consumption = product_data.plan_avg_consume
377+
378+ if data['form']['method_qty_calculation'] == 'his_cons_with_product':
379+ product_min_qty = int(math.ceil(average_daily_consumption * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
380+ product_max_qty = max_qty_factor * product_min_qty
381+
382+ elif data['form']['method_qty_calculation'] == 'his_cons_without_product':
383+ product_min_qty = int(math.ceil(plan_average_daily_consumption * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
384+ product_max_qty = max_qty_factor * product_min_qty
385+
386+ else:
387+ try:
388+ product_min_qty = int(math.ceil((consum_qty + open_sale_qty) / pur_diff_day * (supplier_lead_time + purchase_lead_time) * seasonal_factor))
389+ except Exception,e:
390+ product_min_qty = 0.0
391+ product_max_qty = max_qty_factor * product_min_qty
392+
393+ if data['form']['delactive']:
394+ stock_rule_ids = min_rule_obj.search(cr,uid,[('warehouse_id','=',data['form']['warehouse_id']),('product_id','=',filter_product_id)])
395+ for stock_rule_id in stock_rule_ids:
396+ min_rule_obj.unlink(cr, uid, [stock_rule_id],context=context)
397+
398+ mini_stock_rule_id = min_rule_obj.search(cr,uid,[('warehouse_id','=',data['form']['warehouse_id']),('product_id','=',filter_product_id)])
399+ if mini_stock_rule_id:
400+ for stock_rule_id in mini_stock_rule_id:
401+ min_rule_obj.write(cr, uid, [stock_rule_id],{'product_min_qty':product_min_qty,'product_max_qty':product_max_qty,},context=context)
402+ else:
403+ name = pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint.calculator')
404+ pool.get('stock.warehouse.orderpoint').create(cr, uid, {
405+ 'name': name,
406+ 'active': True,
407+ 'warehouse_id': data['form']['warehouse_id'],
408+ 'location_id': location_id,
409+ 'product_id': filter_product_id,
410+ 'product_min_qty': product_min_qty,
411+ 'product_max_qty': product_max_qty,
412+ 'qty_multiple':1,
413+ 'product_uom':product_data.uom_id.id,
414+ 'logic':'price',
415+ } )
416+
417 return {}
418
419 def _get_message(self, cr, uid, data, context):

Subscribers

People subscribed via source and target branches