~unifield-team/unifield-wm/us-671-homere

« back to all changes in this revision

Viewing changes to procurement_cycle/procurement.py

UF-359 [ADD] Account override module integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from osv import osv, fields
23
23
 
 
24
import time
24
25
from tools.translate import _
25
 
from mx.DateTime import DateFrom
26
 
from mx.DateTime import RelativeDate
27
 
from mx.DateTime import now
28
 
 
29
 
import time
30
26
 
31
27
class stock_warehouse_order_cycle(osv.osv):
32
28
    _name = 'stock.warehouse.order.cycle'
33
29
    _description = 'Order Cycle'
34
 
    _order = 'name, id'
 
30
    _order = 'sequence, id'
35
31
 
36
 
    def create(self, cr, uid, data, context=None):
 
32
    def create(self, cr, uid, data, context={}):
37
33
        '''
38
34
        Checks if a frequence was choosen for the cycle
39
35
        '''
40
 
        if context is None:
41
 
            context = {}
42
36
        if not 'button' in context and (not 'frequence_id' in data or not data.get('frequence_id', False)):
43
37
            raise osv.except_osv(_('Error'), _('You should choose a frequence for this rule !'))
44
38
        
45
39
        return super(stock_warehouse_order_cycle, self).create(cr, uid, data, context=context)
46
40
        
47
 
    def write(self, cr, uid, ids, data, context=None):
 
41
    def write(self, cr, uid, ids, data, context={}):
48
42
        '''
49
43
        Checks if a frequence was choosen for the cycle
50
44
        '''
51
 
        if context is None:
52
 
            context = {}
53
 
        if isinstance(ids, (int, long)):
54
 
            ids = [ids]
55
 
        if data.get('sublist_id', False):
56
 
            data.update({'nomen_manda_0': False, 'nomen_manda_1': False, 'nomen_manda_2': False, 'nomen_manda_3': False})
57
 
        if data.get('nomen_manda_0', False):
58
 
            data.update({'sublist_id': False})
59
45
        if not 'button' in context and (not 'frequence_id' in data or not data.get('frequence_id', False)):
60
46
            for proc in self.browse(cr, uid, ids):
61
47
                if not proc.frequence_id:
64
50
        return super(stock_warehouse_order_cycle, self).write(cr, uid, ids, data, context=context)
65
51
        
66
52
    
67
 
    def _get_frequence_change(self, cr, uid, ids, context=None):
 
53
    def _get_frequence_change(self, cr, uid, ids, context={}):
68
54
        '''
69
55
        Returns ids when the frequence change
70
56
        '''
75
61
        
76
62
        return res.keys()
77
63
    
78
 
    def _get_frequence_name(self, cr, uid, ids, field_name, arg, context=None):
 
64
    def _get_frequence_name(self, cr, uid, ids, field_name, arg, context={}):
79
65
        '''
80
66
        Returns the name_get value of the frequence
81
67
        '''
82
68
        res = {}
83
69
        for proc in self.browse(cr, uid, ids):
84
 
            if proc.frequence_id:
85
 
                res[proc.id] = self.pool.get('stock.frequence').name_get(cr, uid, [proc.frequence_id.id], context=context)[0][1]
86
 
            
87
 
        return res
88
 
    
89
 
    def _get_product_ids(self, cr, uid, ids, field_name, arg, context=None):
90
 
        '''
91
 
        Returns a list of products for the rule
92
 
        '''
93
 
        res = {}
94
 
        
95
 
        for rule in self.browse(cr, uid, ids, context=context):
96
 
            res[rule.id] = []
97
 
            for line in rule.product_ids:
98
 
                res[rule.id].append(line.product_id.id)
99
 
        
100
 
        return res
101
 
    
102
 
    def _src_product_ids(self, cr, uid, obj, name, args, context=None):
103
 
        if not context:
104
 
            context = {}
105
 
            
106
 
        res = []
107
 
            
108
 
        for arg in args:
109
 
            if arg[0] == 'product_line_ids':
110
 
                rule_ids = []
111
 
                line_ids = self.pool.get('stock.warehouse.order.cycle.line').search(cr, uid, [('product_id', arg[1], arg[2])])
112
 
                for l in self.pool.get('stock.warehouse.order.cycle.line').browse(cr, uid, line_ids):
113
 
                    if l.order_cycle_id.id not in rule_ids:
114
 
                        rule_ids.append(l.order_cycle_id.id)
115
 
                res.append(('id', 'in', rule_ids))
116
 
                
 
70
            res[proc.id] = self.pool.get('stock.frequence').name_get(cr, uid, [proc.frequence_id.id])[0][1]
 
71
            
117
72
        return res
118
73
    
119
74
    _columns = {
120
 
        'sequence': fields.integer(string='Order', required=False, help='A higher order value means a low priority'),
121
 
        'name': fields.char(size=64, string='Reference', required=True),
 
75
        'sequence': fields.integer(string='Order', required=True, help='A higher order value means a low priority'),
 
76
        'name': fields.char(size=64, string='Name', required=True),
122
77
        'category_id': fields.many2one('product.category', string='Category'),
123
78
        'product_id': fields.many2one('product.product', string='Specific product'),
124
79
        'warehouse_id': fields.many2one('stock.warehouse', string='Warehouse', required=True),
125
 
        'location_id': fields.many2one('stock.location', 'Location', ondelete="cascade", required=True, 
126
 
                                       domain="[('is_replenishment', '=', warehouse_id)]",
127
 
                                       help='Location where the computation is made'),
128
 
        'frequence_name': fields.function(_get_frequence_name, method=True, string='Frequency', type='char', 
129
 
                                          help='Define the time between two replenishments'),
130
 
        'frequence_id': fields.many2one('stock.frequence', string='Frequency', help='It\'s the time between two replenishments'),
131
 
        'product_ids': fields.one2many('stock.warehouse.order.cycle.line', 'order_cycle_id', string='Products'),
 
80
        'location_id': fields.many2one('stock.location', string='Location'),
 
81
        'frequence_name': fields.function(_get_frequence_name, method=True, string='Frequence', type='char'),
 
82
        'frequence_id': fields.many2one('stock.frequence', string='Frequence'),
 
83
        'product_ids': fields.many2many('product.product', 'order_cycle_product_rel', 'order_cycle_id', 'product_id', string="Products"),
132
84
        'company_id': fields.many2one('res.company','Company',required=True),
133
85
        'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the automatic supply without removing it."),
134
86
        # Parameters for quantity calculation
135
87
        'leadtime': fields.float(digits=(16,2), string='Delivery lead time to consider', help='Delivery lead time in month'),
136
 
        'order_coverage': fields.float(digits=(16,2), string='Order coverage', help='In months. \
137
 
Define the time between two replenishments. \
138
 
Time used to compute the quantity of products to order according to the monthly consumption.'),
139
 
        'safety_stock_time': fields.float(digits=(16,2), string='Safety stock in time', help='In months. \
140
 
Define the time while the stock is not negative but should be replenished. \
141
 
Time used to compute the quantity of products to order according to the monthly consumption.'),
142
 
        'past_consumption': fields.boolean(string='Average monthly consumption', 
143
 
                                           help='If checked, the system will used the average monthly consumption to compute the quantity to order'),
144
 
        'consumption_period_from': fields.date(string='Period of calculation', 
145
 
                                             help='This period is a number of past months the system has to consider for AMC calculation.'\
146
 
                                             'By default this value is equal to the order coverage of the rule.'),
147
 
        'consumption_period_to': fields.date(string='-'),
148
 
        'reviewed_consumption': fields.boolean(string='Forecasted monthly consumption', 
149
 
                                               help='If checked, the system will used the forecasted monthly consumption to compute the quantity to order'),
150
 
        'manual_consumption': fields.float(digits=(16,2), string='Manual monthly consumption',
151
 
                                           help='If not 0.00, the system will used the entered monthly consumption to compute the quantity to order'),
 
88
        'order_coverage': fields.float(digits=(16,2), string='Order coverage'),
 
89
        'safety_stock_time': fields.float(digits=(16,2), string='Safety stock in time'),
 
90
        'safety_stock': fields.integer(string='Safety stock (quantity'),
 
91
        'past_consumption': fields.boolean(string='Past monthly consumption'),
 
92
        'reviewed_consumption': fields.boolean(string='Reviewed monthly consumption'),
 
93
        'manual_consumption': fields.float(digits=(16,2), string='Manual monthly consumption'),
152
94
        'next_date': fields.related('frequence_id', 'next_date', string='Next scheduled date', readonly=True, type='date',
153
 
                                    help='As this date is not in the past, no new replenishment will be run', 
154
 
                                    store={'stock.warehouse.order.cycle': (lambda self, cr, uid, ids, context=None: ids, ['frequence_id'], 20),
 
95
                                    store={'stock.warehouse.order.cycle': (lambda self, cr, uid, ids, context={}: ids, ['frequence_id'], 20),
155
96
                                           'stock.frequence': (_get_frequence_change, None, 20)}),
156
 
        'product_line_ids': fields.function(_get_product_ids, fnct_search=_src_product_ids, 
157
 
                                    type='many2many', relation='product.product', method=True, string='Products'),
158
 
        'sublist_id': fields.many2one('product.list', string='List/Sublist'),
159
 
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type'),
160
 
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group'),
161
 
        'nomen_manda_2': fields.many2one('product.nomenclature', 'Family'),
162
 
        'nomen_manda_3': fields.many2one('product.nomenclature', 'Root'),
163
97
    }
164
98
    
165
99
    _defaults = {
 
100
        'sequence': lambda *a: 10,
166
101
        'past_consumption': lambda *a: 1,
167
102
        'active': lambda *a: 1,
168
103
        'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.order.cycle') or '',
 
104
        'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.order.cycle', context=c),
169
105
        'order_coverage': lambda *a: 3,
170
106
    }
171
107
    
172
 
    def default_get(self, cr, uid, fields, context=None):
173
 
        '''
174
 
        Get the default values for the replenishment rule
175
 
        '''
176
 
        res = super(stock_warehouse_order_cycle, self).default_get(cr, uid, fields, context=context)
177
 
        
178
 
        company_id = res.get('company_id')
179
 
        warehouse_id = res.get('warehouse_id')
180
 
        
181
 
        if not 'company_id' in res:
182
 
            company_id = self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.automatic.supply', context=context)
183
 
            res.update({'company_id': company_id})
184
 
        
185
 
        if not 'warehouse_id' in res:
186
 
            warehouse_id = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', company_id)], context=context)[0]
187
 
            res.update({'warehouse_id': warehouse_id})
188
 
            
189
 
        if not 'location_id' in res:
190
 
            location_id = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context=context).lot_stock_id.id
