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

« back to all changes in this revision

Viewing changes to consumption_calculation/consumption_calculation.py

  • Committer: Olivier DOSSMANN
  • Date: 2014-03-31 09:31:46 UTC
  • mto: This revision was merged to the branch mainline in revision 2086.
  • Revision ID: od@tempo-consulting.fr-20140331093146-tgvxnly1kc1hbv1s
UF-2171 [ADD] Analytic distribution reset button for recurring models

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
import base64
30
30
import netsvc
31
31
 
 
32
import csv
 
33
from tempfile import TemporaryFile
 
34
from product._common import rounding
 
35
from spreadsheet_xml.spreadsheet_xml_write import SpreadsheetCreator
 
36
 
 
37
 
 
38
def _get_asset_mandatory(product):
 
39
    return product.type == 'product' and product.subtype == 'asset'
 
40
 
32
41
 
33
42
class real_average_consumption(osv.osv):
34
43
    _name = 'real.average.consumption'
35
44
    _description = 'Real Average Consumption'
36
45
    
37
 
    def _get_nb_lines(self, cr, uid, ids, field_name, args, context={}):
 
46
    def _get_nb_lines(self, cr, uid, ids, field_name, args, context=None):
38
47
        '''
39
48
        Returns the # of lines on the real average consumption
40
49
        '''
45
54
            
46
55
        return res
47
56
 
48
 
 
49
 
    def unlink(self, cr, uid, ids, context={}):
 
57
    def _check_active_product(self, cr, uid, ids, context=None):
 
58
        '''
 
59
        Check if the real consumption report contains a line with an inactive product
 
60
        '''
 
61
        inactive_lines = self.pool.get('real.average.consumption.line').search(cr, uid, [
 
62
            ('product_id.active', '=', False),
 
63
            ('rac_id', 'in', ids),
 
64
            ('rac_id.created_ok', '=', True)
 
65
        ], context=context)
 
66
 
 
67
        if inactive_lines:
 
68
            plural = len(inactive_lines) == 1 and _('A product has') or _('Some products have')
 
69
            l_plural = len(inactive_lines) == 1 and _('line') or _('lines')
 
70
            p_plural = len(inactive_lines) == 1 and _('this inactive product') or _('those inactive products')
 
71
            raise osv.except_osv(_('Error'), _('%s been inactivated. If you want to validate this document you have to remove/correct the %s containing %s (see red %s of the document)') % (plural, l_plural, p_plural, l_plural))
 
72
            return False
 
73
        return True
 
74
 
 
75
    def unlink(self, cr, uid, ids, context=None):
50
76
        '''
51
77
        Display a message to the user if the report has been confirmed
52
78
        and stock moves has been generated
53
79
        '''
54
80
        for report in self.browse(cr, uid, ids, context=context):
 
81
            if report.state == 'done':
 
82
                raise osv.except_osv(_('Error'), _('This report is closed. You cannot delete it !'))
55
83
            if report.created_ok and report.picking_id:
56
84
                if report.picking_id.state != 'cancel':
57
85
                    raise osv.except_osv(_('Error'), _('You cannot delete this report because stock moves has been generated and validated from this report !'))
62
90
 
63
91
        return super(real_average_consumption, self).unlink(cr, uid, ids, context=context)
64
92
 
65
 
    def copy(self, cr, uid, ids, defaults={}, context={}):
 
93
    def copy(self, cr, uid, ids, default=None, context=None):
66
94
        '''
67
95
        Unvalidate all lines of the duplicate report
68
96
        '''
69
97
        # Change default values
70
 
        if not 'picking_id' in defaults:
71
 
            defaults['picking_id'] = False
72
 
        if not 'created_ok' in defaults:
73
 
            defaults['created_ok'] = False
74
 
        if not 'valid_ok' in defaults:
75
 
            defaults['valid_ok'] = False
 
98
        if default is None:
 
99
            default = {}
 
100
        if context is None:
 
101
            context = {}
 
102
        if not 'picking_id' in default:
 
103
            default['picking_id'] = False
76
104
 
77
 
        defaults['name'] = self.pool.get('ir.sequence').get(cr, uid, 'consumption.report')
 
105
        default['name'] = self.pool.get('ir.sequence').get(cr, uid, 'consumption.report')
78
106
 
79
107
        # Copy the report
80
 
        res = super(real_average_consumption, self).copy(cr, uid, ids, defaults, context=context)
 
108
        res = super(real_average_consumption, self).copy(cr, uid, ids, default, context=context)
81
109
 
82
110
        # Unvalidate all lines of the report
83
111
        for report in self.browse(cr, uid, [res], context=context):
84
112
            lines = []
85
113
            for line in report.line_ids:
86
114
                lines.append(line.id)
87
 
 
88
 
            self.pool.get('real.average.consumption.line').write(cr, uid, lines, {'move_id': False}, context=context)
89
 
 
90
 
        return res
91
 
 
92
 
    
 
115
            if lines:
 
116
                self.pool.get('real.average.consumption.line').write(cr, uid, lines, {'move_id': False}, context=context)
 
117
 
 
118
        # update created_ok at this end to disable _check qty on line
 
119
        self.write(cr, uid, res, {'created_ok': False})
 
120
        self.button_update_stock(cr, uid, res)
 
121
        return res
 
122
 
 
123
 
 
124
    def get_bool_values(self, cr, uid, ids, fields, arg, context=None):
 
125
        res = {}
 
126
        if isinstance(ids, (int, long)):
 
127
            ids = [ids]
 
128
        for obj in self.browse(cr, uid, ids, context=context):
 
129
            res[obj.id] = False
 
130
            if any([item for item in obj.line_ids  if item.to_correct_ok]):
 
131
                res[obj.id] = True
 
132
        return res
 
133
 
93
134
    _columns = {
94
135
        'name': fields.char(size=64, string='Reference'),
95
136
        'creation_date': fields.datetime(string='Creation date', required=1),
96
137
        'cons_location_id': fields.many2one('stock.location', string='Consumer location', domain=[('usage', '=', 'internal')], required=True),
97
 
        'activity_id': fields.many2one('stock.location', string='Activity', domain=[('usage', '=', 'customer')]),
 
138
        'activity_id': fields.many2one('stock.location', string='Activity', domain=[('usage', '=', 'customer')], required=1),
98
139
        'period_from': fields.date(string='Period from', required=True),
99
140
        'period_to': fields.date(string='Period to', required=True),
100
141
        'sublist_id': fields.many2one('product.list', string='List/Sublist'),
101
 
        'nomen_id': fields.many2one('product.nomenclature', string='Products\' nomenclature level'),
102
142
        'line_ids': fields.one2many('real.average.consumption.line', 'rac_id', string='Lines'),
103
143
        'picking_id': fields.many2one('stock.picking', string='Picking', readonly=True),
104
 
        'valid_ok': fields.boolean(string='Create and process out moves'),
105
144
        'created_ok': fields.boolean(string='Out moves created'),
106
145
        'nb_lines': fields.function(_get_nb_lines, method=True, type='integer', string='# lines', readonly=True,),
 
146
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type'),
 
147
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group'),
 
148
        'nomen_manda_2': fields.many2one('product.nomenclature', 'Family'),
 
149
        'nomen_manda_3': fields.many2one('product.nomenclature', 'Root'),
 
150
        'hide_column_error_ok': fields.function(get_bool_values, method=True, readonly=True, type="boolean", string="Show column errors", store=False),
 
151
        'state': fields.selection([('draft', 'Draft'), ('done', 'Closed'),('cancel','Cancelled')], string="State", readonly=True),
107
152
    }
108
 
    
 
153
 
109
154
    _defaults = {
110
 
        'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'consumption.report'),
 
155
        #'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'consumption.report'),
111
156
        'creation_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
112
 
        'activity_id': lambda obj, cr, uid, context: obj.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_internal_cust')[1],
 
157
        'activity_id': lambda obj, cr, uid, context: obj.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_internal_customers')[1],
113
158
        'period_to': lambda *a: time.strftime('%Y-%m-%d'),
114
 
        'valid_ok': lambda *a: True,
 
159
        'nb_lines': lambda *a: 0,
 
160
        'state': lambda *a: 'draft',
115
161
    }
 
162
 
 
163
    _sql_constraints = [
 
164
        ('date_coherence', "check (period_from <= period_to)", '"Period from" must be less than or equal to "Period to"'),
 
165
    ]
 
166
 
 
167
    _constraints = [
 
168
        (_check_active_product, "You cannot confirm this real consumption report because it contains a line with an inactive product", ['line_ids', 'created_ok']),
 
169
    ]
 
170
 
 
171
    def create(self, cr, uid, vals, context=None):
 
172
        '''
 
173
        Add name of the report at creation
 
174
        '''
 
175
        if not vals:
 
176
            vals = {}
 
177
 
 
178
        if not 'name' in vals:
 
179
            vals.update({'name': self.pool.get('ir.sequence').get(cr, uid, 'consumption.report')})
 
180
 
 
181
        return super(real_average_consumption, self).create(cr, uid, vals, context=context)
 
182
 
 
183
    def change_cons_location_id(self, cr, uid, ids, context=None):
 
184
        '''
 
185
        Open the wizard to change the location
 
186
        '''
 
187
        wiz_id = self.pool.get('real.consumption.change.location').create(cr, uid, {'report_id': ids[0]}, context=context)
 
188
        return {'type': 'ir.actions.act_window',
 
189
                'res_model': 'real.consumption.change.location',
 
190
                'view_mode': 'form',
 
191
                'view_type': 'form',
 
192
                'res_id': wiz_id,
 
193
                'target': 'new'}
 
194
 
 
195
    def button_update_stock(self, cr, uid, ids, context=None):
 
196
        if isinstance(ids, (int, long)):
 
197
            ids = [ids]
 
198
        to_update = []
 
199
        for line in self.read(cr, uid, ids, ['created_ok','line_ids']):
 
200
            if line['created_ok']:
 
201
                continue
 
202
            to_update += line['line_ids']
 
203
 
 
204
        if to_update:
 
205
            self.pool.get('real.average.consumption.line')._check_qty(cr, uid, to_update, {'noraise': True})
 
206
        return True
116
207
    
117
 
    def save_and_process(self, cr, uid, ids, context={}):
 
208
    def save_and_process(self, cr, uid, ids, context=None):
118
209
        '''
119
210
        Returns the wizard to confirm the process of all lines
120
211
        '''
 
212
        if context is None:
 
213
            context = {}
 
214
        self.check_lines_to_fix(cr, uid, ids, context)
121
215
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'consumption_calculation', 'real_average_consumption_confirmation_view')[1],
122
216
        
123
217
        return {'type': 'ir.actions.act_window',
129
223
                'res_id': ids[0],
130
224
                }
131
225
        
132
 
    def process_moves(self, cr, uid, ids, context={}):
 
226
    def draft_button(self, cr, uid, ids, context=None):
 
227
        if isinstance(ids, (int, long)):
 
228
            ids = [ids]
 
229
        if context is None:
 
230
            context = {}
 
231
 
 
232
        self.write(cr, uid, ids, {'state':'draft'}, context=context)
 
233
        
 
234
        return {'type': 'ir.actions.act_window',
 
235
                'res_model': 'real.average.consumption',
 
236
                'view_type': 'form',
 
237
                'view_mode': 'form,tree',
 
238
                'target': 'dummy',
 
239
                'res_id': ids[0],
 
240
                }
 
241
 
 
242
    def cancel_button(self, cr, uid, ids, context=None):
 
243
        if isinstance(ids, (int, long)):
 
244
            ids = [ids]
 
245
        if context is None:
 
246
            context = {}
 
247
 
 
248
        self.write(cr, uid, ids, {'state':'cancel'}, context=context)
 
249
        
 
250
        return {'type': 'ir.actions.act_window',
 
251
                'res_model': 'real.average.consumption',
 
252
                'view_type': 'form',
 
253
                'view_mode': 'form,tree',
 
254
                'target': 'dummy',
 
255
                'res_id': ids[0],
 
256
                }
 
257
 
 
258
 
 
259
    def process_moves(self, cr, uid, ids, context=None):
