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

« back to all changes in this revision

Viewing changes to threshold_value/threshold_value.py

  • Committer: jf
  • Date: 2012-04-17 15:29:16 UTC
  • mfrom: (631.3.7 UF_828)
  • Revision ID: jf@tempo4-20120417152916-svm6ioq8ur2bi5tu
UF-955 [DEV] Reporting (Month-end) - 2 remaining reports
lp:~unifield-team/unifield-wm/UF_955

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
#
21
21
##############################################################################
22
22
 
23
 
from osv import osv
24
 
from osv import fields
25
 
from tools.translate import _
 
23
from osv import osv, fields
26
24
 
27
 
from mx.DateTime import DateFrom
28
 
from mx.DateTime import now
29
 
from mx.DateTime import RelativeDate
 
25
from mx.DateTime import *
30
26
 
31
27
class threshold_value(osv.osv):
32
28
    _name = 'threshold.value'
33
29
    _description = 'Threshold value'
34
30
    
35
 
    def _get_product_ids(self, cr, uid, ids, field_name, arg, context=None):
36
 
        '''
37
 
        Returns a list of products for the rule
38
 
        '''
39
 
        res = {}
40
 
        
41
 
        for rule in self.browse(cr, uid, ids, context=context):
42
 
            res[rule.id] = []
43
 
            for line in rule.line_ids:
44
 
                res[rule.id].append(line.product_id.id)
45
 
        
46
 
        return res
47
 
    
48
 
    def _src_product_ids(self, cr, uid, obj, name, args, context=None):
49
 
        if not context:
50
 
            context = {}
51
 
            
52
 
        res = []
53
 
            
54
 
        for arg in args:
55
 
            if arg[0] == 'product_ids':
56
 
                rule_ids = []
57
 
                line_ids = self.pool.get('threshold.value.line').search(cr, uid, [('product_id', arg[1], arg[2])])
58
 
                for l in self.pool.get('threshold.value.line').browse(cr, uid, line_ids):
59
 
                    if l.threshold_value_id.id not in rule_ids:
60
 
                        rule_ids.append(l.threshold_value_id.id)
61
 
                res.append(('id', 'in', rule_ids))
62
 
                
63
 
        return res
64
 
    
65
31
    _columns = {
66
 
        'name': fields.char(size=128, string='Reference', required=True),
 
32
        'name': fields.char(size=128, string='Name', required=True),
67
33
        'active': fields.boolean(string='Active'),
68
 
        'warehouse_id': fields.many2one('stock.warehouse', string='Warehouse', required=True),
69
 
        'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade", 
70
 
                                       domain="[('is_replenishment', '=', warehouse_id)]",
71
 
                                       help='Location where the computation is made'),
 
34
        'warehouse_id': fields.many2one('stock.warehouse', string='Warehouse'),
 
35
        'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade", domain="[('usage', '=', 'internal')]"),
72
36
        'compute_method': fields.selection([('fixed', 'Fixed values'), ('computed', 'Computed values')],
73
37
                                           string='Method of computation', required=True,
74
38
                                           help="""If 'Fixed values', the scheduler will compare stock of product with the threshold value of the line. \n
75
39
                                           If 'Computed values', the threshold value and the ordered quantity will be calculated according to defined parameters"""),
76
40
        'consumption_method': fields.selection([('amc', 'Average Monthly Consumption'), ('fmc', 'Forecasted Monthly Consumption')],
77
 
                                               string='Consumption Method',
78
 
                                               help='Method used to compute the consumption of products.'),
 
41
                                               string='Consumption Method'),
79
42
        'consumption_period_from': fields.date(string='Period of calculation', 
80
43
                                             help='This period is a number of past months the system has to consider for AMC calculation.'\
81
44
                                             'By default this value is equal to the frequency in the Threshold.'),
82
45
        'consumption_period_to': fields.date(string='-'),
83
 
        'frequency': fields.float(digits=(16,2), string='Order frequency', 
84
 
                                  help='The time between two replenishments. Will be used to compute the quantity to order.'),
