21
21
##############################################################################
24
from osv import fields
25
from tools.translate import _
23
from osv import osv, fields
27
from mx.DateTime import DateFrom
28
from mx.DateTime import now
29
from mx.DateTime import RelativeDate
25
from mx.DateTime import *
31
27
class threshold_value(osv.osv):
32
28
_name = 'threshold.value'
33
29
_description = 'Threshold value'
35
def _get_product_ids(self, cr, uid, ids, field_name, arg, context=None):
37
Returns a list of products for the rule
41
for rule in self.browse(cr, uid, ids, context=context):
43
for line in rule.line_ids:
44
res[rule.id].append(line.product_id.id)
48
def _src_product_ids(self, cr, uid, obj, name, args, context=None):
55
if arg[0] == 'product_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))
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'),
112
def copy(self, cr, uid, ids, defaults={}, context=None):
114
Increment the sequence
116
name = self.pool.get('ir.sequence').get(cr, uid, 'threshold.value') or ''
117
defaults.update({'name': name})
119
return super(threshold_value, self).copy(cr, uid, ids, defaults, context=context)
121
def default_get(self, cr, uid, fields, context=None):
123
Get the default values for the replenishment rule
125
res = super(threshold_value, self).default_get(cr, uid, fields, context=context)
127
company_id = res.get('company_id')
128
warehouse_id = res.get('warehouse_id')
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})
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})
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})
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.
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)
223
res[line.id] = {'threshold_value': 0.00, 'product_qty': 0.00}
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)
349
def _get_threshold(self, cr, uid, ids, context={}):
351
for t in self.pool.get('threshold.value').browse(cr, uid, ids, context=context):
357
def _get_data(self, cr, uid, ids, field_name, args, context=None):
361
product_obj = self.pool.get('product.product')
362
proc_obj = self.pool.get('procurement.order')
364
if isinstance(ids, (int, long)):
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
375
'available_stock': 0.,
376
'expiry_before': False,
377
'supplier_id': False,
378
'required_date': False,
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))
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))
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
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))
409
new_context = context.copy()
410
new_context.update({'from_date': from_date,
413
'consumption_period_from': d_values['consumption_period_from'],
414
'consumption_period_to': d_values['consumption_period_to'],})
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)
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,
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',
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)
455
def _check_uniqueness(self, cr, uid, ids, context=None):
457
Check if the product is not already in the current rule
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),
463
('threshold_value_id2', '=', line.threshold_value_id2.id),
464
('threshold_value_id', '=', line.threshold_value_id.id)], context=context)
471
(_check_uniqueness, 'You cannot have two times the same product on the same threshold value rule', ['product_id'])
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):
517
285
return {'threshold_value': threshold_value, 'product_qty': qty_to_order}
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.
530
res = {'value': {'product_uom_id': False,
531
'fake_threshold_value': 0.00,
532
'threshold_value': 0.00}}
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})
538
res['value'].update({'product_uom_id': uom_id})
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})
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)
555
def onchange_uom_qty(self, cr, uid, ids, uom_id, tv_qty, product_qty):
557
Check round of qty according to UoM
560
uom_obj = self.pool.get('product.uom')
563
res = uom_obj._change_round_up_qty(cr, uid, uom_id, tv_qty, 'fixed_threshold_value', result=res)
566
res = uom_obj._change_round_up_qty(cr, uid, uom_id, product_qty, 'fixed_product_qty', result=res)
294
v = {'product_uom_id': prod.uom_id.id}
570
298
threshold_value_line()