191
 
            res.update({'location_id': location_id})
192
 
            
193
 
        if not 'consumption_period_from' in res:
194
 
            res.update({'consumption_period_from': (DateFrom(now()) + RelativeDate(day=1)).strftime('%Y-%m-%d')})
195
 
            
196
 
        if not 'consumption_period_to' in res:
197
 
            res.update({'consumption_period_to': (DateFrom(now()) + RelativeDate(months=1, day=1, days=-1)).strftime('%Y-%m-%d')})
198
 
        
199
 
        return res
200
 
    
201
 
    def on_change_period(self, cr, uid, ids, from_date, to_date):
202
 
        '''
203
 
        Check if the from date is younger than the to date
204
 
        '''
205
 
        warn = {}
206
 
        val = {}
207
 
        
208
 
        if from_date and to_date and from_date > to_date:
209
 
            warn = {'title': 'Issue on date',
210
 
                    'message': 'The start date must be younger than end date'}
211
 
 
212
 
        # Set the from date to the first day of the month            
213
 
        if from_date:
214
 
            val.update({'consumption_period_from': (DateFrom(from_date) + RelativeDate(day=1)).strftime('%Y-%m-%d')})
215
 
            
216
 
        # Set the to date to the last day of the month
217
 
        if to_date:
218
 
            val.update({'consumption_period_to': (DateFrom(to_date) + RelativeDate(months=1, day=1, days=-1)).strftime('%Y-%m-%d')})
219
 
        
220
 
        return {'value': val, 'warning': warn}
221
 
 
222
 
    def onChangeSearchNomenclature(self, cr, uid, ids, position, n_type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
223
 
        return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, 0, position, n_type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, False, context={'withnum': 1})
224
 
 
225
 
    def fill_lines(self, cr, uid, ids, context=None):
226
 
        '''
227
 
        Fill all lines according to defined nomenclature level and sublist
228
 
        '''
229
 
        line_obj = self.pool.get('stock.warehouse.order.cycle.line')