133
260
        '''
134
261
        Creates all stock moves according to the report lines
135
262
        '''
136
263
        if isinstance(ids, (int, long)):
137
264
            ids = [ids]
 
265
        if context is None:
 
266
            context = {}
138
267
        
139
268
        move_obj = self.pool.get('stock.move')
140
269
        line_obj = self.pool.get('real.average.consumption.line')
143
272
        reason_type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_consumption_report')[1]
144
273
 
145
274
        move_ids = []
146
 
        
 
275
       
 
276
        # check and update lines
147
277
        for rac in self.browse(cr, uid, ids, context=context):
148
 
            if not rac.valid_ok:
149
 
                raise osv.except_osv(_('Error'), _('Please check the last checkbox before processing the lines'))
 
278
            if DateFrom(rac.period_to) > now():
 
279
                raise osv.except_osv(_('Error'), _('"Period to" can\'t be in the future.'))
 
280
 
150
281
            if rac.created_ok:
151
282
                return {'type': 'ir.actions.close_window'}
152
 
 
 
283
            line_obj._check_qty(cr, uid, [x.id for x in rac.line_ids])
 
284
 
 
285
        partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.partner_id.id
 
286
        addresses = self.pool.get('res.partner').address_get(cr, uid, partner_id, ['delivery', 'default'])
 
287
        address_id = addresses.get('delivery') or addresses.get('default')
 
288
 
 
289
        for rac in self.browse(cr, uid, ids, context=context):
 
290
            date = '%s %s'%(rac.period_to, time.strftime('%H:%M:%S'))
153
291
            picking_id = self.pool.get('stock.picking').create(cr, uid, {'name': 'OUT-%s' % rac.name,
154
292
                                                                         'origin': rac.name,
 
293
                                                                         'partner_id': partner_id,
 
294
                                                                         'address_id': address_id,
155
295
                                                                         'type': 'out',
156
296
                                                                         'subtype': 'standard',
157
297
                                                                         'state': 'auto',
158
298
                                                                         'move_type': 'one',
159
299
                                                                         'invoice_state': 'none',
160
 
                                                                         'date': time.strftime('%Y-%m-%d %H:%M:%S'),
 
300
                                                                         'date': date,
161
301
                                                                         'reason_type_id': reason_type_id}, context=context)
162
 
 
 
302
            
 
303
            self.write(cr, uid, [rac.id], {'created_ok': True}, context=context)
163
304
            for line in rac.line_ids:
164
 
                move_id = move_obj.create(cr, uid, {'name': '%s/%s' % (rac.name, line.product_id.name),
165
 
                                                    'picking_id': picking_id,
166
 
                                                    'product_uom': line.uom_id.id,
167
 
                                                    'product_id': line.product_id.id,
168
 
                                                    'date_expected': '%s %s'%(rac.period_to, time.strftime('%H:%M:%S')),
169
 
                                                    'date': rac.creation_date,
170
 
                                                    'product_qty': line.consumed_qty,
171
 
                                                    'prodlot_id': line.prodlot_id.id,
172
 
                                                    'expiry_date': line.expiry_date,
173
 
                                                    'location_id': rac.cons_location_id.id,
174
 
                                                    'location_dest_id': rac.activity_id.id,
175
 
                                                    'state': 'done',
176
 
                                                    'reason_type_id': reason_type_id})
177
 
                move_ids.append(move_id)
178
 
                line_obj.write(cr, uid, [line.id], {'move_id': move_id})
 
305
                if line.consumed_qty != 0.00:
 
306
                    move_id = move_obj.create(cr, uid, {'name': '%s/%s' % (rac.name, line.product_id.name),
 
307
                                                        'picking_id': picking_id,
 
308
                                                        'product_uom': line.uom_id.id,
 
309
                                                        'product_id': line.product_id.id,
 
310
                                                        'date_expected': date,
 
311
                                                        'date': date,
 
312
                                                        'product_qty': line.consumed_qty,
 
313
                                                        'prodlot_id': line.prodlot_id.id,
 
314
                                                        'expiry_date': line.expiry_date,
 
315
                                                        'asset_id': line.asset_id.id,
 
316
                                                        'location_id': rac.cons_location_id.id,
 
317
                                                        'location_dest_id': rac.activity_id.id,
 
318
                                                        'state': 'done',
 
319
                                                        'reason_type_id': reason_type_id})
 
320
                    move_ids.append(move_id)
 
321
                    line_obj.write(cr, uid, [line.id], {'move_id': move_id})
179
322
 
180
 
            self.write(cr, uid, [rac.id], {'picking_id': picking_id}, context=context)
 
323
            self.write(cr, uid, [rac.id], {'picking_id': picking_id, 'state': 'done'}, context=context)
181
324
 
182
325
            # Confirm the picking
183
326
            wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
184
327
 
185
328
            # Confirm all moves
186
329
            move_obj.action_done(cr, uid, move_ids, context=context)
 
330
            #move_obj.write(cr, uid, move_ids, {'date': rac.period_to}, context=context)
187
331
            
188
 
            self.write(cr, uid, [rac.id], {'created_ok': True}, context=context)
189
332
        
190
333
        return {'type': 'ir.actions.act_window',
191
334
                'res_model': 'real.average.consumption',
195
338
                'res_id': ids[0],
196
339
                }
197
340
        
198
 
    def import_rac(self, cr, uid, ids, context={}):
 
341
    def import_rac(self, cr, uid, ids, context=None):
199
342
        '''
200
343
        Launches the wizard to import lines from a file
201
344
        '''
 
345
        if context is None:
 
346
            context = {}
202
347
        context.update({'active_id': ids[0]})
203
348
        
204
349
        return {'type': 'ir.actions.act_window',
209
354
                'context': context,
210
355
                }
211
356
        
212
 
    def export_rac(self, cr, uid, ids, context={}):
213
 
        '''
214
 
        Creates a CSV file and launches the wizard to save it
215
 
        '''
 
357
    def export_rac(self, cr, uid, ids, context=None):
 
358
        '''
 
359
        Creates an XML file and launches the wizard to save it
 
360
        '''
 
361
        if context is None:
 
362
            context = {}
216
363
        rac = self.browse(cr, uid, ids[0], context=context)
217
 
        
218
 
        export = 'Product reference;Product name;Product UoM;Consumed Qty;Remark'
219
 
        export += '\n'
220
 
        
 
364
        header_columns = [(_('Product Code'), 'string'), (_('Product Description'),'string'),(_('Product UoM'), 'string'), (_('Batch Number'),'string'), (_('Expiry Date'), 'string'), (_('Asset'), 'string'), (_('Consumed Qty'),'string'), (_('Remark'), 'string')]
 
365
        list_of_lines = []
221
366
        for line in rac.line_ids:
222
 
            export += '%s;%s;%s;%s;%s' % (line.name.default_code, line.name.name, line.uom_id.id, line.consumed_qty, line.remark)
223
 
            export += '\n'
224
 
            
225
 
        file = base64.encodestring(export.encode("utf-8"))
 
367
            list_of_lines.append([line.product_id.default_code, line.product_id.name, line.uom_id.name, line.prodlot_id and line.prodlot_id.name, 
 
368
                                  line.expiry_date and strptime(line.expiry_date,'%Y-%m-%d').strftime('%d/%m/%Y') or '', line.asset_id and line.asset_id.name, line.consumed_qty, line.remark or ''])
 
369
        instanciate_class = SpreadsheetCreator('RAC', header_columns, list_of_lines)
 
370
        file = base64.encodestring(instanciate_class.get_xml(default_filters=['decode.utf8']))
226
371
        
227
 
        export_id = self.pool.get('wizard.export.rac').create(cr, uid, {'rac_id': ids[0], 'file': file, 
228
 
                                                                        'filename': 'rac_%s.csv' % (rac.cons_location_id.name.replace(' ', '_')), 
229
 
                                                                        'message': 'The RAC lines has been exported. Please click on Save As button to download the file'})
 
372
        export_id = self.pool.get('wizard.export.rac').create(cr, uid, {'rac_id': ids[0], 
 
373
                                                                        'file': file,
 
374
                                                                        'filename': 'rac_%s.xls' % (rac.cons_location_id.name.replace(' ', '_')), 
 
375
                                                                        'message': _('The RAC lines has been exported. Please click on Save As button to download the file'),}, context=context)
230
376
        
231
377
        return {'type': 'ir.actions.act_window',
232
378
                'res_model': 'wizard.export.rac',
234
380
                'view_mode': 'form',
235
381
                'view_type': 'form',
236
382
                'target': 'new',
 
383
                'context': context,
237
384
                }
238
 
        
239
 
    def fill_lines(self, cr, uid, ids, context={}):
 
385
 
 
386
    def fill_lines(self, cr, uid, ids, context=None):
240
387
        '''
241
388
        Fill all lines according to defined nomenclature level and sublist
242
389
        '''
 
390
        if context is None:
 
391
            context = {}
 
392
        self.write(cr, uid, ids, {'created_ok': True})    
243
393
        for report in self.browse(cr, uid, ids, context=context):
244
394
            product_ids = []
245
395
            products = []
246
 
            
 
396
 
 
397
            nom = False
247
398
            # Get all products for the defined nomenclature
248
 
            if report.nomen_id:
249
 
                nomen_id = report.nomen_id.id
250
 
                nomen = self.pool.get('product.nomenclature').browse(cr, uid, nomen_id, context=context)
251
 
                if nomen.type == 'mandatory':
252
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_0', '=', nomen_id)], context=context))
253
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_1', '=', nomen_id)], context=context))
254
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_2', '=', nomen_id)], context=context))
255
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_3', '=', nomen_id)], context=context))
256
 
                else:
257
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_0', '=', nomen_id)], context=context))
258
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_1', '=', nomen_id)], context=context))
259
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_2', '=', nomen_id)], context=context))
260
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_3', '=', nomen_id)], context=context))
261
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_4', '=', nomen_id)], context=context))
262
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_5', '=', nomen_id)], context=context))
263
 
            
 
399
            if report.nomen_manda_3:
 
400
                nom = report.nomen_manda_3.id
 
401
                field = 'nomen_manda_3'
 
402
            elif report.nomen_manda_2:
 
403
                nom = report.nomen_manda_2.id
 
404
                field = 'nomen_manda_2'
 
405
            elif report.nomen_manda_1:
 
406
                nom = report.nomen_manda_1.id
 
407
                field = 'nomen_manda_1'
 
408
            elif report.nomen_manda_0:
 
409
                nom = report.nomen_manda_0.id
 
410
                field = 'nomen_manda_0'
 
411
            if nom:
 
412
                product_ids.extend(self.pool.get('product.product').search(cr, uid, [(field, '=', nom)], context=context))
 
413
 
264
414
            # Get all products for the defined list
265
415
            if report.sublist_id:
266
416
                for line in report.sublist_id.product_ids:
267
417
                    product_ids.append(line.name.id)
268
 
                    
 
418
 
269
419
            # Check if products in already existing lines are in domain
270
420
            products = []
271
421
            for line in report.line_ids:
273
423
                    products.append(line.product_id.id)
274
424
                else:
275
425
                    self.pool.get('real.average.consumption.line').unlink(cr, uid, line.id, context=context)
276
 
                    
 
426
 
277
427
            for product in self.pool.get('product.product').browse(cr, uid, product_ids, context=context):
278
428
                # Check if the product is not already on the report
279
429
                if product.id not in products:
280
 
                    self.pool.get('real.average.consumption.line').create(cr, uid, {'product_id': product.id,
281
 
                                                                                    'uom_id': product.uom_id.id,
282
 
                                                                                    'consumed_qty': 0.00,
283
 
                                                                                    'rac_id': report.id})
 
430
                    batch_mandatory = product.batch_management
 
431
                    date_mandatory = product.perishable
 
432
                    asset_mandatory = _get_asset_mandatory(product)
 
433
                    values = {'product_id': product.id,
 
434
                              'uom_id': product.uom_id.id,
 
435
                              'consumed_qty': 0.00,
 
436
                              'batch_mandatory': batch_mandatory,
 
437
                              'date_mandatory': date_mandatory,
 
438
                              'asset_mandatory': asset_mandatory,
 
439
                              'rac_id': report.id,}
 
