~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to procurement_cycle/scheduler.py

  • Committer: Quentin THEURET
  • Date: 2011-12-12 08:02:59 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: qt@tempo-consulting.fr-20111212080259-oul1f0g37hcpubyc
UF-641 [ADD] Added the empty purchase_followup module

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#
20
20
##############################################################################
21
21
 
22
 
from osv import osv
 
22
from osv import osv, fields
23
23
from datetime import datetime
24
24
from tools.translate import _
25
 
from mx.DateTime import RelativeDate
26
 
from mx.DateTime import RelativeDateTime
27
 
from mx.DateTime import now
28
 
from mx.DateTime import Parser
 
25
from mx.DateTime import *
29
26
 
30
27
import time
31
28
import pooler
37
34
    _name = 'procurement.order'
38
35
    _inherit = 'procurement.order'
39
36
    
40
 
    def run_automatic_cycle(self, cr, uid, use_new_cursor=False, batch_id=False, context=None):
 
37
    def run_automatic_cycle(self, cr, uid, use_new_cursor=False, context={}):
41
38
        '''
42
39
        Create procurement on fixed date
43
40
        '''
47
44
        request_obj = self.pool.get('res.request')
48
45
        cycle_obj = self.pool.get('stock.warehouse.order.cycle')
49
46
        proc_obj = self.pool.get('procurement.order')
 
47
        product_obj = self.pool.get('product.product')
50
48
        freq_obj = self.pool.get('stock.frequence')
51
49
 
52
 
        start_date = time.strftime('%Y-%m-%d %H:%M:%S')
 
50
        start_date = datetime.now()
53
51
        
54
 
        cycle_ids = cycle_obj.search(cr, uid, [('next_date', '<=', datetime.now())])
 
52
        cycle_ids = cycle_obj.search(cr, uid, [('next_date', '<=', start_date.strftime('%Y-%m-%d'))])
55
53
        
56
54
        created_proc = []
57
55
        report = []
58
56
        report_except = 0
59
 
        ran_proc = []
 
57
        
 
58
        # Cache for product/location
 
59
        cache = {}
 
60
 
60
61
        
61
62
        # We start with only category Automatic Supply
62
63
        for cycle in cycle_obj.browse(cr, uid, cycle_ids):
70
71
            d_values = {'leadtime': cycle.leadtime,
71
72
                        'coverage': cycle.order_coverage,
72
73
                        'safety_time': cycle.safety_stock_time,
73
 
                        'consumption_period_from': cycle.consumption_period_from,
74
 
                        'consumption_period_to': cycle.consumption_period_to,
 
74
                        'safety': cycle.safety_stock,
75
75
                        'past_consumption': cycle.past_consumption,
76
76
                        'reviewed_consumption': cycle.reviewed_consumption,
77
77
                        'manual_consumption': cycle.manual_consumption,}
78
78
 
79
 
            if cycle.product_ids:
80
 
                ran_proc.append(cycle.id)
81
 
                for line in cycle.product_ids:
82
 
                    # Update the safety stock according to the safety stock defined in the line
83
 
                    d_values.update({'safety_stock': line.safety_stock})
84
 
                    proc_id = self.create_proc_cycle(cr, uid, cycle, line.product_id.id, location_id, d_values, line)
 
79
            if not cycle.product_id:
 
80
                not_products = []
 
81
                for p in cycle.product_ids:
 
82
                    not_products.append(p.id)
 
83
 
 
84
                product_ids = product_obj.search(cr, uid, [('categ_id', 'child_of', cycle.category_id.id), ('id', 'not in', not_products)])
 
85
                
 
86
                for product in product_obj.browse(cr, uid, product_ids):
 
87
                    proc_id = self.create_proc_cycle(cr, uid, cycle, product.id, location_id, d_values, cache=cache)
 
88
                    
85
89
 
86
90
                    if proc_id:
87
91
                        created_proc.append(proc_id)
88
 
            
 
92
            else:
 
93
                proc_id = self.create_proc_cycle(cr, uid, cycle, cycle.product_id.id, location_id, d_values, cache=cache)
 
94
                
 
95
                if proc_id:
 
96
                    created_proc.append(proc_id)
 
97
        
89
98
            if cycle.frequence_id:
90
 
                freq_obj.write(cr, uid, cycle.frequence_id.id, {'last_run': datetime.now()})
 
99
                freq_obj.write(cr, uid, cycle.frequence_id.id, {'last_run': start_date.strftime('%Y-%m-%d')})
91
100
 
92
 
        created_doc = '''################################
93
 
Created documents : \n'''
94
101
                    
95
102
        for proc in proc_obj.browse(cr, uid, created_proc):
96
103
            if proc.state == 'exception':
98
105
                               (proc.id, proc.product_qty, proc.product_uom.name,
99
106
                                proc.product_id.name,))
100
107
                report_except += 1
101
 
            elif proc.purchase_id:
102
 
                created_doc += "    * %s => %s \n" % (proc.name, proc.purchase_id.name)
103
108
                
104
 
        end_date = time.strftime('%Y-%m-%d %H:%M:%S')
 
109
        end_date = datetime.now()
105
110
                
106
111
        summary = '''Here is the procurement scheduling report for Order Cycle
107
112
 
108
113
        Start Time: %s
109
114
        End Time: %s
110
 
        Total Rules processed: %d
 
115
        Total Procurements processed: %d
111
116
        Procurements with exceptions: %d
112
 
        \n %s \n Exceptions: \n'''% (start_date, end_date, len(ran_proc), report_except, len(created_proc) > 0 and created_doc or '')
 
117
        \n'''% (start_date, end_date, len(created_proc), report_except)
113
118
        summary += '\n'.join(report)
114
 
        if batch_id:
115
 
            self.pool.get('procurement.batch.cron').write(cr, uid, batch_id, {'last_run_on': time.strftime('%Y-%m-%d %H:%M:%S')})
116
 
            old_request = request_obj.search(cr, uid, [('batch_id', '=', batch_id), ('name', '=', 'Procurement Processing Report (Order cycle).')])
117
 
            request_obj.write(cr, uid, old_request, {'batch_id': False})
118
 
        
119
 
        request_obj.create(cr, uid,
120
 
                {'name': "Procurement Processing Report (Order cycle).",
 
119
        req_id = request_obj.create(cr, uid,
 
120
                {'name': "Procurement Processing Report.",
121
121
                 'act_from': uid,
122
122
                 'act_to': uid,
123
 
                 'batch_id': batch_id,
124
123
                 'body': summary,
125
124
                })
126
 
        # UF-952 : Requests should be in consistent state
127
 
#        if req_id:
128
 
#            request_obj.request_send(cr, uid, [req_id])
 
125
        if req_id:
 
126
            request_obj.request_send(cr, uid, [req_id])
129
127
        
130
128
        if use_new_cursor:
131
129
            cr.commit()
133
131
            
134
132
        return {}
135
133
    
136
 
    def create_proc_cycle(self, cr, uid, cycle, product_id, location_id, d_values=None, line=None, context=None):
 
134
    def create_proc_cycle(self, cr, uid, cycle, product_id, location_id, d_values={}, cache={}, context={}):
137
135
        '''
138
136
        Creates a procurement order for a product and a location