230
 
        product_obj = self.pool.get('product.product')
231
 
        
232
 
        if context is None:
233
 
            context = {}
234
 
            
235
 
        for report in self.browse(cr, uid, ids, context=context):
236
 
            product_ids = []
237
 
 
238
 
            nom = False
239
 
            field = False
240
 
            # Get all products for the defined nomenclature
241
 
            if report.nomen_manda_3:
242
 
                nom = report.nomen_manda_3.id
243
 
                field = 'nomen_manda_3'
244
 
            elif report.nomen_manda_2:
245
 
                nom = report.nomen_manda_2.id
246
 
                field = 'nomen_manda_2'
247
 
            elif report.nomen_manda_1:
248
 
                nom = report.nomen_manda_1.id
249
 
                field = 'nomen_manda_1'
250
 
            elif report.nomen_manda_0:
251
 
                nom = report.nomen_manda_0.id
252
 
                field = 'nomen_manda_0'
253
 
            if nom:
254
 
                product_ids.extend(self.pool.get('product.product').search(cr, uid, [(field, '=', nom)], context=context))
255
 
 
256
 
            # Get all products for the defined list
257
 
            if report.sublist_id:
258
 
                for line in report.sublist_id.product_ids:
259
 
                    product_ids.append(line.name.id)
260
 
                    
261
 
            for product in product_obj.browse(cr, uid, product_ids, context=context):
262
 
                # Check if the product is not already in the list
263
 
                if product.type not in ('consu', 'service', 'service_recep') and \
264
 
                        not line_obj.search(cr, uid, [('order_cycle_id', '=', report.id), 
265
 
                                                      ('product_id', '=', product.id),
266
 
                                                      ('uom_id', '=', product.uom_id.id)], context=context):
267
 
                    line_obj.create(cr, uid, {'order_cycle_id': report.id,
268
 
                                              'product_id': product.id, 
269
 
                                              'safety_stock': 0.00,
270
 
                                              'uom_id': product.uom_id.id}, context=context)
271
 
        
272
 
        return True
273
 
 
274
 
    def get_nomen(self, cr, uid, ids, field):
275
 
        return self.pool.get('product.nomenclature').get_nomen(cr, uid, self, ids, field, context={'withnum': 1})
276
 
 
277
 
    def consumption_method_change(self, cr, uid, ids, past_consumption, reviewed_consumption, manual_consumption, order_coverage, field='past'):
 
108
    def consumption_method_change(self, cr, uid, ids, past_consumption, reviewed_consumption, manual_consumption, product_id, field='past'):
278
109
        '''
279
110
        Uncheck a box when the other is checked
280
111
        '''
281
112
        v = {}
282
 
        w = {}
283
 
        date_from = now() + RelativeDate(day=1, months=-round(order_coverage, 1)+1)
284
 
        date_to = now() + RelativeDate(days=-1, day=1, months=1)
285
 
        dates = self.on_change_period(cr, uid, ids, date_from, date_to)
286
113
        if field == 'past' and past_consumption:
287
 
            v.update({'reviewed_consumption': 0, 'manual_consumption': 0.00,
288
 
                      'consumption_period_from': dates.get('value', {}).get('consumption_period_from'),
289
 
                      'consumption_period_to': dates.get('value', {}).get('consumption_period_to'),})
 
114
            v.update({'reviewed_consumption': 0, 'manual_consumption': 0.00})
290
115
        elif field == 'past' and not past_consumption:
291
 
            v.update({'reviewed_consumption': 1, 'manual_consumption': 0.00,})
292
 
            v.update({'consumption_period_from': False, 'consumption_period_to': False})
 
116
            v.update({'reviewed_consumption': 1, 'manual_consumption': 0.00})
293
117
        elif field == 'review' and reviewed_consumption:
294
118
            v.update({'past_consumption': 0, 'manual_consumption': 0.00})
295
 
            v.update({'consumption_period_from': False, 'consumption_period_to': False})
296
119
        elif field == 'review' and not reviewed_consumption:
297
 
            v.update({'past_consumption': 1, 'manual_consumption': 0.00, 
298
 
                      'consumption_period_from': dates.get('value', {}).get('consumption_period_from'),
299
 
                      'consumption_period_to': dates.get('value', {}).get('consumption_period_to'),})