440
                    v = self.pool.get('real.average.consumption.line').product_onchange(cr, uid, [], product.id, report.cons_location_id.id,
 
441
                                                                                        product.uom_id.id, False, context=context)['value']
 
442
                    values.update(v)
 
443
                    if batch_mandatory:
 
444
                        values.update({'remark': _('You must assign a batch number')})
 
445
                    if date_mandatory:
 
446
                        values.update({'remark': _('You must assign an expiry date')})
 
447
                    if asset_mandatory:
 
448
                        values.update({'remark': _('You must assign an asset')})
 
449
                    self.pool.get('real.average.consumption.line').create(cr, uid, values)
284
450
        
 
451
        self.write(cr, uid, ids, {'created_ok': False})    
285
452
        return {'type': 'ir.actions.act_window',
286
453
                'res_model': 'real.average.consumption',
287
454
                'view_type': 'form',
290
457
                'target': 'dummy',
291
458
                'context': context}
292
459
        
293
 
    def nomen_change(self, cr, uid, ids, nomen_id, context={}):
294
 
        context.update({'test_id': nomen_id})
295
 
    
296
 
        return {}
297
 
    
 
460
    def get_nomen(self, cr, uid, id, field):
 
461
        return self.pool.get('product.nomenclature').get_nomen(cr, uid, self, id, field, context={'withnum': 1})
 
462
 
 
463
    def onChangeSearchNomenclature(self, cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
 
464
        return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, 0, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, False, context={'withnum': 1})
 
465
    
 
466
    def write(self, cr, uid, ids, vals, context=None):
 
467
        if vals.get('sublist_id',False):
 
468
            vals.update({'nomen_manda_0':False,'nomen_manda_1':False,'nomen_manda_2':False,'nomen_manda_3':False})
 
469
        if vals.get('nomen_manda_0',False):
 
470
            vals.update({'sublist_id':False})
 
471
        ret = super(real_average_consumption, self).write(cr, uid, ids, vals, context=context)
 
472
        return ret
 
473
    
 
474
    def button_remove_lines(self, cr, uid, ids, context=None):
 
475
        '''
 
476
        Remove lines
 
477
        '''
 
478
        if context is None:
 
479
            context = {}
 
480
        if isinstance(ids, (int, long)):
 
481
            ids = [ids]
 
482
        vals = {}
 
483
        vals['line_ids'] = []
 
484
        for line in self.browse(cr, uid, ids, context=context):
 
485
            line_browse_list = line.line_ids
 
486
            for var in line_browse_list:
 
487
                vals['line_ids'].append((2, var.id))
 
488
            self.write(cr, uid, ids, vals, context=context)
 
489
        return True
 
490
 
 
491
    def check_lines_to_fix(self, cr, uid, ids, context=None):
 
492
        """
 
493
        Check the lines that need to be corrected
 
494
        """
 
495
        if isinstance(ids, (int, long)):
 
496
            ids = [ids]
 
497
        obj_data = self.pool.get('ir.model.data')
 
498
        product_tbd = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'product_tbd')[1]
 
499
        uom_tbd = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]
 
500
 
 
501
        for var in self.browse(cr, uid, ids, context=context):
 
502
            # we check the lines that need to be fixed
 
503
            if var.line_ids:
 
504
                for var in var.line_ids:
 
505
                    if var.consumed_qty and var.to_correct_ok:
 
506
                        raise osv.except_osv(_('Warning !'), _('Some lines need to be fixed before.'))
 
507
                    else:
 
508
                        self.pool.get('real.average.consumption.line').write(cr, uid, var.id, {},context)
 
509
        return True
 
510
 
298
511
real_average_consumption()
299
512
 
300
513
 
302
515
    _name = 'real.average.consumption.line'
303
516
    _description = 'Real average consumption line'
304
517
    _rec_name = 'product_id'
305
 
    
 
518
    _order = 'id, ref'
 
519
 
306
520
    def _get_checks_all(self, cr, uid, ids, name, arg, context=None):
307
521
        result = {}
308
522
        for id in ids:
309
 
            result[id] = {'batch_number_check': False, 'expiry_date_check': False, 'type_check': False}
 
523
            result[id] = {'batch_number_check': False, 'expiry_date_check': False, 'type_check': False, 'to_correct_ok': False}
310
524
            
311
525
        for out in self.browse(cr, uid, ids, context=context):
312
526
            if out.product_id:
313
527
                result[out.id]['batch_number_check'] = out.product_id.batch_management
314
528
                result[out.id]['expiry_date_check'] = out.product_id.perishable
 
529
                result[out.id]['asset_check'] = _get_asset_mandatory(out.product_id)
 
530
            # the lines with to_correct_ok=True will be red
 
531
            if out.text_error:
 
532
                result[out.id]['to_correct_ok'] = True
 
533
        return result
 
534
 
 
535
    def _get_qty(self, cr, uid, product, lot, location, uom):
 
536
        if not product and not lot:
 
537
            return False
 
538
        context = {'location_id': location, 'location': location, 'uom': uom, 'compute_child': False}
 
539
        if not lot:
 
540
            return self.pool.get('product.product').read(cr, uid, product, ['qty_available'], context=context)['qty_available']
315
541
            
316
 
        return result
317
 
    
318
 
    def write(self, cr, uid, ids, vals, context={}):
319
 
        '''
320
 
        Change the expiry date according to the prodlot_id
321
 
        Change the product_qty if the product is changed
322
 
        '''
 
542
        return self.pool.get('stock.production.lot').read(cr, uid, lot, ['stock_available'], context=context)['stock_available']
 
543
 
 
544
    def _check_qty(self, cr, uid, ids, context=None):
 
545
       
 
546
        if context is None:
 
547
            context = {}
 
548
        noraise = context.get('noraise')
 
549
        context.update({'error_message': ''})
 
550
        if isinstance(ids, (int, long)):
 
551
            ids = [ids]
 
552
        error_message = []
 
553
        for obj in self.browse(cr, uid, ids):
 
554
            if obj.rac_id.created_ok:
 
555
                continue
 
556
 
 
557
            # Prevent negative consumption qty.
 
558
            if obj.consumed_qty < 0.00:
 
559
                if not noraise:
 
560
                    raise osv.except_osv(_('Error'), _('The consumed qty. must be positive or 0.00'))
 
561
                elif context.get('import_in_progress'):
 
562
                    error_message.append(_('The consumed qty. must be positive or 0.00'))
 
563
                    context.update({'error_message': error_message})
 
564
 
 
565
            location = obj.rac_id.cons_location_id.id
 
566
            prodlot_id = None
 
567
            expiry_date = None
 
568
            asset_id = None
 
569
 
 
570
            batch_mandatory = obj.product_id.batch_management
 
571
            date_mandatory = obj.product_id.perishable
 
572
            asset_mandatory = _get_asset_mandatory(obj.product_id)
 
573
        
 
574
            if batch_mandatory and obj.consumed_qty != 0.00:
 
575
                if not obj.prodlot_id:
 
576
                    if not noraise:
 
577
                        raise osv.except_osv(_('Error'), 
 
578
                            _("Product: %s, You must assign a Batch Number to process it.")%(obj.product_id.name,))
 
579
                    elif context.get('import_in_progress'):
 
580
                        error_message.append(_("You must assign a Batch Number to process it."))
 
581
                        context.update({'error_message': error_message})
 
582
                elif obj.prodlot_id:
 
583
                    prodlot_id = obj.prodlot_id.id
 
584
                    expiry_date = obj.prodlot_id.life_date
 
585
 
 
586
            if date_mandatory and not batch_mandatory and obj.consumed_qty != 0.00:
 
587
                prod_ids = self.pool.get('stock.production.lot').search(cr, uid, [('life_date', '=', obj.expiry_date),
 
588
                                                                                  ('type', '=', 'internal'),
 
589
                                                                                  ('product_id', '=', obj.product_id.id)], context=context)
 
590
                expiry_date = obj.expiry_date or None # None because else it is False and a date can't have a boolean value
 
591
                if not prod_ids:
 
592
                    if not noraise:
 
593
                        raise osv.except_osv(_('Error'), 
 
594
                            _("Product: %s, no internal batch found for expiry (%s)")%(obj.product_id.name, obj.expiry_date or _('No expiry date set')))
 
595
                    elif context.get('import_in_progress'):
 
596
                        error_message.append(_("Line %s of the imported file: no internal batch number found for ED %s (please correct the data)"
 
597
                                               ) % (context.get('line_num', False), expiry_date and strptime(expiry_date, '%Y-%m-%d').strftime('%d-%m-%Y')))
 
598
                        context.update({'error_message': error_message})
 
599
                else:
 
600
                    prodlot_id = prod_ids[0]
 
601
 
 
602
            if asset_mandatory and obj.consumed_qty != 0.00:
 
603
                if not obj.asset_id:
 
604
                    if not noraise:
 
605
                        raise osv.except_osv(_('Error'),
 
606
                                _("Product: %s, You must assign an Asset to process it.")%(obj.product_id.name,))
 
607
                    elif context.get('import_in_progress'):
 
608
                        error_message.append(_("You must assign an Asset to process it."))
 
609
                        context.update({'error_message': error_message})
 
610
                elif obj.asset_id:
 
611
                    asset_id = obj.asset_id.id
 
612
 
 
613
            product_qty = self._get_qty(cr, uid, obj.product_id.id, prodlot_id, location, obj.uom_id and obj.uom_id.id)
 
614
 
 
615
            if prodlot_id and obj.consumed_qty > product_qty:
 
616
                if not noraise:
 
617
                    raise osv.except_osv(_('Error'), 
 
618
                        _("Product: %s, Qty Consumed (%s) can't be greater than the Indicative Stock (%s)")%(obj.product_id.name, obj.consumed_qty, product_qty))
 
619
                elif context.get('import_in_progress'):
 
620
                    error_message.append(_("Line %s of the imported file: Qty Consumed (%s) can't be greater than the Indicative Stock (%s)") % (context.get('line_num', False), obj.consumed_qty, product_qty))
 
621
                    context.update({'error_message': error_message})
 
622
                    # uf-1344 "quantity NOT in stock with this ED => line should be in red, no batch picked up"
 
623
                    prodlot_id = None
 
624
            #recursion: can't use write
 
625
            cr.execute('UPDATE '+self._table+' SET product_qty=%s, batch_mandatory=%s, date_mandatory=%s, asset_mandatory=%s, prodlot_id=%s, expiry_date=%s, asset_id=%s  where id=%s', (product_qty, batch_mandatory, date_mandatory, asset_mandatory, prodlot_id, expiry_date, asset_id, obj.id))
 
626
 
 
627
        return True
 
628
 
 
629
    def _get_product(self, cr, uid, ids, context=None):
 
630
        return self.pool.get('real.average.consumption.line').search(cr, uid, [('product_id', 'in', ids)], context=context)
 
631
 
 
632
    def _get_inactive_product(self, cr, uid, ids, field_name, args, context=None):
 
633
        '''
 
634
        Fill the error message if the product of the line is inactive
 
635
        '''
 
636
        res = {}
323
637
        for line in self.browse(cr, uid, ids, context=context):
324
 
            if line.batch_mandatory and 'prodlot_id' in vals:
325
 
                life_date = self.pool.get('stock.production.lot').browse(cr, uid, vals['prodlot_id'], context=context).life_date
326
 
                vals.update({'expiry_date': life_date})
327
 
 
328
 
            rac = self.pool.get('real.average.consumption').browse(cr, uid, vals.get('rac_id', line.rac_id.id), context=context)
329
 
            context.update({'location': rac.cons_location_id.id})
330
 
            if 'product_id' in vals and vals.get('product_id') != line.product_id.id:
331
 
                product = self.pool.get('product.product').browse(cr, uid, vals.get('product_id'), context=context)
332
 
                vals.update({'product_qty': product.qty_available})
333
 
                
334
 
        return super(real_average_consumption_line, self).write(cr, uid, ids, vals, context=context)
335
 
    
336
 
    def create(self, cr, uid, vals, context={}):
