1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2011 MSF, TeMPO Consulting.
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as
9
# published by the Free Software Foundation, either version 3 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
22
from osv import osv, fields
23
from tools.translate import _
25
from mx.DateTime import *
26
from datetime import date, timedelta, datetime
30
class expiry_quantity_report(osv.osv_memory):
31
_name = 'expiry.quantity.report'
32
_description = 'Products Expired'
34
def _get_date_to(self, cr, uid, ids, field_name, arg, context=None):
36
Compute the end date for the calculation
43
for report in self.browse(cr, uid, ids, context=context):
44
res[report.id] = (date.today() + timedelta(weeks=report.week_nb)).strftime('%Y-%m-%d')
49
'location_id': fields.many2one('stock.location', string='Location'),
50
'input_output_ok': fields.boolean(string='Exclude Input and Output locations'),
51
'week_nb': fields.integer(string='Period of calculation (Today till XX weeks)', required=True),
52
'date_to': fields.function(_get_date_to, method=True, type='date', string='Limit date', readonly=True),
53
'line_ids': fields.one2many('expiry.quantity.report.line', 'report_id', string='Products', readonly=True),
56
def print_report_wizard(self, cr, uid, ids, context=None):
58
Print the report directly from the wizard
60
self.process_lines(cr, uid, ids, context=context)
61
return self.print_report(cr, uid, ids, context=context)
63
def print_report(self, cr, uid, ids, context=None):
65
Print the report of expiry report
70
'type': 'ir.actions.report.xml',
71
'report_name': 'expiry.report',
78
def process_lines(self, cr, uid, ids, context=None):
80
Creates all lines of expired products
85
move_obj = self.pool.get('stock.move')
86
lot_obj = self.pool.get('stock.production.lot')
87
loc_obj = self.pool.get('stock.location')
91
report = self.browse(cr, uid, ids[0], context=context)
92
lot_ids = lot_obj.search(cr, uid, [('life_date', '<=', (date.today() + timedelta(weeks=report.week_nb)).strftime('%Y-%m-%d'))])
93
domain = [('date', '<=', (date.today() + timedelta(weeks=report.week_nb)).strftime('%Y-%m-%d')), ('state', '=', 'done'), ('prodlot_id', 'in', lot_ids)]
94
domain_out = [('date', '<=', (date.today() + timedelta(weeks=report.week_nb)).strftime('%Y-%m-%d')), ('state', '=', 'done'), ('prodlot_id', 'in', lot_ids)]
97
# Remove input and output location
98
if report.input_output_ok:
99
wh_ids = self.pool.get('stock.warehouse').search(cr, uid, [], context=context)
100
for wh in self.pool.get('stock.warehouse').browse(cr, uid, wh_ids, context=context):
101
not_loc_ids.extend(loc_obj.search(cr, uid, [('location_id', 'child_of', wh.lot_input_id.id)], context=context))
102
not_loc_ids.extend(loc_obj.search(cr, uid, [('location_id', 'child_of', wh.lot_output_id.id)], context=context))
104
if report.location_id:
105
# Search all children locations of the report location
106
loc_ids = loc_obj.search(cr, uid, [('location_id', 'child_of', report.location_id.id), ('quarantine_location', '=', False), ('usage', '=', 'internal'), ('id', 'not in', not_loc_ids)], context=context)
108
# Search all locations according to parameters
109
loc_ids = loc_obj.search(cr, uid, [('usage', '=', 'internal'), ('quarantine_location', '=', False), ('id', 'not in', not_loc_ids)], context=context)
111
# Return the good view
112
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'consumption_calculation', 'expiry_quantity_report_processed_loc_view')[1]
113
domain.append(('location_dest_id', 'in', loc_ids))
114
domain_out.append(('location_id', 'in', loc_ids))
116
move_ids = move_obj.search(cr, uid, domain, context=context)
117
for move in move_obj.browse(cr, uid, move_ids, context=context):
119
lot_id = move.prodlot_id.id
120
# Add the lot in the list
121
if lot_id not in lots:
124
# Add the location in the lot list
125
if move.location_dest_id.id not in lots[lot_id]:
126
lots[lot_id][move.location_dest_id.id] = 0.00
128
lots[lot_id][move.location_dest_id.id] += move.product_qty
131
move_out_ids = move_obj.search(cr, uid, domain_out, context=context)
132
for move in move_obj.browse(cr, uid, move_out_ids, context=context):
133
if move.prodlot_id and move.prodlot_id.id in lots and move.location_id.id in lots[move.prodlot_id.id]:
134
lots[move.prodlot_id.id][move.location_id.id] -= move.product_qty
136
for lot_location in lots:
137
lot_brw = lot_obj.browse(cr, uid, lot_location, context=context)
138
for location in lots[lot_location]:
139
if lots[lot_location][location] > 0.00:
140
context.update({'location': location, 'compute_child': False})
141
real_qty = lot_obj.browse(cr, uid, lot_location, context=context).product_id.qty_available
142
self.pool.get('expiry.quantity.report.line').create(cr, uid, {'product_id': lot_brw.product_id.id,
143
'uom_id': lot_brw.product_id.uom_id.id,
144
'real_stock': real_qty,
145
'expired_qty': lots[lot_location][location],
146
'batch_number': lot_brw.name,
147
'expiry_date': lot_brw.life_date,
148
'location_id': location,
152
return {'type': 'ir.actions.act_window',
153
'res_model': 'expiry.quantity.report',
157
'view_id': [view_id],
161
expiry_quantity_report()
164
class expiry_quantity_report_line(osv.osv_memory):
165
_name = 'expiry.quantity.report.line'
166
_description = 'Products expired line'
167
_order = 'expiry_date, location_id, product_id asc'
170
'report_id': fields.many2one('expiry.quantity.report', string='Report', required=True),
171
'product_id': fields.many2one('product.product', string='Product', required=True),
172
'product_code': fields.related('product_id', 'default_code', string='Ref.', type='char'),
173
'product_name': fields.related('product_id', 'name', string='Name', type='char'),
174
'uom_id': fields.related('product_id', 'uom_id', string='UoM', type='many2one', relation='product.uom'),
175
'real_stock': fields.float(digits=(16, 2), string='Real stock'),
176
'expired_qty': fields.float(digits=(16, 2), string='Batch exp.'),
177
#'batch_number': fields.many2one('production.lot', string='Batch'),
178
'batch_number': fields.char(size=64, string='Batch'),
179
'expiry_date': fields.date(string='Exp. date'),
180
'location_id': fields.many2one('stock.location', string='Loc.'),
183
expiry_quantity_report_line()
186
class product_likely_expire_report(osv.osv_memory):
187
_name = 'product.likely.expire.report'
188
_description = 'Products list likely to expire'
191
'location_id': fields.many2one('stock.location', string='Location'),
192
'msf_instance': fields.char(size=64, string='Location', readonly=True),
193
'input_output_ok': fields.boolean(string='Exclude Input and Output locations'),
194
'date_from': fields.date(string='From', required=True, readonly=True),
195
'date_to': fields.date(string='To', required=True),
196
'consumption_type': fields.selection([('fmc', 'FMC -- Forecasted Monthly Consumption'),
197
('amc', 'AMC -- Average Monthly Consumption'),
198
('rac', 'RAC -- Real Average Consumption')], string='Consumption', required=True),
199
'line_ids': fields.one2many('product.likely.expire.report.line', 'report_id', string='Lines', readonly=True),
200
'consumption_from': fields.date(string='From'),
201
'consumption_to': fields.date(string='To'),
202
'only_non_zero': fields.boolean(string='Only products with total expired > 0'),
206
'date_from': lambda *a: time.strftime('%Y-%m-%d'),
207
'consumption_to': lambda *a: time.strftime('%Y-%m-%d'),
208
'consumption_type': lambda *a: 'fmc',
209
'msf_instance': lambda *a: 'MSF Instance',
212
def period_change(self, cr, uid, ids, consumption_from, consumption_to, consumption_type, context=None):
214
Get the first or last day of month
218
if consumption_type == 'amc':
220
res.update({'consumption_from': (DateFrom(consumption_from) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')})
222
res.update({'consumption_to': (DateFrom(consumption_to) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')})
224
return {'value': res}
227
def _get_average_consumption(self, cr, uid, product_id, consumption_type, date_from, date_to, context=None):
229
Return the average consumption for all locations
234
product_obj = self.pool.get('product.product')
237
if context.get('manual_consumption'):
238
return context.get('manual_consumption')
240
new_context = context.copy()
241
new_context.update({'from_date': date_from,
245
if consumption_type == 'fmc':
246
res = product_obj.browse(cr, uid, product_id, context=new_context).reviewed_consumption
247
elif consumption_type == 'amc':
248
res = product_obj.compute_amc(cr, uid, product_id, context=new_context)
250
res = product_obj.browse(cr, uid, product_id, context=new_context).monthly_consumption
254
def process_lines(self, cr, uid, ids, context=None):
256
Creates all moves with expiry quantities for all
262
if isinstance(ids, (int, long)):
265
move_obj = self.pool.get('stock.move')
266
lot_obj = self.pool.get('stock.production.lot')
267
loc_obj = self.pool.get('stock.location')
268
product_obj = self.pool.get('product.product')
269
line_obj = self.pool.get('product.likely.expire.report.line')
270
item_obj = self.pool.get('product.likely.expire.report.item')
271
item_line_obj = self.pool.get('product.likely.expire.report.item.line')
273
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'consumption_calculation', 'product_likely_expire_report_form_processed')[1]
274
report = self.browse(cr, uid, ids[0], context=context)
276
if report.date_to <= report.date_from:
277
raise osv.except_osv(_('Error'), _('You cannot have \'To date\' older than \'From date\''))
279
if report.consumption_type in ('amc', 'rac') and report.consumption_from > report.consumption_to:
280
raise osv.except_osv(_('Error'), _('You cannot have \'To date\' older than \'From date\''))
282
if report.consumption_type in ('amc', 'rac'):
283
context.update({'from': report.consumption_from, 'to': report.consumption_to})
285
context.update({'from': report.date_from, 'to': report.date_to})
289
only_product_ids = []
291
if context.get('only_product_ids'):
292
only_product_ids = context.get('only_product_ids')
294
if report.input_output_ok:
295
wh_ids = self.pool.get('stock.warehouse').search(cr, uid, [], context=context)
296
for wh in self.pool.get('stock.warehouse').browse(cr, uid, wh_ids, context=context):
297
not_loc_ids.extend(loc_obj.search(cr, uid, [('location_id', 'child_of', wh.lot_input_id.id)], context=context))
298
not_loc_ids.extend(loc_obj.search(cr, uid, [('location_id', 'child_of', wh.lot_output_id.id)], context=context))
300
if report.location_id:
302
location_ids = loc_obj.search(cr, uid, [('location_id', 'child_of', report.location_id.id), ('quarantine_location', '=', False), ('id', 'not in', not_loc_ids)], order='location_id', context=context)
305
wh_location_ids = loc_obj.search(cr, uid, [('usage', '=', 'internal'), ('quarantine_location', '=', False), ('id', 'not in', not_loc_ids)], order='location_id', context=context)
307
move_ids = move_obj.search(cr, uid, [('prodlot_id', '!=', False)], context=context)
308
for move in move_obj.browse(cr, uid, move_ids, context=context):
309
if move.location_id.id not in location_ids:
310
if move.location_id.usage == 'internal' and not move.location_id.quarantine_location and move.location_id.id in wh_location_ids:
311
location_ids.append(move.location_id.id)
312
if move.location_dest_id.id not in location_ids and not move.location_dest_id.quarantine_location and move.location_dest_id.id in wh_location_ids:
313
if move.location_dest_id.usage == 'internal':
314
location_ids.append(move.location_dest_id.id)
316
context.update({'location_id': location_ids, 'location': location_ids})
318
lot_domain = [('stock_available', '>', 0.00)]
320
lot_domain.append(('product_id', 'in', only_product_ids))
322
lot_ids = lot_obj.search(cr, uid, lot_domain, order='product_id, life_date', context=context)
324
from_date = DateFrom(report.date_from)
325
to_date = DateFrom(report.date_to) + RelativeDateTime(day=1, months=1, days=-1)
327
# Set all months between from_date and to_date
329
while (from_date < to_date):
330
dates.append(from_date)
331
from_date = from_date + RelativeDateTime(months=1, day=1)
333
# Create a report line for each product
335
for lot in lot_obj.browse(cr, uid, lot_ids, context=context):
336
if lot.product_id and lot.product_id.id not in products:
337
products.update({lot.product_id.id: {}})
338
consumption = self._get_average_consumption(cr, uid, lot.product_id.id,
339
report.consumption_type,
340
context.get('from', report.date_from),
341
context.get('to', report.date_to),
344
products[lot.product_id.id].update({'line_id': line_obj.create(cr, uid, {'report_id': report.id,
345
'product_id': lot.product_id.id,
346
'in_stock': lot.product_id.qty_available,
347
'total_expired': 0.00,
348
'consumption': consumption,}, context=context)})
350
# Create an item for each date
356
start_month_flag = True
357
last_expiry_date = False
359
# Remove one day to include the expiry date as possible consumable day
360
if not last_expiry_date: last_expiry_date = month - RelativeDateTime(days=1)
362
item_id = item_obj.create(cr, uid, {'name': month.strftime('%m/%y'),
363
'line_id': products[lot.product_id.id]['line_id']}, context=context)
368
# Create a line for each lot which expired in this month
369
domain = [('product_id', '=', lot.product_id.id),
370
('stock_available', '>', 0.00),
371
('life_date', '<', (month + RelativeDateTime(months=1, day=1)).strftime('%Y-%m-%d'))]
373
if not start_month_flag:
374
domain.append(('life_date', '>=', month.strftime('%Y-%m-%d')))
375
item_obj.write(cr, uid, [item_id], {'period_start': (month + RelativeDateTime(day=1)).strftime('%Y-%m-%d')}, context=context)
377
item_obj.write(cr, uid, [item_id], {'period_start': report.date_from}, context=context)
378
# Uncomment the first line if you want products already expired in the first month
379
#domain.append(('life_date', '>=', month.strftime('%Y-%m-01')))
380
# Comment line if you want all products already expired
381
#if not context.get('only_product_ids'):
382
# domain.append(('life_date', '>=', month.strftime('%Y-%m-%d')))
385
# Remove the token after the first month processing
386
start_month_flag = False
388
product_lot_ids = lot_obj.search(cr, uid, domain, order='life_date', context=context)
390
# Create an item line for each lot and each location
391
for product_lot in lot_obj.browse(cr, uid, product_lot_ids, context=context):
392
lot_days = Age(DateFrom(product_lot.life_date), last_expiry_date)
393
lot_coeff = (lot_days.years*365.0 + lot_days.months*30.0 + lot_days.days)/30.0
394
if lot_coeff >= 0.00: last_expiry_date = DateFrom(product_lot.life_date)
395
if lot_coeff < 0.00: lot_coeff = 0.00
396
lot_cons = self.pool.get('product.uom')._compute_qty(cr, uid, lot.product_id.uom_id.id, round(lot_coeff*consumption,2), lot.product_id.uom_id.id) + rest
399
if lot_cons >= product_lot.stock_available:
400
already_cons += product_lot.stock_available
401
rest = lot_cons - product_lot.stock_available
404
l_expired_qty = product_lot.stock_available - lot_cons
405
already_cons += lot_cons
408
l_expired_qty = product_lot.stock_available
409
expired_qty += l_expired_qty
411
lot_context = context.copy()
412
lot_context.update({'prodlot_id': product_lot.id})
413
product = product_obj.browse(cr, uid, lot.product_id.id, context=lot_context)
414
lot_expired_qty = l_expired_qty
415
for location in location_ids:
416
new_lot_context = lot_context.copy()
417
new_lot_context.update({'location': location, 'compute_child': False})
418
product2 = product_obj.browse(cr, uid, lot.product_id.id, context=new_lot_context)
419
if product2.qty_available > 0.00:
420
# Create the item line
421
if product2.qty_available <= lot_expired_qty:
422
new_lot_expired = product2.qty_available
423
lot_expired_qty -= product2.qty_available
425
new_lot_expired = lot_expired_qty
426
lot_expired_qty = 0.00
427
item_line_obj.create(cr, uid, {'item_id': item_id,
428
'lot_id': product_lot.id,
429
'location_id': location,
430
'available_qty': product2.qty_available,
431
'expired_qty': new_lot_expired}, context=context)
433
available_qty += product.qty_available
435
item_obj.write(cr, uid, [item_id], {'available_qty': available_qty,
436
'expired_qty': expired_qty}, context=context)
437
total_expired += expired_qty
439
if report.only_non_zero and total_expired <= 0.00:
440
line_obj.unlink(cr, uid, [products[lot.product_id.id]['line_id']], context=context)
442
line_obj.write(cr, uid, [products[lot.product_id.id]['line_id']], {'total_expired': total_expired}, context=context)
446
new_date.append(date.strftime('%m/%y'))
448
context.update({'dates': new_date})
450
return {'type': 'ir.actions.act_window',
451
'res_model': 'product.likely.expire.report',
453
'view_id': [view_id],
460
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
464
res = super(product_likely_expire_report, self).fields_view_get(cr, uid, view_id, view_type, context=context)
466
line_view = """<tree string="Expired products">
467
<field name="product_id"/>
468
<field name="consumption"/>
471
dates = context.get('dates', [])
473
line_view += '<field name="%s" />' % month
474
line_view += '<button name="go_to_item_%s" type="object" string="Go to item" icon="gtk-info" context="{item_date: %s}" />' % (month, month)
476
line_view += """<field name="in_stock"/>
477
<field name="total_expired" />
480
if res['fields'].get('line_ids', {}).get('views', {}).get('tree', {}).get('arch', {}):
481
res['fields']['line_ids']['views']['tree']['arch'] = line_view
485
product_likely_expire_report()
488
class product_likely_expire_report_line(osv.osv_memory):
489
_name = 'product.likely.expire.report.line'
492
'report_id': fields.many2one('product.likely.expire.report', string='Report', required=True, ondelete='cascade'),
493
'product_id': fields.many2one('product.product', string='Product', required=True),
494
'consumption': fields.float(digits=(16,2), string='Monthly Consumption', required=True),
495
'in_stock': fields.float(digits=(16,2), string='In stock'),
496
'total_expired': fields.float(digits=(16,2), string='Total expired'),
499
def __getattr__(self, name, *args, **kwargs):
500
if name[:11] == 'go_to_item_':
503
return self.go_to_item
505
return super(product_likely_expire_report_line, self).__getattr__(name, *args, **kwargs)
507
def fields_get(self, cr, uid, fields=None, context=None):
511
res = super(product_likely_expire_report_line, self).fields_get(cr, uid, fields, context)
512
dates = context.get('dates', [])
515
res.update({month: {'selectable': True,
517
'relation': 'product.likely.expire.report.item',
522
def go_to_item(self, cr, uid, ids, context=None):
526
if not context.get('item_date', self.date):
527
raise osv.except_osv(_('Error'), _('You haven\'t choose an item to open'))
529
item_date = context.get('item_date', self.date)
530
item_ids = self.pool.get('product.likely.expire.report.item').search(cr, uid, [('name', '=', item_date), ('line_id', '=', ids[0])], context=context)
532
raise osv.except_osv(_('Error'), _('You haven\'t choose an item to open'))
534
return {'type': 'ir.actions.act_window',
535
'res_model': 'product.likely.expire.report.item',
536
'res_id': item_ids[0],
543
def read(self, cr, uid, ids, vals, context=None, load='_classic_read'):
545
Set values for all dates
548
res = super(product_likely_expire_report_line, self).read(cr, uid, ids, vals, context=context, load=load)
550
item_obj = self.pool.get('product.likely.expire.report.item')
552
exp_ids = item_obj.search(cr, uid, [('line_id', '=', r['id'])], context=context)
553
for exp in item_obj.browse(cr, uid, exp_ids, context=context):
554
r.update({exp.name: ''})
555
if exp.expired_qty > 0.00:
556
name = '%s (%s)' % (exp.available_qty, exp.expired_qty)
558
# Be careful to the undividable spaces
559
name = ' %s' % (exp.available_qty)
561
r.update({exp.name: name})
566
product_likely_expire_report_line()
569
class product_likely_expire_report_item(osv.osv_memory):
570
_name = 'product.likely.expire.report.item'
573
'line_id': fields.many2one('product.likely.expire.report.line', string='Line', ondelete='cascade'),
574
'name': fields.char(size=64, string='Month'),
575
'available_qty': fields.float(digits=(16,2), string='Available Qty.'),
576
'expired_qty': fields.float(digits=(16,2), string='Expired Qty.'),
577
'period_start': fields.date(string='Period start', readonly=True),
578
'line_ids': fields.one2many('product.likely.expire.report.item.line', 'item_id', string='Batchs'),
581
product_likely_expire_report_item()
584
class product_likely_expire_report_item_line(osv.osv_memory):
585
_name = 'product.likely.expire.report.item.line'
586
_order = 'expired_date, location_id'
589
'item_id': fields.many2one('product.likely.expire.report.item', strig='Item', ondelete='cascade'),
590
'lot_id': fields.many2one('stock.production.lot', string='Batch number'),
591
'location_id': fields.many2one('stock.location', string='Location'),
592
'available_qty': fields.float(digits=(16,2), string='Available Qty.'),
593
'expired_qty': fields.float(digits=(16,2), string='Expired Qty.'),
594
'expired_date': fields.related('lot_id', 'life_date', type='date', string='Expiry date'),
597
product_likely_expire_report_item_line()
600
class product_product(osv.osv):
601
_name = 'product.product'
602
_inherit = 'product.product'
604
def get_expiry_qty(self, cr, uid, product_id, location_id, monthly_consumption, d_values=None, context=None):
606
Get the expired quantity of product
614
monthly_consumption = 'rac'
616
# Get the monthly consumption
617
if d_values.get('reviewed_consumption', False):
618
monthly_consumption = 'fmc'
619
elif d_values.get('past_consumption', False):
620
monthly_consumption = 'amc'
622
monthly_consumption = d_values.get('manual_consumption', 0.00)
623
context.update({'manual_consumption': monthly_consumption})
625
product = self.browse(cr, uid, product_id, context=context)
626
# Get the delivery lead time of the product if the leadtime is not defined in rule and no supplier found in product form
627
delivery_leadtime = product.procure_delay and round(int(product.procure_delay)/30.0, 2) or 1
628
# Get the leadtime of the rule if defined
629
if 'leadtime' in d_values and d_values.get('leadtime', 0.00) != 0.00:
630
delivery_leadtime = d_values.get('leadtime')
631
elif product.seller_ids:
632
# Get the supplier lead time if supplier is defined
633
# The seller delay is defined in days, so divide it by 30.0 to have a LT in months
634
delivery_leadtime = product.seller_delay and round(int(product.seller_delay)/30.0, 2) or 1
636
delta = (delivery_leadtime + d_values.get('coverage', 0.00))*30.0
638
report_data = {'date_from': today().strftime('%Y-%m-%d'),
639
'date_to': (today() + RelativeDateTime(days=delta)).strftime('%Y-%m-%d'),
640
'consumption_type': monthly_consumption,
641
'consumption_from': d_values.get('consumption_period_from'),
642
'consumption_to': d_values.get('consumption_period_to'),
643
'location_id': location_id}
645
report_obj = self.pool.get('product.likely.expire.report')
646
line_obj = self.pool.get('product.likely.expire.report.line')
648
exp_context = context.copy()
649
exp_context.update({'only_product_ids': [product_id]})
650
report_id = report_obj.create(cr, uid, report_data, context=exp_context)
652
report_obj.process_lines(cr, uid, report_id, context=exp_context)
654
lines = line_obj.search(cr, uid, [('report_id', '=', report_id)], context=context)
655
for line in line_obj.browse(cr, uid, lines, context=context):
656
if line.product_id.id == product_id:
657
return line.total_expired
660
# location_ids = stock_obj.search(cr, uid, [('location_id', 'child_of', location_id)])
662
# move_ids = move_obj.search(cr, uid, ['|', ('location_id', 'in', location_ids), ('location_dest_id', 'in', location_ids),
663
# ('product_id', '=', product_id), ('prodlot_id', '!=', False)], context=context)
666
# for move in move_obj.browse(cr, uid, move_ids, context=context):
667
# if not move.prodlot_id.id in lots:
668
# lots.append(move.prodlot_id.id)
670
# # Get all lots for the product product_id
671
# lot_ids = lot_obj.search(cr, uid, [('product_id', '=', product_id), ('stock_available', '>', 0.00), ('id', 'in', lots)], \
672
# order='life_date', context=context)
676
# # Sum of months before expiry
682
# for lot in lot_obj.browse(cr, uid, lot_ids, context=context):
683
# life_date = strptime(lot.life_date, '%Y-%m-%d')
684
# rel_time = RelativeDateDiff(life_date, now())
685
# ni = round((rel_time.months*30 + rel_time.days)/30.0, 2)
686
# if last_qty == False:
687
# last_qty = uom_obj._compute_qty(cr, uid, lot.product_id.uom_id.id, (ni-sum_ni)*monthly_consumption, lot.product_id.uom_id.id)
688
# if last_date > life_date:
689
# expired_qty = lot.stock_available
690
# elif ni - sum_ni > 0.00:
691
# expired_qty += last_qty
692
# last_qty = uom_obj._compute_qty(cr, uid, lot.product_id.uom_id.id, (ni-sum_ni)*monthly_consumption, lot.product_id.uom_id.id)
700
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: