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

Proposed by gpa(OpenERP)
Status: Merged
Merged at revision: 76
Proposed branch: lp:~openbig/bigconsulting/order_point_calcultor
Merge into: lp:bigconsulting
Diff against target: 445 lines (+232/-132)
4 files modified
packing_barcode_check/packing_barcode_check.py (+2/-2)
packing_barcode_check/wizard/select_picking.py (+1/-0)
stock_minimum_calculator/stock_minimum_calculator_wizard.xml (+1/-1)
stock_minimum_calculator/wizard/stock_order_point_calculator.py (+228/-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+32844@code.launchpad.net

This proposal supersedes 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.

Preview Diff

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

Subscribers

People subscribed via source and target branches