85
 
        'safety_month': fields.float(digits=(16,2), string='Safety Stock in months',
86
 
                                     help='In months. Period during the stock is not empty but need to be replenish. \
87
 
                                     Used to compute the quantity to order.'),
88
 
        'lead_time': fields.float(digits=(16,2), string='Fixed Lead Time in months',
89
 
                                  help='In months. Time to be delivered after processing the purchase order.'),
90
 
        'supplier_lt': fields.boolean(string='Product\'s supplier LT',
91
 
                                      help='If checked, use the lead time set in the supplier form.'),
 
46
        'frequency': fields.float(digits=(16,2), string='Order frequency'),
 
47
        'safety_month': fields.float(digits=(16,2), string='Safety Stock in months'),
 
48
        'lead_time': fields.float(digits=(16,2), string='Fixed Lead Time in months'),
 
49
        'supplier_lt': fields.boolean(string='Product\'s supplier LT'),
92
50
        'line_ids': fields.one2many('threshold.value.line', 'threshold_value_id', string="Products"),
93
51
        'fixed_line_ids': fields.one2many('threshold.value.line', 'threshold_value_id2', string="Products"),
94
 
        'product_ids': fields.function(_get_product_ids, fnct_search=_src_product_ids, 
95
 
                                    type='many2many', relation='product.product', method=True, string='Products'),
96
52
        'sublist_id': fields.many2one('product.list', string='List/Sublist'),
97
53
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type'),
98
54
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group'),
109
65
        'consumption_period_to': lambda *a: (now() + RelativeDate(day=1)).strftime('%Y-%m-%d'),
110
66
    }
111
67
    
112
 
    def copy(self, cr, uid, ids, defaults={}, context=None):
113
 
        '''
114
 
        Increment the sequence
115
 
        '''
116
 
        name = self.pool.get('ir.sequence').get(cr, uid, 'threshold.value') or ''
117
 
        defaults.update({'name': name})
118
 
        
119
 
        return super(threshold_value, self).copy(cr, uid, ids, defaults, context=context)
120
 
    
121
 
    def default_get(self, cr, uid, fields, context=None):
122
 
        '''
123
 
        Get the default values for the replenishment rule
124
 
        '''
125
 
        res = super(threshold_value, self).default_get(cr, uid, fields, context=context)
126
 
        
127
 
        company_id = res.get('company_id')
128
 
        warehouse_id = res.get('warehouse_id')
129
 
        
130
 
        if not 'company_id' in res:
131
 
            company_id = self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.automatic.supply', context=context)
132
 
            res.update({'company_id': company_id})
133
 
        
134
 
        if not 'warehouse_id' in res:
135
 
            warehouse_id = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', company_id)], context=context)[0]
136
 
            res.update({'warehouse_id': warehouse_id})
137
 
            
138
 
        if not 'location_id' in res:
139
 
            location_id = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context=context).lot_stock_id.id
140
 
            res.update({'location_id': location_id})
141
 
        
142
 
        return res
143
 
    
144
68
    def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, context=None):