337
 
        '''
338
 
        Add the expiry date if a lot is set
339
 
        '''
340
 
        if vals.get('batch_mandatory', False) == True and 'prodlot_id' in vals:
341
 
            life_date = self.pool.get('stock.production.lot').browse(cr, uid, vals['prodlot_id'], context=context).life_date
342
 
            vals.update({'expiry_date': life_date})
343
 
 
344
 
        rac = self.pool.get('real.average.consumption').browse(cr, uid, vals.get('rac_id'), context=context)
345
 
 
346
 
        context.update({'location': rac.cons_location_id.id})
347
 
        product = self.pool.get('product.product').browse(cr, uid, vals.get('product_id'), context=context)
348
 
        vals.update({'product_qty': product.qty_available})
349
 
        
350
 
        return super(real_average_consumption_line, self).create(cr, uid, vals, context=context)
 
638
            res[line.id] = {'inactive_product': False,
 
639
                            'inactive_error': ''}
 
640
            if line.product_id and not line.product_id.active:
 
641
                res[line.id] = {
 
642
                    'inactive_product': True,
 
643
                    'inactive_error': _('The product in line is inactive !')
 
644
                }
 
645
 
 
646
        return res
351
647
 
352
648
    _columns = {
353
649
        'product_id': fields.many2one('product.product', string='Product', required=True),
 
650
        'ref': fields.related('product_id', 'default_code', type='char', size=64, readonly=True, 
 
651
                              store={'product.product': (_get_product, ['default_code'], 10),
 
652
                                     'real.average.consumption.line': (lambda self, cr, uid, ids, c=None: ids, ['product_id'], 20)}),
354
653
        'uom_id': fields.many2one('product.uom', string='UoM', required=True),
355
654
        'product_qty': fields.float(digits=(16,2), string='Indicative stock', readonly=True),
356
655
        'consumed_qty': fields.float(digits=(16,2), string='Qty consumed', required=True),
357
656
        'batch_number_check': fields.function(_get_checks_all, method=True, string='Batch Number Check', type='boolean', readonly=True, multi="m"),
358
657
        'expiry_date_check': fields.function(_get_checks_all, method=True, string='Expiry Date Check', type='boolean', readonly=True, multi="m"),
 
658
        'asset_check': fields.function(_get_checks_all, method=True, string='Asset Check', type='boolean', readonly=True, multi="m"),
359
659
        'prodlot_id': fields.many2one('stock.production.lot', string='Batch number'),
360
660
        'batch_mandatory': fields.boolean(string='BM'),
361
661
        'expiry_date': fields.date(string='Expiry date'),
362
662
        'date_mandatory': fields.boolean(string='DM'),
 
663
        'asset_id': fields.char(size=32, string='Asset'),
 
664
        'asset_mandatory': fields.boolean('AM'),
363
665
        'remark': fields.char(size=256, string='Remark'),
364
666
        'move_id': fields.many2one('stock.move', string='Move'),
365
667
        'rac_id': fields.many2one('real.average.consumption', string='RAC', ondelete='cascade'),
 
668
        'text_error': fields.text('Errors', readonly=True),
 
669
        'to_correct_ok': fields.function(_get_checks_all, method=True, type="boolean", string="To correct", store=False, readonly=True, multi="m"),
 
670
        'just_info_ok': fields.boolean(string='Just for info'),
 
671
        'inactive_product': fields.function(_get_inactive_product, method=True, type='boolean', string='Product is inactive', store=False, multi='inactive'),
 
672
        'inactive_error': fields.function(_get_inactive_product, method=True, type='char', string='Comment', store=False, multi='inactive'),
 
673
        }
 
674
 
 
675
    _defaults = {
 
676
        'inactive_product': False,
 
677
        'inactive_error': lambda *a: '',
366
678
    }
367
679
 
368
 
    def change_expiry(self, cr, uid, id, expiry_date, product_id, location_id, context={}):
 
680
# uf-1344 => need to pass the context so we use create and write instead
 
681
#    _constraints = [
 
682
#        (_check_qty, "The Qty Consumed can't be greater than the Indicative Stock", ['consumed_qty']),
 
683
#    ]
 
684
 
 
685
    _sql_constraints = [
 
686
        ('unique_lot_poduct', "unique(product_id, prodlot_id, rac_id)", 'The couple product, batch number has to be unique'),
 
687
    ]
 
688
 
 
689
    def create(self, cr, uid, vals=None, context=None):
 
690
        '''
 
691
        Call the constraint
 
692
        '''
 
693
        if context is None:
 
694
            context = {}
 
695
        res = super(real_average_consumption_line, self).create(cr, uid, vals, context=context)
 
696
        check = self._check_qty(cr, uid, res, context)
 
697
        if not check:
 
698
            raise osv.except_osv(_('Error'), _('The Qty Consumed cant\'t be greater than the Indicative Stock'))
 
699
        if vals.get('uom_id') and vals.get('product_id'):
 
700
            product_id = vals.get('product_id')
 
701
            product_uom = vals.get('uom_id')
 
702
            if not self.pool.get('uom.tools').check_uom(cr, uid, product_id, product_uom, context):
 
703
                raise osv.except_osv(_('Warning !'), _("You have to select a product UOM in the same category than the purchase UOM of the product"))
 
704
        return res
 
705
 
 
706
    def write(self, cr, uid, ids, vals, context=None):
 
707
        if context is None:
 
708
            context = {}
 
709
        if isinstance(ids, (int, long)):
 
710
            ids = [ids]
 
711
        if not context.get('import_in_progress') and not context.get('button'):
 
712
            obj_data = self.pool.get('ir.model.data')
 
713
            tbd_uom = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]
 
714
            tbd_product = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'product_tbd')[1]
 
715
            message = ''
 
716
            if vals.get('uom_id'):
 
717
                if vals.get('uom_id') == tbd_uom:
 
718
                    message += _('You have to define a valid UOM, i.e. not "To be define".')
 
719
            if vals.get('product_id'):
 
720
                if vals.get('product_id') == tbd_product:
 
721
                    message += _('You have to define a valid product, i.e. not "To be define".')
 
722
            if vals.get('uom_id') and vals.get('product_id'):
 
723
                product_id = vals.get('product_id')
 
724
                product_uom = vals.get('uom_id')
 
725
                if not self.pool.get('uom.tools').check_uom(cr, uid, product_id, product_uom, context):
 
726
                    message += _("You have to select a product UOM in the same category than the purchase UOM of the product")
 
727
            if message:
 
728
                raise osv.except_osv(_('Warning !'), message)
 
729
            else:
 
730
                vals['text_error'] = False
 
731
        res = super(real_average_consumption_line, self).write(cr, uid, ids, vals, context=context)
 
732
        check = self._check_qty(cr, uid, ids, context)
 
733
        if not check:
 
734
            raise osv.except_osv(_('Error'), _('The Qty Consumed cant\'t be greater than the Indicative Stock'))
 
735
        return res
 
736
 
 
737
    def change_expiry(self, cr, uid, id, expiry_date, product_id, location_id, uom, remark=False, context=None):
369
738
        '''
370
739
        expiry date changes, find the corresponding internal prod lot
371
740
        '''
 
741
        if context is None:
 
742
            context = {}
372
743
        prodlot_obj = self.pool.get('stock.production.lot')
373
744
        result = {'value':{}}
374
745
        context.update({'location': location_id})
375
 
        
 
746
       
376
747
        if expiry_date and product_id:
377
748
            prod_ids = prodlot_obj.search(cr, uid, [('life_date', '=', expiry_date),
378
749
                                                    ('type', '=', 'internal'),
379
750
                                                    ('product_id', '=', product_id)], context=context)
380
751
            if not prod_ids:
381
 
                    # display warning
382
 
                    result['warning'] = {'title': _('Error'),
383
 
                                         'message': _('The selected Expiry Date does not exist in the system.')}
384
 
                    # clear date
385
 
                    result['value'].update(expiry_date=False, prodlot_id=False)
 
752
                # display warning
 
753
                result['warning'] = {'title': _('Error'),
 
754
                                     'message': _('The selected Expiry Date does not exist in the system.')}
 
755
                # clear date
 
756
                result['value'].update(expiry_date=False, prodlot_id=False)
386
757
            else:
387
758
                # return first prodlot
388
 
                result['value'].update(prodlot_id=prod_ids[0])
 
759
                result = self.change_prodlot(cr, uid, id, product_id, prod_ids[0], expiry_date, location_id, uom, context={})
 
760
                result.setdefault('value',{}).update(prodlot_id=prod_ids[0])
 
761
                if remark and remark in ('You must assign a batch number', 'You must assign an expiry date') :
 
762
                    result['value']['remark'] = ''
 
763
                return result
389
764
                
390
765
        else:
391
766
            # clear expiry date, we clear production lot
392
767
            result['value'].update(prodlot_id=False)
393
 
    
 
768
   
 
769
        context.update(uom=uom)
 
770
        context.update({'compute_child': False})
394
771
        product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
395
772
        result['value'].update({'product_qty': product.qty_available})
396
773
        
397
774
        return result
398
775
 
399
 
    def change_prodlot(self, cr, uid, ids, product_id, prodlot_id, expiry_date, location_id, context={}):
 
776
    def change_asset(self, cr, uid, id, asset, product_id, location_id, uom, remark=False, context=None):
 
777
        '''
 
778
        Asset change, remove the remark
 
779
        '''
 
780
        if context is None:
 
781
            context = {}
 
782
 
 
783
        result = {'value':{}}
 
784
        if remark and remark == 'You must assign an asset':
 
785
            result.setdefault('value', {}).update(remark='')
 
786
 
 
787
        return result
 
788
 
 
789
 
 
790
    def change_qty(self, cr, uid, ids, qty, product_id, prodlot_id, location, uom, context=None):
 
791
        if context is None:
 
792
            context = {}
 
793
 
 
794
        res = {'value': {}}
 
795
 
 
796
        stock_qty = self._get_qty(cr, uid, product_id, prodlot_id, location, uom)
 
797
        warn_msg = {'title': _('Error'), 'message': _("The Qty Consumed is greater than the Indicative Stock")}
 
798
        
 
799
        if qty:
 
800
            res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom, qty, 'consumed_qty', result=res)
 
801
 
 
802
        if prodlot_id and qty > stock_qty:
 
803
            res.setdefault('warning', {}).update(warn_msg)
 
804
            res.setdefault('value', {}).update({'consumed_qty': 0})
 
805
        if qty > stock_qty:
 
806
            res.setdefault('warning', {}).update(warn_msg)
 
807
        
 
808
        return res
 
809
 
 
810
    def change_prodlot(self, cr, uid, ids, product_id, prodlot_id, expiry_date, location_id, uom, remark=False, context=None):
400
811
        '''
401
812
        Set the expiry date according to the prodlot
402
813
        '''
 
814
        if context is None:
 
815
            context = {}
403
816
        res = {'value': {}}
404
 
        context.update({'location': location_id})
 
817
        context.update({'location': location_id, 'uom': uom})
405
818
        if prodlot_id and not expiry_date:
 
819
            if remark and remark in ('You must assign a batch number', 'You must assign an expiry date') :
 
820
                res['value']['remark'] = ''
406
821
            res['value'].update({'expiry_date': self.pool.get('stock.production.lot').browse(cr, uid, prodlot_id, context=context).life_date})
407
822
        elif not prodlot_id and expiry_date:
408
823
            res['value'].update({'expiry_date': False})
409
824
 
410
 
        product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
411
 
        res['value'].update({'product_qty': product.qty_available})
412
 
 
413
 
        return res
414
 
    
415
 
    def product_onchange(self, cr, uid, ids, product_id, location_id=False, prodlot_id=False, context={}):
 
825
        if not prodlot_id:
 
826
            context.update({'compute_child': False})
 
827
            product_qty = self.pool.get('product.product').browse(cr, uid, product_id, context=context).qty_available
 
828
        else:
 
829
            if remark and remark in ('You must assign a batch number', 'You must assign an expiry date') :
 
830
                res['value']['remark'] = ''
 
831
            context.update({'location_id': location_id})
 