300
 
        elif field == 'manual' and manual_consumption < 0.00:
301
 
            v.update({'manual_consumption': 0.00})
302
 
            w.update({'title': 'Negative consumption',
303
 
                      'message': 'You mustn\'t have a negative consumption'})
304
 
        elif field == 'manual' and manual_consumption != 0.00 :
 
120
            v.update({'past_consumption': 1, 'manual_consumption': 0.00})
 
121
        elif field == 'manual' and manual_consumption != 0.00 and product_id:
305
122
            v.update({'reviewed_consumption': 0, 'past_consumption': 0})
306
 
            v.update({'consumption_period_from': False, 'consumption_period_to': False})
307
 
        elif field == 'manual' and (manual_consumption == 0.00 ):
308
 
            v.update({'past_consumption': 1,
309
 
                      'consumption_period_from': dates.get('value', {}).get('consumption_period_from'),
310
 
                      'consumption_period_to': dates.get('value', {}).get('consumption_period_to'),})
 
123
        elif field == 'manual' and (manual_consumption == 0.00 or not product_id):
 
124
            v.update({'past_consumption': 1})
311
125
            
312
 
        return {'value': v, 'warning': w}
 
126
        return {'value': v}
313
127
    
314
 
    def choose_change_frequence(self, cr, uid, ids, context=None):
 
128
    def choose_change_frequence(self, cr, uid, ids, context={}):
315
129
        '''
316
130
        Open a wizard to define a frequency for the order cycle
317
131
        or open a wizard to modify the frequency if frequency already exists
318
132
        '''
319
133
        if isinstance(ids, (int, long)):
320
134
            ids = [ids]
321
 
        if context is None:
322
 
            context = {}
323
135
            
324
136
        frequence_id = False
325
137
        res_id = False
330
142
            if proc.frequence_id and proc.frequence_id.id:
331
143
                frequence_id = proc.frequence_id.id
332
144
                res_ok = True
333
 
            else:
334
 
                frequence_data = {'name': 'monthly',
335
 
                                  'monthly_choose_freq': 1,
336
 
                                  'monthly_choose_day': 'monday',
337
 
                                  'monthly_frequency': 1,
338
 
                                  'monthly_one_day': True,
339
 
                                  'no_end_date': True,
340
 
                                  'start_date': time.strftime('%Y-%m-%d'),}
341
 
                frequence_id = self.pool.get('stock.frequence').create(cr, uid, frequence_data, context=context)
342
 
                self.write(cr, uid, proc.id, {'frequence_id': frequence_id}, context=context)
343
 
                res_ok = True
344
145
            