139
137
        '''
141
139
        cycle_obj = self.pool.get('stock.warehouse.order.cycle')
142
140
        product_obj = self.pool.get('product.product')
143
141
        wf_service = netsvc.LocalService("workflow")
 
142
        report = []
144
143
        proc_id = False
145
 
       
146
 
        if context is None:
147
 
            context = {}
148
 
        if d_values is None:
149
 
            d_values = {}
150
 
 
 
144
        
151
145
        if isinstance(product_id, (int, long)):
152
146
            product_id = [product_id]
153
 
            
154
 
        if d_values.get('past_consumption', False):
155
 
            # If the AMC should be used, compute the period of calculation
156
 
            if not d_values.get('consumption_period_from', False):
157
 
                order_coverage = d_values.get('coverage', 3)
158
 
                d_values.update({'consumption_period_from': (now() + RelativeDate(day=1, months=-round(order_coverage, 1)+1)).strftime('%Y-%m-%d')})
159
 
            if not d_values.get('consumption_period_to', False):
160
 
                d_values.update({'consumption_period_to': (now() + RelativeDate(days=-1, day=1, months=1)).strftime('%Y-%m-%d')})
161
 
            context.update({'from_date': d_values.get('consumption_period_from'), 'to_date': d_values.get('consumption_period_to')})
162
 
        
163
 
        product = product_obj.browse(cr, uid, product_id[0], context=context)
164
 
 
165
 
        newdate = datetime.today().strftime('%Y-%m-%d %H:%M:%S')
166
 
        if line and line.required_date:
167
 
            newdate = line.required_date
168
 
 
169
 
        quantity_to_order = self._compute_quantity(cr, uid, cycle, product, location_id, d_values, context=context)
170
 
            
171
 
        # Create a procurement only if the quantity to order is more than 0.00
172
 
        if quantity_to_order <= 0.00:
173
 
            return False
174
 
        else:
175
 
            proc_id = proc_obj.create(cr, uid, {
176
 
                                    'name': _('Procurement cycle: %s') % (cycle.name,),
177
 
                                    'origin': cycle.name,
178
 
                                    'date_planned': newdate,
179
 
                                    'product_id': product.id,
180
 
                                    'product_qty': quantity_to_order,
181
 
                                    'product_uom': product.uom_id.id,
182
 
                                    'location_id': location_id,
183
 
                                    'procure_method': 'make_to_order',
184
 
            })
185
 
            # Confirm the procurement order
186
 
            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
187
 
            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
188
 
            context.update({'button': 'scheduler'})
189
 
            cycle_obj.write(cr, uid, [cycle.id], {'procurement_id': proc_id}, context=context)
 
147
        
 
148
        product = product_obj.browse(cr, uid, product_id[0])
 
149
        
 
150
        # Enter the stock location in cache to know which products has been already replenish for this location
 
151
        if not cache.get(location_id, False):
 
152
            cache.update({location_id: []})
 
153
        
 
154
        # If a rule already exist for the category of the product or for the product
 
155
        # itself for the same location, we don't create a procurement order
 
156
        #cycle_ids = cycle_obj.search(cr, uid, [('category_id', '=', product.categ_id.id), ('product_id', '=', False), ('location_id', '=', location_id), ('id', '!=', cycle.id)])
 
157
        #cycle2_ids = cycle_obj.search(cr, uid, [('product_id', '=', product.id), ('location_id', '=', location_id), ('id', '!=', cycle.id)])
 
158
        #if cycle_ids:
 
159
        #    cr.execute('''SELECT order_cycle_id
 
160
        #                FROM order_cycle_product_rel
 
161
        #                WHERE order_cycle_id in %s
 
162
        #                AND product_id = %s''', (tuple(cycle_ids), product.id))
 
163
        #    res = cr.fetchall()
 
164
        #    for r in res:
 
165
        #        cycle_ids.remove(r[0])
 
166
        #if cycle2_ids or cycle_ids:
 
167
        #    return False
 
168
        
 
169
            
 
170
        if product.id not in cache.get(location_id):
 
171
            newdate = datetime.today()
 
172
            quantity_to_order = self._compute_quantity(cr, uid, cycle, product.id, location_id, d_values)
 
173
                
 
174
            if quantity_to_order <= 0:
 
175
                return False
 
176
            else:
 
177
                proc_id = proc_obj.create(cr, uid, {
 
178
                                        'name': _('Automatic Supply: %s') % (cycle.name,),
 
179
                                        'origin': cycle.name,
 
180
                                        'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
 
181
                                        'product_id': product.id,
 
182
                                        'product_qty': quantity_to_order,
 
183
                                        'product_uom': product.uom_id.id,
 
184
                                        'location_id': location_id,
 
185
                                        'procure_method': 'make_to_order',
 
186
                })
 
187
                wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
 
188
                wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
 
189
                context.update({'button': 'scheduler'})
 
190
                cycle_obj.write(cr, uid, [cycle.id], {'procurement_id': proc_id}, context=context)
 
191
            
 
192
            # Fill the cache
 
193
            cache.get(location_id).append(product.id)
190
194
        
191
195
        return proc_id
192
196
    
193
 
    def _compute_quantity(self, cr, uid, cycle_id, product, location_id, d_values=None, context=None):
 
197
    def _compute_quantity(self, cr, uid, cycle_id, product_id, location_id, d_values={}, context={}):