832
            product_qty = self.pool.get('stock.production.lot').browse(cr, uid, prodlot_id, context=context).stock_available
 
833
        res['value'].update({'product_qty': product_qty})
 
834
 
 
835
        return res
 
836
   
 
837
    def uom_onchange(self, cr, uid, ids, product_id, product_qty, location_id=False, uom=False, lot=False, context=None):
 
838
        if context is None:
 
839
            context = {}
 
840
        qty_available = 0
 
841
        d = {}
 
842
        if uom and product_id:
 
843
            qty_available = self._get_qty(cr, uid, product_id, lot, location_id, uom)
 
844
 
 
845
        if not uom and product_id:
 
846
            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
 
847
            d['uom_id'] = [('category_id', '=', product.uom_id.category_id.id)]
 
848
 
 
849
        res = {'value': {'product_qty': qty_available}, 'domain': d}
 
850
 
 
851
        if product_qty:
 
852
            res = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom, product_qty, 'consumed_qty', result=res)
 
853
 
 
854
        return res
 
855
 
 
856
    def product_onchange(self, cr, uid, ids, product_id, location_id=False, uom=False, lot=False, context=None):
416
857
        '''
417
858
        Set the product uom when the product change
418
859
        '''
419
 
        v = {}
420
 
        
 
860
        if context is None:
 
861
            context = {}
 
862
        product_obj = self.pool.get('product.product')
 
863
        v = {'batch_mandatory': False, 'date_mandatory': False, 'asset_mandatory': False}
 
864
        d = {'uom_id': []} 
421
865
        if product_id:
 
866
            # Test the compatibility of the product with a consumption report
 
867
            res, test = product_obj._on_change_restriction_error(cr, uid, product_id, field_name='product_id', values={'value': v}, vals={'constraints': 'consumption'}, context=context)
 
868
            if test:
 
869
                return res
422
870
            if location_id:
423
 
                context.update({'location': location_id})
 
871
                context.update({'location': location_id, 'uom': uom})
424
872
 
425
 
            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) 
 
873
            context.update({'compute_child': False})
 
874
            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
 
875
            qty_available = product.qty_available
426
876
                
 
877
            if product.batch_management:
 
878
                v.update({'batch_mandatory': True, 'remark': _('You must assign a batch')})
 
879
            if product.perishable:
 
880
                v.update({'date_mandatory': True, 'remark': _('You must assign an expiry date')})
 
881
            if product.type == 'product' and product.subtype == 'asset':
 
882
                v.update({'asset_mandatory': True, 'remark': _('You must assign an asset')})
 
883
 
427
884
            uom = product.uom_id.id
428
 
            if product.batch_management:
429
 
                v.update({'batch_mandatory': True})
430
 
            elif product.perishable:
431
 
                v.update({'date_mandatory': True})
432
 
 
433
885
            v.update({'uom_id': uom})
434
 
 
 
886
            d['uom_id'] = [('category_id', '=', product.uom_id.category_id.id)]
435
887
            if location_id:
436
 
                v.update({'product_qty': product.qty_available})
 
888
                v.update({'product_qty': qty_available})
437
889
        else:
438
890
            v.update({'uom_id': False, 'product_qty': 0.00, 'prodlot_id': False, 'expiry_date': False, 'consumed_qty': 0.00})
439
891
        
440
 
        return {'value': v}
441
 
    
 
892
        return {'value': v, 'domain': d}
 
893
 
 
894
    def copy(self, cr, uid, line_id, default=None, context=None):
 
895
        if not context:
 
896
            context = {}
 
897
 
 
898
        if not default:
 
899
            default = {}
 
900
 
 
901
        default.update({'prodlot_id': False, 'expiry_date': False, 'asset_id': False})
 
902
 
 
903
        if 'consumed_qty' in default and default['consumed_qty'] < 0.00:
 
904
            default['consumed_qty'] = 0.00
 
905
 
 
906
        return super(real_average_consumption_line, self).copy(cr, uid, line_id[0], default=default, context={'noraise': True})
 
907
 
442
908
real_average_consumption_line()
443
909
 
444
910
 
 
911
class real_consumption_change_location(osv.osv_memory):
 
912
    _name = 'real.consumption.change.location'
 
913
 
 
914
    _columns = {
 
915
        'report_id': fields.many2one('real.average.consumption', string='Report'),
 
916
        'location_id': fields.many2one('stock.location', string='Consumer location', required=True),
 
917
    }
 
918
 
 
919
    def change_location(self, cr, uid, ids, context=None):
 
920
        '''
 
921
        Change location of the report and reload the report
 
922
        '''
 
923
        if isinstance(ids, (int, long)):
 
924
            ids = [ids]
 
925
 
 
926
        wiz = self.browse(cr, uid, ids[0], context=context)
 
927
 
 
928
        self.pool.get('real.average.consumption').write(cr, uid, [wiz.report_id.id], {'cons_location_id': wiz.location_id.id}, context=context)
 
929
        self.pool.get('real.average.consumption').button_update_stock(cr, uid, [wiz.report_id.id], context=context)
 
930
 
 
931
        return {'type': 'ir.actions.act_window',
 
932
                'res_model': 'real.average.consumption',
 
933
                'view_type': 'form',
 
934
                'view_mode': 'form,tree',
 
935
                'res_id': wiz.report_id.id,
 
936
                'target': 'dummy'}
 
937
 
 
938
real_consumption_change_location()
 
939
 
 
940
 
445
941
class monthly_review_consumption(osv.osv):
446
942
    _name = 'monthly.review.consumption'
447
943
    _description = 'Monthly review consumption'
448
944
    _rec_name = 'creation_date'
449
945
    
450
 
    def _get_nb_lines(self, cr, uid, ids, field_name, args, context={}):
 
946
    def _get_nb_lines(self, cr, uid, ids, field_name, args, context=None):
451
947
        '''
452
948
        Returns the # of lines on the monthly review consumption
453
949
        '''
457
953
            res[mrc.id] = len(mrc.line_ids)
458
954
            
459
955
        return res
460
 
    
 
956
 
 
957
    def get_bool_values(self, cr, uid, ids, fields, arg, context=None):
 
958
        res = {}
 
959
        if isinstance(ids, (int, long)):
 
960
            ids = [ids]
 
961
        for obj in self.browse(cr, uid, ids, context=context):
 
962
            res[obj.id] = False
 
963
            if any([item for item in obj.line_ids  if item.to_correct_ok]):
 
964
                res[obj.id] = True
 
965
        return res
 
966
 
461
967
    _columns = {
462
968
        'creation_date': fields.date(string='Creation date'),
463
969
        'cons_location_id': fields.char(size=256, string='Location', readonly=True),
467
973
        'nomen_id': fields.many2one('product.nomenclature', string='Products\' nomenclature level'),
468
974
        'line_ids': fields.one2many('monthly.review.consumption.line', 'mrc_id', string='Lines'),
469
975
        'nb_lines': fields.function(_get_nb_lines, method=True, type='integer', string='# lines', readonly=True,),
 
976
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type'),
 
977
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group'),
 
978
        'nomen_manda_2': fields.many2one('product.nomenclature', 'Family'),
 
979
        'nomen_manda_3': fields.many2one('product.nomenclature', 'Root'),
 
980
        'hide_column_error_ok': fields.function(get_bool_values, method=True, readonly=True, type="boolean", string="Show column errors", store=False),
470
981
    }
471
982
    