345
146
        context.update({'active_id': res_id, 
346
147
                        'active_model': 'stock.warehouse.order.cycle',
365
166
            return {'value': v}
366
167
        return {}
367
168
    
368
 
    def unlink(self, cr, uid, ids, context=None):
369
 
        '''
370
 
        When remove an order cycle rule, delete the attached frequency
371
 
        '''
 
169
    def unlink(self, cr, uid, ids, context):
372
170
        if isinstance(ids, (int, long)):
373
171
            ids = [ids]
374
172
        freq_ids = []
379
177
            self.pool.get('stock.frequence').unlink(cr, uid, freq_ids, context)
380
178
        return super(stock_warehouse_order_cycle, self).unlink(cr, uid, ids, context=context)
381
179
    
382
 
    def copy(self, cr, uid, ids, default=None, context=None):
383
 
        '''
384
 
        When duplicate an order cycle rule, duplicate the frequency
385
 
        '''
 
180
    def copy(self, cr, uid, id, default=None, context=None):
386
181
        if not default:
387
182
            default = {}
388
 
        obj = self.read(cr, uid, ids, ['frequence_id'])
 
183
        obj = self.read(cr, uid, id, ['frequence_id'])
389
184
        if obj['frequence_id']:
390
185
            default['frequence_id'] = self.pool.get('stock.frequence').copy(cr, uid, obj['frequence_id'][0], context=context)
391
186
 
392
187
        default.update({
393
188
            'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.order.cycle') or '',
394
189
        })
395
 
        return super(stock_warehouse_order_cycle, self).copy(cr, uid, ids, default, context=context)
 
190
        return super(stock_warehouse_order_cycle, self).copy(cr, uid, id, default, context=context)
396
191
    
397
192
stock_warehouse_order_cycle()
398
193
 
399
 
class stock_warehouse_order_cycle_line(osv.osv):
400
 
    _name = 'stock.warehouse.order.cycle.line'
401
 
    _rec_name = 'product_id'
402
 
    _description = 'Products to replenish'
403
 
 
404
 
    def _get_data(self, cr, uid, ids, field_name, args, context=None):
405
 
        '''
406
 
        Compute some data
407
 
        '''
408
 
        product_obj = self.pool.get('product.product')
409
 
        proc_obj = self.pool.get('procurement.order')
410
 
        prodlot_obj = self.pool.get('stock.production.lot')
411
 
 
412
 
        if isinstance(ids, (int, long)):
413
 
            ids = [ids]
414
 
 
415
 
        res = {}
416
 
 
417
 
        for line in self.browse(cr, uid, ids, context=context):
418
 
            # Stock values
419
 
            location_id = line.order_cycle_id.location_id.id
420
 
            stock_product = product_obj.browse(cr, uid, line.product_id.id, context=dict(context, location=location_id))
421
 
            # Consumption values
422
 
            from_date = line.order_cycle_id.consumption_period_from
423
 
            to_date = line.order_cycle_id.consumption_period_to
424
 
            consu_product = product_obj.browse(cr, uid, line.product_id.id, context=dict(context, from_date=from_date, to_date=to_date))
425
 
            consu = 0.00
426
 
            if line.order_cycle_id.past_consumption:
427
 
                consu = consu_product.product_amc
428
 
            elif line.order_cycle_id.reviewed_consumption:
429
 
                consu = consu_product.reviewed_consumption
430
 
            else:
431
 
                consu = line.order_cycle_id.manual_consumption
432
 
 
433
 
            # Expiry values
434
 
            d_values = {'reviewed_consumption': False,
435
 
                        'past_consumption': False,
436
 
                        'manual_consumption': consu,
437
 
                        'consumption_period_from': line.order_cycle_id.consumption_period_from,
438
 
                        'consumption_period_to': line.order_cycle_id.consumption_period_to,
439
 
                        'leadtime': line.order_cycle_id.leadtime,
440
 
                        'coverage': line.order_cycle_id.order_coverage,
441
 
                        'safety_stock': line.safety_stock,
442
 
                        'safety_time': line.order_cycle_id.safety_stock_time}
443
 
            expiry_product_qty = product_obj.get_expiry_qty(cr, uid, line.product_id.id, location_id, False, d_values, context=dict(context, location=location_id, compute_child=True))
444
 
            expiry_product_qty = expiry_product_qty or 0.00
445
 
 
446
 
            qty_to_order, req_date = proc_obj._compute_quantity(cr, uid, False, line.product_id, line.order_cycle_id.location_id.id, d_values, context=dict(context, from_date=from_date, to_date=to_date, get_data=True))
447
 
 
448
 
            consumed_in_period = (consu * d_values['coverage']) + (consu * d_values['safety_time']) + d_values['safety_stock'] + expiry_product_qty
449
 
            if stock_product.perishable and stock_product.virtual_available < consumed_in_period:
450
 
                prodlot_ids = prodlot_obj.search(cr, uid, [('product_id', '=', stock_product.id)], order='life_date desc', limit=1, context=context)
451
 
                if prodlot_ids:
452
 
                    life_date = prodlot_obj.read(cr, uid, prodlot_ids[0], ['life_date'], context=context)['life_date']
453
 
                    if life_date < req_date:
454
 
                        req_date = life_date
455
 
 
456
 
            res[line.id] = {'consumption': consu,
457
 
                            'real_stock': stock_product.qty_available,
458
 
                            'available_stock': stock_product.virtual_available,
459
 
                            'expiry_before': expiry_product_qty,
460
 
                            'qty_to_order': qty_to_order >= 0.00 and qty_to_order or 0.00,
461
 
                            'supplier_id': stock_product.seller_id and stock_product.seller_id.id or False,
462
 
                            'required_date': req_date,
463
 
                            }
464
 
 
465
 
        return res
466
 
    
467
 
    _columns = {
468
 
        'product_id': fields.many2one('product.product', required=True, string='Product'),
469
 
        'uom_id': fields.many2one('product.uom', string='UoM', required=True),
470
 
        'order_cycle_id': fields.many2one('stock.warehouse.order.cycle', string='Order cycle', required=True, ondelete='cascade'),
471
 
        'safety_stock': fields.float(digits=(16,2), string='Safety stock (Qty)', required=True),
472
 
        'consumption': fields.function(_get_data, method=True, type='float', digits=(16,3), string='AMC/FMC', multi='data', readonly=True),
473
 
        'real_stock': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Real stock', multi='data', readonly=True),
474
 
        'available_stock': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Available stock', multi='data', readonly=True),
475
 
        'expiry_before': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Exp. before consumption', multi='data', readonly=True),
476
 
        'qty_to_order': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Qty. to order', multi='data', readonly=True),
477
 
        'supplier_id': fields.function(_get_data, method=True, type='many2one', relation='res.partner', string='Supplier', multi='data', readonly=True),
478
 
        'required_date': fields.function(_get_data, method=True, type='date', string='Required by date', multi='data', readonly=True),
479
 
    }
480
 
    
481
 
    def _check_uniqueness(self, cr, uid, ids, context=None):
482
 
        '''
483
 
        Check if the product is not already in the current rule
484
 
        '''
485
 
        for line in self.browse(cr, uid, ids, context=context):
486
 
            lines = self.search(cr, uid, [('id', '!=', line.id), 
487
 
                                          ('product_id', '=', line.product_id.id),
488
 
                                          ('order_cycle_id', '=', line.order_cycle_id.id)], context=context)
489
 
            if lines:
490
 
                return False
491
 
            
492
 
        return True
493
 
    
494
 
    _constraints = [
495
 
        (_check_uniqueness, 'You cannot have two times the same product on the same order cycle rule', ['product_id'])
496
 
    ]
497
 
 
498
 
    def onchange_uom_qty(self, cr, uid, ids, uom_id, qty):
499
 
        res = {}
500
 
 
501
 
        if qty:
502
 
            res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom_id, qty, 'safety_stock', result=res)