194
198
        '''
195
199
        Compute the quantity of product to order like thid :
196
200
            [Delivery lead time (from supplier tab of the product or by default or manually overwritten) x Monthly Consumption]
197
201
            + Order coverage (number of months : 3 by default, manually overwritten) x Monthly consumption
198
202
            - Projected available quantity
199
203
        '''
200
 
        if d_values is None:
201
 
            d_values = {}
 
204
        product_obj = self.pool.get('product.product')
 
205
        supplier_info_obj = self.pool.get('product.supplierinfo')
 
206
        location_obj = self.pool.get('stock.location')
 
207
        cycle_obj = self.pool.get('stock.warehouse.order.cycle')
 
208
        review_obj = self.pool.get('monthly.review.consumption')
 
209
        review_line_obj = self.pool.get('monthly.review.consumption.line')
 
210
        
 
211
        product = product_obj.browse(cr, uid, product_id)
 
212
        location = location_obj.browse(cr, uid, location_id)
202
213
 
203
 
        # Get the delivery lead time of the product if the leadtime is not defined in rule and no supplier found in product form
204
 
        delivery_leadtime = product.procure_delay and round(int(product.procure_delay)/30.0, 2) or 1
205
 
        # Get the leadtime of the rule if defined
 
214
        
 
215
        # Get the delivery lead time
 
216
        delivery_leadtime = product.seller_delay and product.seller_delay != 'N/A' and round(int(product.seller_delay)/30.0, 2) or 1
206
217
        if 'leadtime' in d_values and d_values.get('leadtime', 0.00) != 0.00:
207
218
            delivery_leadtime = d_values.get('leadtime')
208
 
        elif product.seller_ids:
209
 
            # Get the supplier lead time if supplier is defined
210
 
            # The seller delay is defined in days, so divide it by 30.0 to have a LT in months
211
 
            delivery_leadtime = product.seller_delay and round(int(product.seller_delay)/30.0, 2) or 1
 
219
        else:
 
220
            sequence = False
 
221
            for supplier_info in product.seller_ids:
 
222
                if sequence and supplier_info.sequence < sequence:
 
223
                    sequence = supplier_info.sequence
 
224
                    delivery_leadtime = round(supplier_info.delay/30.0, 2)
 
225
                elif not sequence:
 
226
                    sequence = supplier_info.sequence
 
227
                    delivery_leadtime = round(supplier_info.delay/30.0, 2)
212
228
                
213
229
        # Get the monthly consumption
214
230
        monthly_consumption = 0.00
215
231
        
216
232
        if 'reviewed_consumption' in d_values and d_values.get('reviewed_consumption'):
217
 
            monthly_consumption = product.reviewed_consumption
218
 
        elif 'past_consumption' in d_values and d_values.get('past_consumption'):
219
 
            monthly_consumption = product.product_amc
 
233
            review_ids = review_obj.search(cr, uid, [], order='period_to', context=context)
 
234
            review_line_ids = review_line_obj.search(cr, uid, [('mrc_id', 'in', review_ids), ('name', '=', product_id)], context=context)
 
235
            for line in review_line_obj.browse(cr, uid, review_line_ids, context=context):
 
236
                last_date = False
 
237
                if not last_date or last_date < line.mrc_id.period_to:
 
238
                    monthly_consumption = line.fmc
 
239
        elif 'monthly_consumption' in d_values and d_values.get('monthly_consumption'):
 
240
            monthly_consumption = product_obj.compute_amc(cr, uid, product.id, context=context)
220
241
        else:
221
242
            monthly_consumption = d_values.get('manual_consumption', 0.00)
222
243
            
223
244
        # Get the order coverage
224
 
        order_coverage = d_values.get('coverage', 0.00)
 
245
        order_coverage = d_values.get('coverage', 3)
225
246
        
226
247
        # Get the projected available quantity
227
 
        available_qty = self.get_available(cr, uid, product.id, location_id, monthly_consumption, d_values)
 
248
        available_qty = self.get_available(cr, uid, product_id, location_id, monthly_consumption, d_values)
228
249
        
229
250
        qty_to_order = (delivery_leadtime * monthly_consumption) + (order_coverage * monthly_consumption) - available_qty
230
 
 
231
 
        if not context.get('get_data', False):
232
 
            res = round(self.pool.get('product.uom')._compute_qty(cr, uid, product.uom_id.id, qty_to_order, product.uom_id.id), 2)
233
 
        else:
234
 
            delta = 0
235
 
            if monthly_consumption:
236
 
                delta = available_qty / monthly_consumption * 30
237
 
 
238
 
            if delta <= 0.00:
239
 
                req_date = now().strftime('%Y-%m-%d')
240
 
            else:
241
 
                req_date = (now() + RelativeDateTime(days=delta)).strftime('%Y-%m-%d')
242
 
            res = round(self.pool.get('product.uom')._compute_qty(cr, uid, product.uom_id.id, qty_to_order, product.uom_id.id), 2), req_date
243
 
 
244
 
        return res
245
 
        
246
 
    def get_available(self, cr, uid, product_id, location_id, monthly_consumption, d_values=None, context=None):
 
251
        
 
252
        return round(self.pool.get('product.uom')._compute_qty(cr, uid, product.uom_id.id, qty_to_order, product.uom_id.id), 2)
 
253
        
 
254
        
 
255
    def get_available(self, cr, uid, product_id, location_id, monthly_consumption, d_values={}, context={}):
247
256
        '''
248
257
        Compute the projected available quantity like this :
249
258
            Available stock (real stock - picked reservation)
253
262
                        manually overwritten for a product or at product level)]
254
263
            - Expiry quantities.
255
264
        '''
256
 
        if context is None:
257
 
            context = {}
258
 
        if d_values is None:
259
 
            d_values = {}
260
 
            
261
265
        product_obj = self.pool.get('product.product')
 
266
        location_obj = self.pool.get('stock.location')
 
267
        move_obj = self.pool.get('stock.move')
262
268
        
263
269
        context.update({'location': location_id,
264
 
                        'compute_child': True, })
 
270
                        'compute_child': True, 
 
271
                        'from_date': time.strftime('%Y-%m-%d')})
265
272
        
266
273
        product = product_obj.browse(cr, uid, product_id, context=context)
267
 
 
268
 
        ''' Set this part of algorithm as comment because this algorithm seems to be equal to virtual stock
 
274
        location_name = location_obj.browse(cr, uid, location_id, context=context).name
 
275
        
 
276
        ''' Set this part of algorithm as comments because this algorithm seems to be equal to virtual stock
269
277
        
270
278
            To do validate by Magali
271
279
            
277
285
        picked_resa = product_obj.get_product_available(cr, uid, [product_id], context={'states': ['assigned'],
278
286
                                                                                       'what': ('in, out'), 
279
287
                                                                                       'location': location_id,
280
 
                                                                                       'compute_child': True,})
 
288
                                                                                       'compute_child': True, 
 
289
                                                                                       'from_date': time.strftime('%Y-%m-%d')})
 
290
        # Get the picked reservation
 
291
        ## TODO: To confirm by Magali
 
292
#        picked_reservation = 0.00
 
293
#        move_ids = []
 
294
#        for location in location_obj.search(cr, uid, [('location_id', 'child_of', [location_id])]):
 
295
#            for move_id in move_obj.search(cr, uid, [('product_id', '=', product_id), ('location_dest_id', '=', location), 
 
296
#                                                     ('state', '!=', 'draft'), ('move_dest_id', '!=', False)]):
 
297
#                move_ids.append(move_id)
 
298
#            
 
299
#        for move in move_obj.browse(cr, uid, move_ids):
 
300
#            picked_reservation += move.product_qty
281
301
            
282
 
        available_stock = product.qty_available + picked_resa.get(product.id)
 
302
        available_stock = product.qty_available - picked_resa.get(product.id)
 
303
        
 
304
        #available_stock = real_stock.get(product_id) - picked_reservation
 
305
        
 
306
        # Get the quantity on order
 
307
        ## TODO : To confirm by Magali
 
308
#        quantity_on_order = 0.00
 
309
#        move_ids = []
 
310
#        for location in location_obj.search(cr, uid, [('location_id', 'child_of', [location_id])]):
 
311
#            for move_id in move_obj.search(cr, uid, [('product_id', '=', product_id), ('location_dest_id', '=', location)]):
 
312
#                move_ids.append(move_id)
 
313
#            
 
314
#        for move in move_obj.browse(cr, uid, move_ids):
 
315
#            quantity_on_order += move.product_qty
283
316
            