145
69
        """ Finds default stock location id for changed warehouse.
146
70
        @param warehouse_id: Changed id of warehouse.
160
84
        
161
85
        if method and method == 'fmc':
162
86
            res.update({'consumption_period_from': False, 'consumption_period_to': False})
163
 
        elif method and method == 'amc':
164
 
            res.update({'consumption_period_from': (now() + RelativeDate(day=1, months=-2)).strftime('%Y-%m-%d'),
165
 
                        'consumption_period_to': (now() + RelativeDate(day=1, months=1, days=-1)).strftime('%Y-%m-%d')})
166
87
        
167
88
        return {'value': res}
168
89
    
234
155
 
235
156
            for product in self.pool.get('product.product').browse(cr, uid, product_ids, context=context):
236
157
                # Check if the product is not already on the report
237
 
                if product.type not in ('consu', 'service', 'service_recep') and product.id not in products:
 
158
                if product.id not in products:
238
159
                    self.pool.get('threshold.value.line').create(cr, uid, {'product_id': product.id,
239
160
                                                                                            'product_uom_id': product.uom_id.id,
240
161
                                                                                            'product_qty': 1.00,
261
182
        ret = super(threshold_value, self).write(cr, uid, ids, vals, context=context)
262
183
        return ret
263
184
 
264
 
    def on_change_compute_method(self, cr, uid, ids, compute_method,
265
 
        context=None):
266
 
        res = {}
267
 
        if compute_method and compute_method == 'computed':
268
 
            # UF-2511: switch from 'fixed' to 'compute' compute method
269
 
            # warn user to refresh values
270
 
            # (are not any more computed in 'fixed' mode)
271
 
            msg = "You switch from 'fixed values' to 'computed values'. " \
272
 
                "Please click on 'Refresh values' button to compute values."
273
 
            res = {
274
 
                'warning': {
275
 
                    'title': _('Warning'),
276
 
                    'message': _(msg),
277
 
                    },
278
 
            }
279
 
        return res
280
 
 
281
185
threshold_value()
282
186
 
283
187
class threshold_value_line(osv.osv):
285
189
    _description = 'Threshold Value Line'
286
190
    _rec_name = 'product_id'
287
191
    
288
 
    def copy_data(self, cr, uid, ids, defaults={}, context=None):
289
 
        res = super(threshold_value_line, self).copy_data(cr, uid, ids, defaults, context=context)
290
 
        
291
 
        if isinstance(res, dict):
292
 
            if 'threshold_value_id' in res:
293
 
                del res['threshold_value_id']
294
 
            if 'threshold_value_id2' in res:
295
 
                del res['threshold_value_id2']
296
 
        
297
 
        return res
298
 
    
299
192
    def create(self, cr, uid, vals, context=None):
300
193
        '''
301
194
        Add the second link to the threshold value rule
315
208
            vals.update({'threshold_value_id2': vals['threshold_value_id']})
316
209
        elif 'threshold_value_id2' in vals:
317
210
            vals.update({'threshold_value_id': vals['threshold_value_id2']})
318
 
            
319
 
        context.update({'fake_threshold_value': vals.get('fake_threshold_value', False)})
320
 
        vals.update({'fake_threshold_value': 0.00})
321
211
        
322
212
        return super(threshold_value_line, self).write(cr, uid, ids, vals, context=context)
323
213
    
330
220
            context = {}
331
221
        
332
222
        for line in self.browse(cr, uid, ids, context=context):
333
 
            if context.get('fake_threshold_value', False):
334
 
                res[line.id] = context.get('fake_threshold_value', 0.00)
335
 
                continue 
336
 
            res[line.id] = 0.00
 
223
            res[line.id] = {'threshold_value': 0.00, 'product_qty': 0.00}
337
224
            
338
225
            rule = line.threshold_value_id
339
226
            context.update({'location_id': rule.location_id.id, 'compute_child': True})
340
227
            product = self.pool.get('product.product').browse(cr, uid, line.product_id.id, context=context)
341
 
            result = self._get_threshold_value(cr, uid, line.id, product, rule.compute_method, rule.consumption_method, 
 
228
            res[line.id] = self._get_threshold_value(cr, uid, line.id, product, rule.compute_method, rule.consumption_method, 
342
229
                                                     rule.consumption_period_from, rule.consumption_period_to, rule.frequency, 
343
230
                                                     rule.safety_month, rule.lead_time, rule.supplier_lt, line.product_uom_id.id, context)
344
 
            res[line.id] = result.get(field_name, 0.00) 
345
 
        
346
 
        return res
347
 
 
348
 
    
349
 
    def _get_threshold(self, cr, uid, ids, context={}):
350
 
        res = {}
351
 
        for t in self.pool.get('threshold.value').browse(cr, uid, ids, context=context):
352
 
            for l in t.line_ids:
353
 
                res[l.id] = True
354
 
                
355
 
        return res.keys()
356
 
        
357
 
    def _get_data(self, cr, uid, ids, field_name, args, context=None):
358
 
        '''
359
 
        Compute some data
360
 
        '''
361
 
        product_obj = self.pool.get('product.product')
362
 
        proc_obj = self.pool.get('procurement.order')
363
 
 
364
 
        if isinstance(ids, (int, long)):
365
 
            ids = [ids]
366
 
 
367
 
        res = {}
368
 
 
369
 
        for line in self.browse(cr, uid, ids, context=context):
370
 
            if context and context.get('compute_method', False) == 'fixed':
371
 
                # UF-2511: do not compute in 'fixed' compute method mode
372
 
                res[line.id] = {
373
 
                    'consumption': 0.,
374
 
                    'real_stock': 0.,
375
 
                    'available_stock': 0.,
376
 
                    'expiry_before': False,
377
 
                    'supplier_id': False,
378
 
                    'required_date': False,
379
 
                }
380
 
                continue
381
 
 
382
 
            # Stock values
383
 
            location_id = line.threshold_value_id.location_id.id
384
 
            stock_product = product_obj.browse(cr, uid, line.product_id.id, context=dict(context, location=location_id))
385
 
            # Consumption values
386
 
            from_date = line.threshold_value_id.consumption_period_from
387
 
            to_date = line.threshold_value_id.consumption_period_to
388
 
            consu_product = product_obj.browse(cr, uid, line.product_id.id, context=dict(context, from_date=from_date, to_date=to_date))
389
 
            consu = 0.00
390
 
            if line.threshold_value_id.consumption_method == 'amc':
391
 
                consu = consu_product.product_amc
392
 
            elif line.threshold_value_id.consumption_method == 'fmc':
393
 
                consu = consu_product.reviewed_consumption
394
 
            else:
395
 
                consu = 0.00
396
 
 
397
 
            # Expiry values
398
 
            d_values = {'reviewed_consumption': line.threshold_value_id.consumption_method == 'fmc',
399
 
                        'past_consumption': line.threshold_value_id.consumption_method == 'amc',
400
 
                        'manual_consumption': 0.00,
401
 
                        'consumption_period_from': line.threshold_value_id.consumption_period_from,
402
 
                        'consumption_period_to': line.threshold_value_id.consumption_period_to,
403
 
                        'leadtime': line.threshold_value_id.lead_time,
404
 
                        'coverage': line.threshold_value_id.frequency,
405
 
                        'safety_stock': 0.00,
406
 
                        'safety_time': line.threshold_value_id.safety_month}
407
 
            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))
408
 
 
409
 
            new_context = context.copy()
410
 
            new_context.update({'from_date': from_date,
411
 
                                'to_date': to_date,
412
 
                                'get_data': True,
413
 
                                'consumption_period_from': d_values['consumption_period_from'],
414
 
                                'consumption_period_to': d_values['consumption_period_to'],})
415
 
 
416
 
            qty_to_order, req_date = proc_obj._compute_quantity(cr, uid, False, line.product_id, line.threshold_value_id.location_id.id, d_values, context=new_context)
417
 
 
418
 
            res[line.id] = {'consumption': consu,
419
 
                            'real_stock': stock_product.qty_available,
420
 
                            'available_stock': stock_product.virtual_available,
421
 
                            'expiry_before': expiry_product_qty,
422
 
                            'supplier_id': stock_product.seller_id.id,
423
 
                            'required_date': req_date,
424
 
                            }
425
 
 
 
231
        
426
232
        return res
427
233
    
428
234
    _columns = {
429
235
        'product_id': fields.many2one('product.product', string='Product', required=True),
430
236
        'product_uom_id': fields.many2one('product.uom', string='Product UoM', required=True),
431
 
        'product_qty': fields.function(_get_values, method=True, type='float', string='Quantity to order'),
432
 
        'fake_threshold_value': fields.float(digits=(16,2), string='Threshold value'),
433
 
        'threshold_value': fields.function(_get_values, method=True, type='float', string='Threshold value',
434
 
                                           store={'threshold.value.line': (lambda self, cr, uid, ids, c=None: ids, ['product_id'],20),
435
 
                                                  'threshold.value': (_get_threshold, ['compute_method',
436
 
                                                                                       'consumption_method',
437
 
                                                                                       'consumption_period_from',
438
 
                                                                                       'consumption_period_to',
439
 
                                                                                       'frequency',
440
 
                                                                                       'safety_month',
441
 
                                                                                       'lead_time',
442
 
                                                                                       'supplier_lt'], 10)}),
 
237
        'product_qty': fields.function(_get_values, method=True, type='float', string='Quantity to order', multi='values'),
 
238
        'threshold_value': fields.function(_get_values, method=True, type='float', string='Threshold value', multi='values'),
443
239
        'fixed_product_qty': fields.float(digits=(16,2), string='Quantity to order'),
444
240
        'fixed_threshold_value': fields.float(digits=(16,2), string='Threshold value'),
445
241
        'threshold_value_id': fields.many2one('threshold.value', string='Threshold', ondelete='cascade', required=True),
446
 
        'threshold_value_id2': fields.many2one('threshold.value', string='Threshold', ondelete='cascade', required=True),
447
 
        'consumption': fields.function(_get_data, method=True, type='float', digits=(16,3), string='AMC/FMC', multi='data', readonly=True),
448
 
        'real_stock': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Real stock', multi='data', readonly=True),
449
 
        'available_stock': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Available stock', multi='data', readonly=True),
450
 
        'expiry_before': fields.function(_get_data, method=True, type='float', digits=(16,3), string='Exp. before consumption', multi='data', readonly=True),
451
 
        'supplier_id': fields.function(_get_data, method=True, type='many2one', relation='res.partner', string='Supplier', multi='data', readonly=True),
452
 
        'required_date': fields.function(_get_data, method=True, type='date', string='Required by date', multi='data', readonly=True),
 
242
        'threshold_value_id2': fields.many2one('threshold.value', string='Threshold', ondelete='cascade', required=True)
453
243
    }
454
244
    
455
 
    def _check_uniqueness(self, cr, uid, ids, context=None):
456
 
        '''
457
 
        Check if the product is not already in the current rule
458
 
        '''
459
 
        for line in self.browse(cr, uid, ids, context=context):
460
 
            lines = self.search(cr, uid, [('id', '!=', line.id), 
461
 
                                          ('product_id', '=', line.product_id.id),
462
 
                                          '|',
463
 
                                          ('threshold_value_id2', '=', line.threshold_value_id2.id),
464
 
                                          ('threshold_value_id', '=', line.threshold_value_id.id)], context=context)
465
 
            if lines:
466
 
                return False
467
 
            
468
 
        return True
469
 
    
470
 
    _constraints = [
471
 
        (_check_uniqueness, 'You cannot have two times the same product on the same threshold value rule', ['product_id'])
472
 
    ]
473
 
    
474
245
    def _get_threshold_value(self, cr, uid, line_id, product, compute_method, consumption_method,
475
246
                                consumption_period_from, consumption_period_to, frequency,
476
247
                                safety_month, lead_time, supplier_lt, uom_id, context=None):
479
250
        '''
480
251
        if not context:
481
252
            context = {}
482
 
 
483
 
        if line_id and isinstance(line_id, list):
484
 
            line_id = line_id[0]
485
253
        
486
254
        cons = 0.00
487
255
        threshold_value = 0.00
516
284
            
517
285
        return {'threshold_value': threshold_value, 'product_qty': qty_to_order}
518
286
 
519
 
    def onchange_product_id(self, cr, uid, ids, product_id, compute_method=False, consumption_method=False,
520
 
                                consumption_period_from=False, consumption_period_to=False, frequency=False,
521
 
                                safety_month=False, lead_time=False, supplier_lt=False, fixed_tv=0.00, 
522
 
                                fixed_qty=0.00, uom_id=False, field='product_id', context=None):
 
287
    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
523
288
        """ Finds UoM for changed product.