503
 
 
504
 
        return res
505
 
    
506
 
    def product_change(self, cr, uid, ids, product_id=False, context=None):
507
 
        '''
508
 
        Set the UoM as the default UoM of the product
509
 
        '''
510
 
        v = {}
511
 
        
512
 
        if not product_id:
513
 
            v.update({'product_uom': False, 'safety_stock': 0.00})
514
 
        else:
515
 
            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
516
 
            if product.uom_id:
517
 
                v.update({'uom_id': product.uom_id.id})
518
 
                
519
 
        return {'value': v}
520
 
    
521
 
stock_warehouse_order_cycle_line()
522
 
 
523
194
class stock_frequence(osv.osv):
524
195
    _name = 'stock.frequence'
525
196
    _inherit = 'stock.frequence'
528
199
        'order_cycle_ids': fields.one2many('stock.warehouse.order.cycle', 'frequence_id', string='Order Cycle'),
529
200
    }
530
201
 
531
 
    def copy(self, cr, uid, ids, default=None, context=None):
532
 
        '''
533
 
        When the frequence is duplicate, remove the attached order cycle rules
534
 
        '''
 
202
    def copy(self, cr, uid, id, default=None, context=None):
535
203
        if not default:
536
204
            default = {}
537
205
        default['order_cycle_ids'] = False
538
 
        return super(stock_frequence, self).copy(cr, uid, ids, default, context)
 
206
        return super(stock_frequence, self).copy(cr, uid, id, default, context)
539
207
 
540
 
    def choose_frequency(self, cr, uid, ids, context=None):
 
208
    def choose_frequency(self, cr, uid, ids, context={}):
541
209
        '''
542
210
        Adds the support of order cycles on choose frequency method
543
211
        '''
544
212
        if isinstance(ids, (int, long)):
545
213
            ids = [ids]
546
 
        if context is None:
547
 
            context = {}
 
214
            
548
215
        if not context.get('res_ok', False) and 'active_id' in context and 'active_model' in context and \
549
216
            context.get('active_model') == 'stock.warehouse.order.cycle':
550
217
            self.pool.get('stock.warehouse.order.cycle').write(cr, uid, [context.get('active_id')], {'frequence_id': ids[0]})