472
983
    _defaults = {
 
984
        'period_to': lambda *a: (DateFrom(time.strftime('%Y-%m-%d')) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d'),
473
985
        'creation_date': lambda *a: time.strftime('%Y-%m-%d'),
474
 
        'period_to': lambda *a: time.strftime('%Y-%m-%d'),
475
986
        'cons_location_id': lambda *a: 'MSF Instance',
476
987
    }
 
988
 
 
989
    def period_change(self, cr, uid, ids, period_from, period_to, context=None):
 
990
        '''
 
991
        Get the first day of month and the last day
 
992
        '''
 
993
        res = {}
 
994
 
 
995
        if period_from:
 
996
            res.update({'period_from': (DateFrom(period_from) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')})
 
997
        if period_to:
 
998
            res.update({'period_to': (DateFrom(period_to) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')})
 
999
 
 
1000
        return {'value': res}
477
1001
    
478
 
    def import_fmc(self, cr, uid, ids, context={}):
 
1002
    def import_fmc(self, cr, uid, ids, context=None):
479
1003
        '''
480
1004
        Launches the wizard to import lines from a file
481
1005
        '''
 
1006
        if context is None:
 
1007
            context = {}
482
1008
        context.update({'active_id': ids[0]})
483
1009
        
484
1010
        return {'type': 'ir.actions.act_window',
489
1015
                'context': context,
490
1016
                }
491
1017
        
492
 
    def export_fmc(self, cr, uid, ids, context={}):
493
 
        '''
494
 
        Creates a CSV file and launches the wizard to save it
495
 
        '''
496
 
        fmc = self.browse(cr, uid, ids[0], context=context)
497
 
        
498
 
        export = 'Product reference;Product name;AMC;FMC;Valid until'
499
 
        export += '\n'
500
 
        
501
 
        for line in fmc.line_ids:
502
 
            export += '%s;%s;%s;%s;%s' % (line.name.default_code, line.name.name, line.amc, line.fmc, line.valid_until or '')
503
 
            export += '\n'
504
 
            
505
 
        file = base64.encodestring(export.encode("utf-8"))
506
 
        
507
 
        export_id = self.pool.get('wizard.export.fmc').create(cr, uid, {'fmc_id': ids[0], 'file': file, 
508
 
                                                                        'filename': 'fmc_%s.csv' % (time.strftime('%Y_%m_%d')), 
509
 
                                                                        'message': 'The FMC lines has been exported. Please click on Save As button to download the file'})
510
 
        
511
 
        return {'type': 'ir.actions.act_window',
512
 
                'res_model': 'wizard.export.fmc',
513
 
                'res_id': export_id,
514
 
                'view_mode': 'form',
515
 
                'view_type': 'form',
516
 
                'target': 'new',
517
 
                }
518
 
        
519
 
    def fill_lines(self, cr, uid, ids, context={}):
 
1018
    def export_fmc(self, cr, uid, ids, context=None):
 
1019
        """
 
1020
        Return an xml file to open with Excel
 
1021
        """
 
1022
        datas = {'ids': ids}
 
1023
 
 
1024
        return {'type': 'ir.actions.report.xml',
 
1025
                'report_name': 'monthly.consumption.xls',
 
1026
                'datas': datas}
 
1027
#        '''
 
1028
#        Creates a CSV file and launches the wizard to save it
 
1029
#        '''
 
1030
#        if context is None:
 
1031
#            context = {}
 
1032
#        fmc = self.browse(cr, uid, ids[0], context=context)
 
1033
#        
 
1034
#        outfile = TemporaryFile('w+')
 
1035
#        writer = csv.writer(outfile, quotechar='"', delimiter=',')
 
1036
#        writer.writerow(['Product Code', 'Product Description', 'AMC', 'FMC', 'Valid until'])
 
1037
#        
 
1038
#        for line in fmc.line_ids:
 
1039
#            writer.writerow([line.name.default_code and line.name.default_code.encode('utf-8'), line.name.name and line.name.name.encode('utf-8'), line.amc, line.fmc, line.valid_until or ''])
 
1040
#        outfile.seek(0)    
 
1041
#        file = base64.encodestring(outfile.read())
 
1042
#        outfile.close()
 
1043
#        
 
1044
#        export_id = self.pool.get('wizard.export.fmc').create(cr, uid, {'fmc_id': ids[0], 'file': file, 
 
1045
#                                                                        'filename': 'fmc_%s.csv' % (time.strftime('%Y_%m_%d')), 
 
1046
#                                                                        'message': 'The FMC lines has been exported. Please click on Save As button to download the file'})
 
1047
#        
 
1048
#        return {'type': 'ir.actions.act_window',
 
1049
#                'res_model': 'wizard.export.fmc',
 
1050
#                'res_id': export_id,
 
1051
#                'view_mode': 'form',
 
1052
#                'view_type': 'form',
 
1053
#                'target': 'new',
 
1054
#                }
 
1055
        
 
1056
    def fill_lines(self, cr, uid, ids, context=None):
520
1057
        '''
521
1058
        Fill all lines according to defined nomenclature level and sublist
522
1059
        '''
 
1060
        if context is None:
 
1061
            context = {}
523
1062
        line_obj = self.pool.get('monthly.review.consumption.line')
524
1063
        for report in self.browse(cr, uid, ids, context=context):
525
1064
            product_ids = []
526
1065
            products = []
527
 
            
528
1066
            # Get all products for the defined nomenclature
529
 
            if report.nomen_id:
530
 
                nomen_id = report.nomen_id.id
531
 
                nomen = self.pool.get('product.nomenclature').browse(cr, uid, nomen_id, context=context)
532
 
                if nomen.type == 'mandatory':
533
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_0', '=', nomen_id)], context=context))
534
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_1', '=', nomen_id)], context=context))
535
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_2', '=', nomen_id)], context=context))
536
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_manda_3', '=', nomen_id)], context=context))
537
 
                else:
538
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_0', '=', nomen_id)], context=context))
539
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_1', '=', nomen_id)], context=context))
540
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_2', '=', nomen_id)], context=context))
541
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_3', '=', nomen_id)], context=context))
542
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_4', '=', nomen_id)], context=context))
543
 
                    product_ids.extend(self.pool.get('product.product').search(cr, uid, [('nomen_sub_5', '=', nomen_id)], context=context))
 
1067
            nom = False
 
1068
            field = False
 
1069
            if report.nomen_manda_3:
 
1070
                nom = report.nomen_manda_3.id
 
1071
                field = 'nomen_manda_3'
 
1072
            elif report.nomen_manda_2:
 
1073
                nom = report.nomen_manda_2.id
 
1074
                field = 'nomen_manda_2'
 
1075
            elif report.nomen_manda_1:
 
1076
                nom = report.nomen_manda_1.id
 
1077
                field = 'nomen_manda_1'
 
1078
            elif report.nomen_manda_0:
 
1079
                nom = report.nomen_manda_0.id
 
1080
                field = 'nomen_manda_0'
 
1081
            if nom:
 
1082
                product_ids.extend(self.pool.get('product.product').search(cr, uid, [(field, '=', nom)], context=context))
544
1083
            
545
1084
            # Get all products for the defined list
546
1085
            if report.sublist_id:
554
1093
                    products.append(line.name.id)
555
1094
                else:
556
1095
                    self.pool.get('monthly.review.consumption.line').unlink(cr, uid, line.id, context=context)
 
1096
 
 
1097
            amc_context = context.copy()
 
1098
            amc_context.update({'from_date': report.period_from, 'to_date': report.period_to})
 
1099
            if amc_context.get('from_date', False):
 
1100
                from_date = (DateFrom(amc_context.get('from_date')) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')
 
1101
                amc_context.update({'from_date': from_date})
 
1102
                                               
 
1103
            if amc_context.get('to_date', False):
 
1104
                to_date = (DateFrom(amc_context.get('to_date')) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')
 
1105
                amc_context.update({'to_date': to_date})
557
1106
                    
558
1107
            for product in self.pool.get('product.product').browse(cr, uid, product_ids, context=context):
559
1108
                # Check if the product is not already on the report
560
1109
                if product.id not in products:
561
1110
                    products.append(product.id)
562
 
                    amc = self.pool.get('product.product').compute_amc(cr, uid, product.id, context=context)
 
1111
                    amc = self.pool.get('product.product').compute_amc(cr, uid, product.id, context=amc_context)
563
1112
                    last_fmc_reviewed = False
564
1113
                    line_ids = line_obj.search(cr, uid, [('name', '=', product.id), ('valid_ok', '=', True)], order='valid_until desc, id desc', context=context)
565
1114
                    if line_ids:
582
1131
                'context': context}
583
1132
 
584
1133
 
585
 
    def valid_multiple_lines(self, cr, uid, ids, context={}):
 
1134
    def valid_multiple_lines(self, cr, uid, ids, context=None):
586
1135
        '''
587
1136
        Validate multiple lines
588
1137
        '''
 
1138
        if context is None:
 
1139
            context = {}
 
1140
        self.check_lines_to_fix(cr, uid, ids, context)
589
1141
        for report in self.browse(cr, uid, ids, context=context):
590
1142
            for line in report.line_ids:
591
1143
                if not line.valid_ok:
599
1151
                'target': 'dummy',
600
1152
                'context': context}
601
1153
    
 
1154
    def write(self, cr, uid, ids, vals, context=None):
 
1155
        if vals.get('sublist_id',False):
 
1156
            vals.update({'nomen_manda_0':False,'nomen_manda_1':False,'nomen_manda_2':False,'nomen_manda_3':False})
 
1157
        if vals.get('nomen_manda_0',False):
 
1158
            vals.update({'sublist_id':False})
 
1159
        ret = super(monthly_review_consumption, self).write(cr, uid, ids, vals, context=context)
 
1160
        return ret
 
1161
    
 
1162
    def onChangeSearchNomenclature(self, cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
 
1163
        return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, 0, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, False, context={'withnum': 1})
 
1164
    
 
1165
    def get_nomen(self, cr, uid, id, field):
 
1166
        return self.pool.get('product.nomenclature').get_nomen(cr, uid, self, id, field, context={'withnum': 1})
 
1167
 
 
1168
    def button_remove_lines(self, cr, uid, ids, context=None):
 
1169
        '''
 
1170
        Remove lines
 
1171
        '''
 
1172
        if context is None:
 
1173
            context = {}
 
1174
        if isinstance(ids, (int, long)):
 
1175
            ids = [ids]
 
1176
        vals = {}
 
1177
        vals['line_ids'] = []
 
1178
        for line in self.browse(cr, uid, ids, context=context):
 
1179
            line_browse_list = line.line_ids
 
1180
            for var in line_browse_list:
 
1181
                vals['line_ids'].append((2, var.id))
 
1182
            self.write(cr, uid, ids, vals, context=context)
 
1183
        return True
 
1184
 
 
1185
    def check_lines_to_fix(self, cr, uid, ids, context=None):
 
1186
        """
 
1187
        Check the lines that need to be corrected.
 
1188
        """
 
1189
        if isinstance(ids, (int, long)):
 
1190
            ids = [ids]
 
1191
        for var in self.browse(cr, uid, ids, context=context):
 
1192
            # we check the lines that need to be fixed
 
1193
            if var.line_ids:
 
1194
                for var in var.line_ids:
 
1195
                    if var.to_correct_ok:
 
1196
                        raise osv.except_osv(_('Warning !'), _('Some lines need to be fixed before.'))
 
1197
        return True
 
1198
 
602
1199
monthly_review_consumption()
603
1200
 
604
1201
 
605
1202
class monthly_review_consumption_line(osv.osv):
606
1203
    _name = 'monthly.review.consumption.line'
607
1204
    _description = 'Monthly review consumption line'
 
1205
    _order = 'ref'
608
1206
    
609
 
    def _get_amc(self, cr, uid, ids, field_name, arg, context={}):
 
1207
    def _get_amc(self, cr, uid, ids, field_name, arg, ctx=None):
610
1208
        '''
611
1209
        Calculate the product AMC for the period
612
1210
        '''
 
1211
        if ctx is None:
 
1212
            ctx = {}
 
1213
        context = ctx.copy()
613
1214
        res = {}
614
1215
        
615
1216
        for line in self.browse(cr, uid, ids, context=context):
616
1217
            context.update({'from_date': line.mrc_id.period_from, 'to_date': line.mrc_id.period_to})
 
1218
            if context.get('from_date', False):
 
1219
                from_date = (DateFrom(context.get('from_date')) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')
 
1220
                context.update({'from_date': from_date})
 
1221
                                               
 
1222
            if context.get('to_date', False):
 
1223
                to_date = (DateFrom(context.get('to_date')) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')
 
1224
                context.update({'to_date': to_date})
 
1225
                    
617
1226
            res[line.id] = self.pool.get('product.product').compute_amc(cr, uid, line.name.id, context=context)
618
1227
            
619
1228
        return res
620
1229
    
621
 
    def _get_last_fmc(self, cr, uid, ids, field_name, args, context={}):
 
1230
    def _get_last_fmc(self, cr, uid, ids, field_name, args, context=None):
622
1231
        '''
623
1232
        Returns the last fmc date
624
1233
        '''
 
1234
        if context is None:
 
1235
            context = {}
625
1236
        res = {}
626
1237
        
627
1238
        for line in self.browse(cr, uid, ids, context=context):
629
1240
            
630
1241
        return res
631
1242
 
632
 
    def create(self, cr, uid, vals, context={}):
 
1243
    def create(self, cr, uid, vals, context=None):
 
1244
        if context is None:
 
1245
            context = {}
633
1246
        if 'fmc2' in vals:
634
1247
            vals.update({'fmc': vals.get('fmc2')})
635
1248
        if 'last_reviewed2' in vals:
641
1254
 
642
1255
        return super(monthly_review_consumption_line, self).create(cr, uid, vals, context=context)
643
1256
 
644
 
    def write(self, cr, uid, ids, vals, context={}):
 
1257
    def write(self, cr, uid, ids, vals, context=None):
 
1258
        if context is None:
 
1259
            context = {}
 
1260
        if isinstance(ids, (int, long)):
 
1261
            ids = [ids]
 
1262
        if not context.get('import_in_progress') and not context.get('button'):
 
1263
            obj_data = self.pool.get('ir.model.data')
 
1264
            tbd_product = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'product_tbd')[1]
 
1265
            if vals.get('name'):
 
1266
                if vals.get('name') == tbd_product:
 
1267
                    raise osv.except_osv(_('Warning !'), _('You have to define a valid product, i.e. not "To be define".'))
 
1268
                else:
 
1269
                    vals['to_correct_ok'] = False
 
1270
                    vals['text_error'] = False
645
1271
        if 'fmc2' in vals:
646
1272
            vals.update({'fmc': vals.get('fmc2')})
647
1273
        if 'last_reviewed2' in vals:
652
1278
                         'last_reviewed2': time.strftime('%Y-%m-%d')})
653
1279
 
654
1280
        return super(monthly_review_consumption_line, self).write(cr, uid, ids, vals, context=context)
 
1281
 
 
1282
    def _get_mrc_change(self, cr, uid, ids, context=None):
 
1283
        '''
 
1284
        Returns MRC ids when Date change
 
1285
        '''
 
1286
        result = {}
 
1287
        for mrc in self.pool.get('monthly.review.consumption').browse(cr, uid, ids, context=context):
 
1288
            for line in mrc.line_ids:
 
1289
                result[line.id] = True
 
1290
                
 
1291
        return result.keys()
655
1292
    
 
1293
    def _get_product(self, cr, uid, ids, context=None):
 
1294
        return self.pool.get('monthly.review.consumption.line').search(cr, uid, [('name', 'in', ids)], context=context)
 
1295
 
 
1296
    def _get_checks_all(self, cr, uid, ids, name, arg, context=None):
 
1297
        result = {}
 
1298
        for id in ids:
 
1299
            result[id] = {'to_correct_ok': False}
 
1300
            
 
1301
        for out in self.browse(cr, uid, ids, context=context):
 
1302
            # the lines with to_correct_ok=True will be red
 
1303
            if out.text_error:
 
1304
                result[out.id]['to_correct_ok'] = True
 
1305
        return result
 
1306
 
656
1307
    _columns = {
657
1308
        'name': fields.many2one('product.product', string='Product', required=True),
658
 
        'amc': fields.function(_get_amc, string='AMC', method=True, readonly=True, store=True),
 
1309
        'ref': fields.related('name', 'default_code', type='char', size=64, readonly=True,
 
1310
                              store={'product.product': (_get_product, ['default_code'], 10),
 
1311
                                     'monthly.review.consumption.line': (lambda self, cr, uid, ids, c=None: ids, ['name'], 20)}),
 
1312
        'amc': fields.function(_get_amc, string='AMC', method=True, readonly=True, 
 
1313
                               store={'monthly.review.consumption': (_get_mrc_change, ['period_from', 'period_to'], 20),
 
1314
                                      'monthly.review.consumption.line': (lambda self, cr, uid, ids, c=None: ids, [],20),}),
659
1315
        'fmc': fields.float(digits=(16,2), string='FMC'),
660
1316
        'fmc2': fields.float(digits=(16,2), string='FMC (hidden)'),
661
1317
        #'last_reviewed': fields.function(_get_last_fmc, method=True, type='date', string='Last reviewed on', readonly=True, store=True),
665
1321
        'valid_ok': fields.boolean(string='Validated', readonly=False),
666
1322
        'mrc_id': fields.many2one('monthly.review.consumption', string='MRC', required=True, ondelete='cascade'),
667
1323
        'mrc_creation_date': fields.related('mrc_id', 'creation_date', type='date', store=True),
 
1324
        'text_error': fields.text('Errors', readonly=True),
 
1325
        'to_correct_ok': fields.function(_get_checks_all, method=True, type="boolean", string="To correct", store=False, readonly=True, multi="m"),
668
1326
    }
669
1327
    
670
 
    def valid_line(self, cr, uid, ids, context={}):
 
1328
    def valid_line(self, cr, uid, ids, context=None):
671
1329
        '''
672
1330
        Valid the line and enter data in product form
673
1331
        '''
687
1345
            
688
1346
        return
689
1347
    
690
 
    def display_graph(self, cr, uid, ids, context={}):
 
1348
    def display_graph(self, cr, uid, ids, context=None):
691
1349
        '''
692
1350
        Display the graph view of the line
693
1351
        '''
694
1352
        raise osv.except_osv('Error !', 'Not implemented yet !')
695
1353
 
696
 
    def fmc_change(self, cr, uid, ids, amc, fmc, product_id, context={}):
 
1354
    def fmc_change(self, cr, uid, ids, amc, fmc, product_id, context=None):
697
1355
        '''
698
1356
        Valid the line if the FMC is manually changed
699
1357
        '''
 
1358
        if context is None:
 
1359
            context = {}
700
1360
        res = {}
701
1361
 
702
1362
        if fmc != amc:
714
1374
 
715
1375
        return {'value': res}
716
1376
    
717
 
    def product_onchange(self, cr, uid, ids, product_id, mrc_id=False, from_date=False, to_date=False, context={}):
 
1377
    def product_onchange(self, cr, uid, ids, product_id, mrc_id=False, from_date=False, to_date=False, context=None):
718
1378
        '''
719
1379
        Fill data in the line
720
1380
        '''
748
1408
        if line_ids:
749
1409
            for line in self.browse(cr, uid, [line_ids[0]], context=context):
750
1410
                last_fmc_reviewed = line.mrc_id.creation_date
 
1411
 
 
1412
        if context.get('from_date', False):
 
1413
            from_date = (DateFrom(context.get('from_date')) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')
 
1414
            context.update({'from_date': from_date})
 
1415
                                               
 
1416
        if context.get('to_date', False):
 
1417
            to_date = (DateFrom(context.get('to_date')) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')
 
1418
            context.update({'to_date': to_date})
751
1419
                
752
 
                    
753
1420
        amc = product_obj.compute_amc(cr, uid, product_id, context=context)
754
1421
        return {'value': {'amc': amc,
755
1422
                          'fmc': amc,
767
1434
    _name = 'product.product'
768
1435
    _inherit = 'product.product'
769
1436
    
770
 
    def _compute_fmc(self, cr, uid, ids, field_name, args, context={}):
 
1437
    def _compute_fmc(self, cr, uid, ids, field_name, args, context=None):
771
1438
        '''
772
1439
        Returns the last value of the FMC
773
1440
        '''
775
1442
            context = {}
776
1443
            
777
1444
        res = {}
778
 
        fmc_obj = self.pool.get('monthly.review.consumption')
 
1445
        #fmc_obj = self.pool.get('monthly.review.consumption')
779
1446
        fmc_line_obj = self.pool.get('monthly.review.consumption.line')
780
1447
            
781
1448
        # Search all Review report for locations
782
 
        fmc_ids = fmc_obj.search(cr, uid, [], context=context)
 
1449
        #fmc_ids = fmc_obj.search(cr, uid, [], order='period_to desc, creation_date desc', limit=1, context=context)
783
1450
        
784
1451
        for product in ids:
785
1452
            res[product] = 0.00
786
 
            last_date = False
787
1453
            
788
1454
            # Search all validated lines with the product
789
 
            line_ids = fmc_line_obj.search(cr, uid, [('name', '=', product), ('valid_ok', '=', True), ('mrc_id', 'in', fmc_ids)], context=context)
 
1455
            #line_ids = fmc_line_obj.search(cr, uid, [('name', '=', product), ('valid_ok', '=', True), ('mrc_id', 'in', fmc_ids)], context=context)
 
1456
            line_ids = fmc_line_obj.search(cr, uid, [('name', '=', product), ('valid_ok', '=', True)], order='last_reviewed desc, mrc_id desc', limit=1, context=context)
790
1457
            
791
1458
            # Get the last created line
792
1459
            for line in fmc_line_obj.browse(cr, uid, line_ids, context=context):
793
 
                if not last_date:
794
 
                    last_date = line.valid_until or line.mrc_id.period_to
795
 
                    res[product] = line.fmc
796
 
                elif line.valid_until and line.valid_until > last_date:
797
 
                    last_date = line.valid_until
798
 
                    res[product] = line.fmc
799
 
                elif line.mrc_id.period_to > last_date:
800
 
                    last_date = line.mrc_id.period_to
801
 
                    res[product] = line.fmc
 
1460
                res[product] = line.fmc
802
1461
        
803
1462
        return res
804
1463
    
805
 
    def compute_mac(self, cr, uid, ids, field_name, args, context={}):
 
1464
    def compute_mac(self, cr, uid, ids, field_name, args, context=None):
806
1465
        '''
807
1466
        Compute the Real Average Consumption
808
1467
        '''
809
1468
        if isinstance(ids, (int, long)):
810
1469
            ids = [ids]
 
1470
        if context is None:
 
1471
            context = {}
811
1472
        
812
 
        line_obj = self.pool.get('real.average.consumption.line')
813
 
        rac_obj = self.pool.get('real.average.consumption')
814
1473
        uom_obj = self.pool.get('product.uom')
815
1474
        
816
1475
        rac_domain = [('created_ok', '=', True)]
818
1477
        
819
1478
        from_date = False
820
1479
        to_date = False
 
1480
 
 
1481
        location_ids = []
821
1482
        
822
1483
        # Read if a interval is defined
823
1484
        if context.get('from_date', False):
836
1497
                location_ids = self.pool.get('stock.location').search(cr, uid, [('name','ilike',context['location'])], context=context)
837
1498
            else:
838
1499
                location_ids = context.get('location_id', [])
839
 
            
840
 
            # Update the domain of research
841
 
            rac_domain.append(('cons_location_id', 'in', location_ids))
842
 
        rac_ids = rac_obj.search(cr, uid, rac_domain, context=context)
843
1500
       
844
1501
        for id in ids:
845
1502
            res[id] = 0.00
846
 
            line_ids = line_obj.search(cr, uid, [('rac_id', 'in', rac_ids), ('product_id', '=', id)], context=context)
 
1503
            if from_date and to_date:
 
1504
                rcr_domain = ['&', '&', ('product_id', 'in', ids), ('rac_id.cons_location_id', 'in', location_ids),
 
1505
                              # All lines with a report started out the period and finished in the period 
 
1506
                              '|', '&', ('rac_id.period_to', '>=', from_date), ('rac_id.period_to', '<=', to_date),
 
1507
                              # All lines with a report started in the period and finished out the period 
 
1508
                              '|', '&', ('rac_id.period_from', '<=', to_date), ('rac_id.period_from', '>=', from_date),
 
1509
                              # All lines with a report started before the period  and finished after the period
 
1510
                              '&', ('rac_id.period_from', '<=', from_date), ('rac_id.period_to', '>=', to_date)]
847
1511
            
848
 
            for line in line_obj.browse(cr, uid, line_ids, context=context):
849
 
                res[id] += uom_obj._compute_qty(cr, uid, line.uom_id.id, line.consumed_qty, line.product_id.uom_id.id)
850
 
                if not context.get('from_date') and (not from_date or line.rac_id.period_to < from_date):
851
 
                    from_date = line.rac_id.period_to
852
 
                if not context.get('to_date') and (not to_date or line.rac_id.period_to > to_date):
853
 
                    to_date = line.rac_id.period_to
 
1512
                rcr_line_ids = self.pool.get('real.average.consumption.line').search(cr, uid, rcr_domain, context=context)
 
1513
                for line in self.pool.get('real.average.consumption.line').browse(cr, uid, rcr_line_ids, context=context):
 
1514
                    cons = self._get_period_consumption(cr, uid, line, from_date, to_date, context=context)
 
1515
                    res[id] += uom_obj._compute_qty(cr, uid, line.uom_id.id, cons, line.product_id.uom_id.id)
854
1516
 
855
 
            # We want the average for the entire period
856
 
            if context.get('average', False):
 
1517
                # We want the average for the entire period
857
1518
                if to_date < from_date:
858
1519
                    raise osv.except_osv(_('Error'), _('You cannot have a \'To Date\' younger than \'From Date\'.'))
859
1520
                # Calculate the # of months in the period
860
 
                to_date_str = strptime(to_date, '%Y-%m-%d')
861
 
                from_date_str = strptime(from_date, '%Y-%m-%d')
 
1521
                try:
 
1522
                    to_date_str = strptime(to_date, '%Y-%m-%d')
 
1523
                except ValueError:
 
1524
                    to_date_str = strptime(to_date, '%Y-%m-%d %H:%M:%S')
862
1525
                
863
 
                date_diff = Age(to_date_str, from_date_str)
864
 
                nb_months = round(date_diff.years*12.0 + date_diff.months + (date_diff.days/30.0), 2)
 
1526
                try:
 
1527
                    from_date_str = strptime(from_date, '%Y-%m-%d')
 
1528
                except ValueError:
 
1529
                    from_date_str = strptime(from_date, '%Y-%m-%d %H:%M:%S')
 
1530
        
 
1531
                nb_months = self._get_date_diff(from_date_str, to_date_str)
865
1532
                
866
1533
                if not nb_months: nb_months = 1
867
 
                
 
1534
 
868
1535
                uom_id = self.browse(cr, uid, ids[0], context=context).uom_id.id
869
1536
                res[id] = res[id]/nb_months
870
1537
                res[id] = round(self.pool.get('product.uom')._compute_qty(cr, uid, uom_id, res[id], uom_id), 2)
871
1538
            
872
1539
        return res
873
1540
    
874
 
    def compute_amc(self, cr, uid, ids, context={}):
 
1541
    def compute_amc(self, cr, uid, ids, context=None):
875
1542
        '''
876
1543
        Compute the Average Monthly Consumption with this formula :
877
1544
            AMC = (sum(OUTGOING (except reason types Loan, Donation, Loss, Discrepancy))
878
1545
                  -
879
1546
                  sum(INCOMING with reason type Return from unit)) / Number of period's months
 
1547
            The AMC is the addition of all done stock moves for a product within a period.
 
1548
            For stock moves generated from a real consumption report, the qty of product is computed
 
1549
            according to the average of consumption for the time of the period.
880
1550
        '''
881
1551
        if not context:
882
1552
            context = {}
906
1576
        loss_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_loss')[1]
907
1577
        discrepancy_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_discrepancy')[1]
908
1578
        return_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_return_from_unit')[1]
 
1579
        return_good_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_goods_return')[1]
 
1580
        replacement_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_goods_replacement')[1]
909
1581
 
910
1582
        # Update the domain
911
1583
        domain = [('state', '=', 'done'), ('reason_type_id', 'not in', (loan_id, donation_id, donation_exp_id, loss_id, discrepancy_id)), ('product_id', 'in', ids)]
912
1584
        if to_date:
913
 
            domain.append(('date_expected', '<=', to_date))
 
1585
            domain.append(('date', '<=', to_date))
914
1586
        if from_date:
915
 
            domain.append(('date_expected', '>=', from_date))
 
1587
            domain.append(('date', '>=', from_date))
916
1588
        
917
1589
        locations = self.pool.get('stock.location').search(cr, uid, [('usage', 'in', ('internal', 'customer'))], context=context)
918
1590
        # Add locations filters in domain if locations are passed in context
919
1591
        domain.append(('location_id', 'in', locations))
920
1592
        domain.append(('location_dest_id', 'in', locations))
921
1593
        
 
1594
        # Search all real consumption line included in the period
 
1595
        # If no period found, take all stock moves
 
1596
        if from_date and to_date:
 
1597
            rcr_domain = ['&', ('product_id', 'in', ids),
 
1598
                          # All lines with a report started out the period and finished in the period 
 
1599
                          '|', '&', ('rac_id.period_to', '>=', from_date), ('rac_id.period_to', '<=', to_date),
 
1600
                          # All lines with a report started in the period and finished out the period 
 
1601
                          '|', '&', ('rac_id.period_from', '<=', to_date), ('rac_id.period_from', '>=', from_date),
 
1602
                          # All lines with a report started before the period  and finished after the period
 
1603
                          '&', ('rac_id.period_from', '<=', from_date), ('rac_id.period_to', '>=', to_date)]
 
1604
        
 
1605
            rcr_line_ids = self.pool.get('real.average.consumption.line').search(cr, uid, rcr_domain, context=context)
 
1606
            report_move_ids = []
 
1607
            for line in self.pool.get('real.average.consumption.line').browse(cr, uid, rcr_line_ids, context=context):
 
1608
                report_move_ids.append(line.move_id.id)
 
1609
                res += self._get_period_consumption(cr, uid, line, from_date, to_date, context=context)
 
1610
            
 
1611
            if report_move_ids:
 
1612
                domain.append(('id', 'not in', report_move_ids))
 
1613
        
922
1614
        out_move_ids = move_obj.search(cr, uid, domain, context=context)
923
1615
        
924
1616
        for move in move_obj.browse(cr, uid, out_move_ids, context=context):
925
 
            if move.reason_type_id.id == return_id and move.location_id.usage == 'customer':
 
1617
            if move.reason_type_id.id in (return_id, return_good_id, replacement_id) and move.location_id.usage == 'customer':
926
1618
                res -= uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
927
1619
            elif move.location_dest_id.usage == 'customer':
928
1620
                res += uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
929
1621
            
930
1622
            # Update the limit in time
931
 
            if not context.get('from_date') and (not from_date or move.date_expected < from_date):
932
 
                from_date = move.date_expected
933
 
            if not context.get('to_date') and (not to_date or move.date_expected > to_date):
934
 
                to_date = move.date_expected
 
1623
            if not context.get('from_date') and (not from_date or move.date < from_date):
 
1624
                from_date = move.date
 
1625
            if not context.get('to_date') and (not to_date or move.date > to_date):
 
1626
                to_date = move.date
935
1627
                
936
1628
        if not to_date or not from_date:
937
1629
            return 0.00
950
1642
        except ValueError:
951
1643
            from_date_str = strptime(from_date, '%Y-%m-%d %H:%M:%S')
952
1644
 
953
 
        date_diff = Age(to_date_str, from_date_str)
954
 
        nb_months = date_diff.years*12 + date_diff.months + (date_diff.days/30)
 
1645
        nb_months = self._get_date_diff(from_date_str, to_date_str)
955
1646
        
956
1647
        if not nb_months: nb_months = 1
957
1648
        
958
 
        uom_id = self.browse(cr, uid, ids[0], context=context).uom_id.id
 
1649
        uom_id = self.read(cr, uid, ids[0], ['uom_id'], context=context)['uom_id'][0]
959
1650
        res = res/nb_months
960
1651
        res = self.pool.get('product.uom')._compute_qty(cr, uid, uom_id, res, uom_id)
961
1652
            
962
1653
        return res
963
1654
    
 
1655
    def _get_date_diff(self, from_date, to_date):
 
1656
        '''
 
1657
        Returns the number of months between to dates according to the number
 
1658
        of days in the month.
 
1659
        '''
 
1660
        diff_date = Age(to_date, from_date)
 
1661
        res = 0.0
 
1662
        
 
1663
        def days_in_month(month, year):
 
1664
            '''
 
1665
            Returns the # of days in the month
 
1666
            '''
 
1667
            res = 30
 
1668
            if month == 2 and year%4 == 0:
 
1669
                res = 29
 
1670
            elif month == 2 and year%4 != 0:
 
1671
                res = 28
 
1672
            elif month in (1, 3, 5, 7, 8, 10, 12):
 
1673
                res = 31
 
1674
            return res
 
1675
        
 
1676
        while from_date <= to_date:
 
1677
            # Add 12 months by years between the two dates
 
1678
            if diff_date.years:
 
1679
                res += diff_date.years*12
 
1680
                from_date += RelativeDate(years=diff_date.years)
 
1681
                diff_date = Age(to_date, from_date)
 
1682
            else:
 
1683
                # If two dates are in the same month
 
1684
                if from_date.month == to_date.month:
 
1685
                    nb_days_in_month = days_in_month(from_date.month, from_date.year)
 
1686
                    # We divided the # of days between the two dates by the # of days in month
 
1687
                    # to have a percentage of the number of month
 
1688
                    res += round((to_date.day-from_date.day+1)/nb_days_in_month, 2)
 
1689
                    break
 
1690
                elif to_date.month - from_date.month > 1 or to_date.year - from_date.year > 0:
 
1691
                    res += 1
 
1692
                    from_date += RelativeDate(months=1)
 
1693
                else:
 
1694
                    # Number of month till the end of from month
 
1695
                    fr_nb_days_in_month = days_in_month(from_date.month, from_date.year)
 
1696
                    nb_days = fr_nb_days_in_month - from_date.day + 1
 
1697
                    res += round(nb_days/fr_nb_days_in_month, 2)
 
1698
                    # Number of month till the end of from month
 
1699
                    to_nb_days_in_month = days_in_month(to_date.month, to_date.year)  
 
1700
                    res += round(to_date.day/to_nb_days_in_month, 2)
 
1701
                    break
 
1702
                    
 
1703
        return res
 
1704
                     
 
1705
            
 
1706
 
 
1707
    def _compute_product_amc(self, cr, uid, ids, field_name, args, ctx=None):
 
1708
        if ctx is None:
 
1709
            ctx = {}
 
1710
        context = ctx.copy()
 
1711
        res = {}
 
1712
        from_date = (DateFrom(time.strftime('%Y-%m-%d')) + RelativeDateTime(months=-3, day=1)).strftime('%Y-%m-%d')
 
1713
        to_date = (DateFrom(time.strftime('%Y-%m-%d')) + RelativeDateTime(day=1, days=-1)).strftime('%Y-%m-%d')
 
1714
 
 
1715
        if context.get('from_date', False):
 
1716
            from_date = (DateFrom(context.get('from_date')) + RelativeDateTime(day=1)).strftime('%Y-%m-%d')
 
1717
                                               
 
1718
        if context.get('to_date', False):
 
1719
            to_date = (DateFrom(context.get('to_date')) + RelativeDateTime(months=1, day=1, days=-1)).strftime('%Y-%m-%d')
 
1720
 
 
1721
        context.update({'from_date': from_date})
 
1722
        context.update({'to_date': to_date})
 
1723
 
 
1724
        for product in ids:
 
1725
            res[product] = self.compute_amc(cr, uid, product, context=context)
 
1726
 
 
1727
        return res
 
1728
    
 
1729
    def _get_period_consumption(self, cr, uid, line, from_date, to_date, context=None):
 
1730
        '''
 
1731
        Returns the average quantity of product in the period
 
1732
        '''        
 
1733
        # Compute the # of days in the report period
 
1734
        if context is None:
 
1735
            context = {}
 
1736
        from datetime import datetime
 
1737
        report_from = datetime.strptime(line.rac_id.period_from, '%Y-%m-%d')
 
1738
        report_to = datetime.strptime(line.rac_id.period_to, '%Y-%m-%d')
 
1739
        dt_from_date = datetime.strptime(from_date, '%Y-%m-%d')
 
1740
        dt_to_date = datetime.strptime(to_date, '%Y-%m-%d')
 
1741
        delta = report_to - report_from
 
1742
 
 
1743
        # Add 1 to include the last day of report to        
 
1744
        report_nb_days = delta.days + 1
 
1745
        days_incl = 0
 
1746
        
 
1747
        # Case where the report is totally included in the period
 
1748
        if line.rac_id.period_from >= from_date and line.rac_id.period_to <= to_date:
 
1749
            return line.consumed_qty
 
1750
        # Case where the report started before the period and done after the period
 
1751
        elif line.rac_id.period_from <= from_date and line.rac_id.period_to >= to_date:
 
1752
            # Compute the # of days of the period
 
1753
            delta2 = dt_to_date - dt_from_date
 
1754
            days_incl = delta2.days +1
 
1755
        # Case where the report started before the period and done in the period
 
1756
        elif line.rac_id.period_from <= from_date and line.rac_id.period_to <= to_date and line.rac_id.period_to >= from_date:
 
1757
            # Compute the # of days of the report included in the period
 
1758
            # Add 1 to include the last day of report to
 
1759
            delta2 = report_to - dt_from_date
 
1760
            days_incl = delta2.days +1
 
1761
        # Case where the report started in the period and done after the period
 
1762
        elif line.rac_id.period_from >= from_date and line.rac_id.period_to >= to_date and line.rac_id.period_from <= to_date:
 
1763
            # Compute the # of days of the report included in the period
 
1764
            # Add 1 to include the last day of to_date
 
1765
            delta2 = dt_to_date - report_from
 
1766
            days_incl = delta2.days +1
 
1767
        
 
1768
        # Compute the quantity consumed in the period for this line
 
1769
        consumed_qty = (line.consumed_qty/report_nb_days)*days_incl
 
1770
        return self.pool.get('product.uom')._compute_qty(cr, uid, line.uom_id.id, consumed_qty, line.uom_id.id)
964
1771
    
965
1772
    _columns = {
966
1773
        'procure_delay': fields.float(digits=(16,2), string='Procurement Lead Time', 
967
1774
                                        help='It\'s the default time to procure this product. This lead time will be used on the Order cycle procurement computation'),
968
 
        'monthly_consumption': fields.function(compute_mac, method=True, type='float', string='Monthly consumption', readonly=True),
 
1775
        'monthly_consumption': fields.function(compute_mac, method=True, type='float', string='Real Consumption', readonly=True),
 
1776
        'product_amc': fields.function(_compute_product_amc, method=True, type='float', string='Monthly consumption', readonly=True),
969
1777
        'reviewed_consumption': fields.function(_compute_fmc, method=True, type='float', string='Forecasted Monthly Consumption', readonly=True),
970
1778
    }
971
1779
    
972
1780
    _defaults = {
973
 
        'procure_delay': lambda *a: 1,
 
1781
        'procure_delay': lambda *a: 60,
974
1782
    }
975
1783
 
976
1784
    
988
1796
        report_ids = self.pool.get('real.average.consumption').search(cr, uid, [('picking_id', '=', pick.id)], context=context)
989
1797
        if report_ids:
990
1798
            name = self.pool.get('real.average.consumption').browse(cr, uid, report_ids[0], context=context).picking_id.name
991
 
            return 'Delivery Order %s generated from the consumption report is done.' % name
 
1799
            return 'Delivery Order %s generated from the consumption report is closed.' % name
992
1800
        else:
993
1801
            return super(stock_picking, self)._hook_log_picking_modify_message(cr, uid, ids, context=context, message=message, pick=pick)
994
1802
 
995
1803
stock_picking()
 
1804
 
 
1805
class stock_location(osv.osv):
 
1806
    _inherit = 'stock.location'
 
1807
 
 
1808
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
 
1809
        if context is None:
 
1810
            context = {}
 
1811
        if context.get('no3buttons') and view_type == 'search':
 
1812
            view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'consumption_calculation', 'view_stock_location_without_buttons')
 
1813
        return super(stock_location, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
 
1814
stock_location()