524
289
        @param product_id: Changed id of product.
525
290
        @return: Dictionary of values.
526
291
        """
527
 
        if not context:
528
 
            context = {}
529
 
        
530
 
        res = {'value': {'product_uom_id': False,
531
 
                         'fake_threshold_value': 0.00,
532
 
                         'threshold_value': 0.00}}
533
292
        if product_id:
534
293
            prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
535
 
            if field == 'product_id':
536
 
                res['value'].update({'product_uom_id': prod.uom_id.id})
537
 
            elif uom_id:
538
 
                res['value'].update({'product_uom_id': uom_id})
539
 
            
540
 
            if compute_method:
541
 
                tv = self._get_threshold_value(cr, uid, ids, prod, compute_method, consumption_method,
542
 
                                               consumption_period_from, consumption_period_to, frequency,
543
 
                                               safety_month, lead_time, supplier_lt, uom_id or prod.uom_id.id, context=context)['threshold_value']
544
 
                res['value'].update({'fake_threshold_value': tv, 'threshold_value': tv})
545
 
 
546
 
                if prod.uom_id.id:
547
 
                    res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom_id or prod.uom_id.id, tv, ['fixed_threshold_value', 'fixed_product_qty', 'threshold_value', 'fake_threshold_value'], result=res)
548
 
                if prod.uom_id.id and fixed_tv:
549
 
                    res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom_id or prod.uom_id.id, fixed_tv, ['fixed_threshold_value'], result=res)
550
 
                if prod.uom_id.id and fixed_qty:
551
 
                    res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom_id or prod.uom_id.id, fixed_tv, ['fixed_product_qty'], result=res)
552
 
 
553
 
        return res
554
 
 
555
 
    def onchange_uom_qty(self, cr, uid, ids, uom_id, tv_qty, product_qty):
556
 
        '''
557
 
        Check round of qty according to UoM
558
 
        '''
559
 
        res = {}
560
 
        uom_obj = self.pool.get('product.uom')
561
 
 
562
 
        if tv_qty:
563
 
            res = uom_obj._change_round_up_qty(cr, uid, uom_id, tv_qty, 'fixed_threshold_value', result=res)
564
 
 
565
 
        if product_qty:
566
 
            res = uom_obj._change_round_up_qty(cr, uid, uom_id, product_qty, 'fixed_product_qty', result=res)
567
 
 
568
 
        return res
569
 
 
 
294
            v = {'product_uom_id': prod.uom_id.id}
 
295
            return {'value': v}
 
296
        return {}
 
297
    
570
298
threshold_value_line()