284
317
        quantity_on_order = product_obj.get_product_available(cr, uid, [product_id], context={'states': ['confirmed'],
285
318
                                                                                              'what': ('in, out'), 
286
319
                                                                                              'location': location_id,
287
 
                                                                                              'compute_child': True,})
 
320
                                                                                              'compute_child': True, 
 
321
                                                                                              'from_date': time.strftime('%Y-%m-%d')})
288
322
           
289
323
        # Get the safety stock
290
 
        safety_stock = d_values.get('safety_stock', 0)
 
324
        safety_stock = d_values.get('safety', 0)
291
325
        
292
326
        # Get the safety time
293
327
        safety_time = d_values.get('safety_time', 0)
294
328
        
295
329
        # Get the expiry quantity
296
 
        expiry_quantity = product_obj.get_expiry_qty(cr, uid, product_id, location_id, monthly_consumption, d_values, context=context)
297
 
        expiry_quantity = expiry_quantity and expiry_quantity or 0.00
298
 
 
 
330
        # Set as comment because expiry quantity will be developed in a future sprint
 
331
        expiry_quantity = product_obj.get_expiry_qty(cr, uid, product_id, location_id, monthly_consumption, d_values)
 
332
        expiry_quantity = expiry_quantity and available_stock - expiry_quantity or 0.00
 
333
        #expiry_quantity = 0.00
299
334
        
300
335
        # Set this part of algorithm as comments because this algorithm seems to be equal to virtual stock
 
336
        return available_stock + quantity_on_order.get(product.id) - safety_stock - (safety_time * monthly_consumption) - expiry_quantity
 
337
 
301
338
#        return product.virtual_available - safety_stock - (safety_time * monthly_consumption) - expiry_quantity
302
 
        return available_stock + quantity_on_order.get(product.id) - safety_stock - (safety_time * monthly_consumption) - expiry_quantity
 
339
     
 
340
     
 
341
    def get_expiry_qty(self, cr, uid, product_id, location_id, monthly_consumption, d_values={}, context={}):
 
342
        '''
 
343
        Compute the expiry quantities
 
344
        
 
345
        INFO : This method is not use on Sprint1 because the algorithm is
 
346
        not determined
 
347
        '''
 
348
        product_obj = self.pool.get('product.product')
 
349
        stock_obj = self.pool.get('stock.location')
 
350
        batch_obj = self.pool.get('stock.production.lot')
 
351
        move_obj = self.pool.get('stock.move')
 
352
        
 
353
        res = 0.00
 
354
        
 
355
        location_ids = stock_obj.search(cr, uid, [('location_id', 'child_of', location_id)])
 
356
        available_stock = 0.00
 
357
        
 
358
        # Get all batches for this product
 
359
        batch_ids = batch_obj.search(cr, uid, [('product_id', '=', product_id)], offset=0, limit=None, order='life_date')
 
360
        if len(batch_ids) == 1:
 
361
            # Search all moves with this batch number
 
362
            for location in location_ids:
 
363
                context.update({'location_id': location})
 
364
                available_stock += batch_obj.browse(cr, uid, batch_ids, context=context)[0].stock_available
 
365
            expiry_date = batch_obj.browse(cr, uid, batch_ids)[0].life_date or time.strftime('%Y-%m-%d')
 
366
            nb_month = self.get_diff_date(expiry_date)
 
367
            res = available_stock - (nb_month * monthly_consumption)
 
368
        else:
 
369
            # Get the stock available for the product
 
370
            for location in location_ids:
 
371
                context.update({'location_id': location})
 
372
                for batch in batch_obj.browse(cr, uid, batch_ids, context=context):
 
373
                    available_stock += batch.stock_available
 
374
                    
 
375
            last_nb_month = 0
 
376
            sum_nb_month = 0
 
377
            res = 0
 
378
            for batch in batch_obj.browse(cr, uid, batch_ids):
 
379
                nb_month = self.get_diff_date(batch.life_date)
 
380
                if (nb_month - sum_nb_month) > 0:
 
381
                    tmp_qty = (nb_month - sum_nb_month) * monthly_consumption 
 
382
                    res += available_stock - (last_nb_month * monthly_consumption) - tmp_qty
 
383
                else:
 
384
                    break 
 
385
            
 
386
        return res
303
387
    
304
388
    def get_diff_date(self, date):
305
389
        '''