[FIX]get & set odometer for fleet.vehicle object
[odoo/odoo.git] / addons / fleet / fleet.py
1 from itertools import chain
2 from osv import osv, fields
3 import time
4 import tools
5 import datetime
6 from osv.orm import except_orm
7 from tools.translate import _
8 from import_base.mapper import *
9 ############################
10 ############################
11 #Vehicle.cost class
12 ############################
13 ############################
14
15 class fleet_vehicle_cost(osv.Model):
16     _name = 'fleet.vehicle.cost'
17     _description = 'Cost of vehicle'
18     _order = 'date desc, vehicle_id asc'
19
20     def name_get(self, cr, uid, ids, context=None):
21         if context is None:
22             context = {}
23         if not ids:
24             return []
25         reads = self.browse(cr, uid, ids, context=context)
26         res = []
27         for record in reads:
28             if record.vehicle_id.license_plate:
29                 name = record.vehicle_id.license_plate
30             if record.cost_type.name:
31                 name = name + ' / '+ record.cost_type.name
32             if record.date:
33                 name = name + ' / '+ record.date
34             res.append((record.id, name))
35         return res
36
37     def _cost_name_get_fnc(self, cr, uid, ids, name, unknow_none, context=None):
38         res = self.name_get(cr, uid, ids, context=context)
39         return dict(res)
40
41     _columns = {
42         'name' : fields.function(_cost_name_get_fnc, type="char", string='Name', store=True),
43         #'name' : fields.char('Name',size=32),
44         'vehicle_id': fields.many2one('fleet.vehicle', 'Vehicle', required=True, help='Vehicle concerned by this fuel log'),
45         'cost_type': fields.many2one('fleet.service.type', 'Service type', required=False, help='Service type purchased with this cost'),
46         'amount': fields.float('Total Price'),
47
48         'parent_id': fields.many2one('fleet.vehicle.cost', 'Parent', required=False, help='Parent cost to this current cost'),
49         'cost_ids' : fields.one2many('fleet.vehicle.cost', 'parent_id', 'Included Services'),
50
51         'date' :fields.date('Cost Date',help='Date when the cost has been executed'),
52     }
53
54     _default ={
55         'parent_id':None,
56     }
57
58     
59
60     def create(self, cr, uid, data, context=None):
61         if 'parent_id' in data and data['parent_id']:
62             data['vehicle_id'] = self.browse(cr, uid, data['parent_id'], context=context).vehicle_id.id
63             data['date'] = self.browse(cr, uid, data['parent_id'], context=context).date
64         cost_id = super(fleet_vehicle_cost, self).create(cr, uid, data, context=context)
65         return cost_id
66
67 ############################
68 ############################
69 #Vehicle.tag class
70 ############################
71 ############################
72
73 class fleet_vehicle_tag(osv.Model):
74     _name = 'fleet.vehicle.tag'
75     _columns = {
76         'name': fields.char('Name', required=True, translate=True),
77     }
78
79 ############################
80 ############################
81 #Vehicle.state class
82 ############################
83 ############################
84
85 class fleet_vehicle_state(osv.Model):
86     _name = 'fleet.vehicle.state'
87     _columns = {
88         'name': fields.char('Name', required=True),
89         'sequence': fields.integer('Order',help="Used to order the note stages")
90     }
91     _order = 'sequence asc'
92
93 ############################
94 ############################
95 #Vehicle.model class
96 ############################
97 ############################
98
99 class fleet_vehicle_model(osv.Model):
100
101     def name_get(self, cr, uid, ids, context=None):
102         if context is None:
103             context = {}
104         if not ids:
105             return []
106         reads = self.browse(cr, uid, ids, context=context)
107         res = []
108         for record in reads:
109             name = record.modelname
110             if record.brand.name:
111                 name = record.brand.name+' / '+name
112             res.append((record.id, name))
113         return res
114
115     def _model_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
116         res = self.name_get(cr, uid, ids, context=context)
117         return dict(res)
118
119     def on_change_brand(self, cr, uid, ids, model_id, context=None):
120
121         if not model_id:
122             return {}
123
124         brand = self.pool.get('fleet.vehicle.model.brand').browse(cr, uid, model_id, context=context)
125
126         return {
127             'value' : {
128                 'image' : brand.image,
129             }
130         }
131
132     _name = 'fleet.vehicle.model'
133     _description = 'Model of a vehicle'
134
135     _columns = {
136         'name' : fields.function(_model_name_get_fnc, type="char", string='Name', store=True),
137         'modelname' : fields.char('Model name', size=32, required=True), 
138         'brand' : fields.many2one('fleet.vehicle.model.brand', 'Model Brand', required=True, help='Brand of the vehicle'),
139         'vendors': fields.many2many('res.partner','fleet_vehicle_model_vendors','model_id', 'partner_id',string='Vendors',required=False),
140         'image': fields.related('brand','image',type="binary",string="Logo",store=False),
141         'image_medium': fields.related('brand','image_medium',type="binary",string="Logo",store=False),
142         'image_small': fields.related('brand','image_small',type="binary",string="Logo",store=False),
143     }
144
145 ############################
146 ############################
147 #Vehicle.brand class
148 ############################
149 ############################
150
151 class fleet_vehicle_model_brand(osv.Model):
152     _name = 'fleet.vehicle.model.brand'
153     _description = 'Brand model of the vehicle'
154
155     _order = 'name asc'
156
157     def _get_image(self, cr, uid, ids, name, args, context=None):
158         result = dict.fromkeys(ids, False)
159         for obj in self.browse(cr, uid, ids, context=context):
160             result[obj.id] = tools.image_get_resized_images(obj.image)
161         return result
162     
163     def _set_image(self, cr, uid, id, name, value, args, context=None):
164         return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
165
166     _columns = {
167         'name' : fields.char('Brand Name',size=32, required=True),
168
169         'image': fields.binary("Logo",
170             help="This field holds the image used as logo for the brand, limited to 1024x1024px."),
171         'image_medium': fields.function(_get_image, fnct_inv=_set_image,
172             string="Medium-sized photo", type="binary", multi="_get_image",
173             store = {
174                 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
175             },
176             help="Medium-sized logo of the brand. It is automatically "\
177                  "resized as a 128x128px image, with aspect ratio preserved. "\
178                  "Use this field in form views or some kanban views."),
179         'image_small': fields.function(_get_image, fnct_inv=_set_image,
180             string="Smal-sized photo", type="binary", multi="_get_image",
181             store = {
182                 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
183             },
184             help="Small-sized photo of the brand. It is automatically "\
185                  "resized as a 64x64px image, with aspect ratio preserved. "\
186                  "Use this field anywhere a small image is required."),
187     }
188
189 ############################
190 ############################
191 #Vehicle class
192 ############################
193 ############################
194
195
196 class fleet_vehicle(osv.Model):
197
198     _inherit = 'mail.thread'
199
200     def name_get(self, cr, uid, ids, context=None):
201         if context is None:
202             context = {}
203         if not ids:
204             return []
205         reads = self.browse(cr, uid, ids, context=context)
206         res = []
207         for record in reads:
208             if record.license_plate:
209                 name = record.license_plate
210             if record.model_id.modelname:
211                 name = record.model_id.modelname + ' / ' + name
212             if record.model_id.brand.name:
213                 name = record.model_id.brand.name+' / '+ name
214             res.append((record.id, name))
215         return res
216
217     def _vehicle_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
218         res = self.name_get(cr, uid, ids, context=context)
219         return dict(res)
220
221     def act_show_log_services(self, cr, uid, ids, context=None):
222         """ This opens log view to view and add new log for this vehicle
223             @return: the service log view
224         """
225         res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_services', context)
226         res['context'] = {
227             'default_vehicle_id': ids[0]
228         }
229         res['domain']=[('vehicle_id','=', ids[0])]
230         return res
231
232     def act_show_log_contract(self, cr, uid, ids, context=None):
233         """ This opens log view to view and add new log for this vehicle
234             @return: the contract log view
235         """
236         res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_contract', context)
237         res['context'] = {
238             'default_vehicle_id': ids[0]
239         }
240         res['domain']=[('vehicle_id','=', ids[0])]
241         return res
242
243     def act_show_log_fuel(self, cr, uid, ids, context=None):
244         """ This opens log view to view and add new log for this vehicle
245             @return: the fuel log view
246         """
247         res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_fuel', context)
248         res['context'] = {
249             'default_vehicle_id': ids[0]
250         }
251         res['domain']=[('vehicle_id','=', ids[0])]
252         return res
253
254     def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
255         res = dict.fromkeys(ids, False)
256         for record in self.browse(cr,uid,ids,context=context):    
257             ids = self.pool.get('fleet.vehicle.odometer').search(cr,uid,[('vehicle_id','=',record.id)],limit=1, order='value desc')
258             if len(ids) > 0:
259                 res[record.id] = str(self.pool.get('fleet.vehicle.odometer').browse(cr,uid,ids[0],context=context).value)
260             else:
261                 res[record.id] = str(0)
262         return res
263
264     def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
265         if value:
266             try:
267                 value = float(value)
268             except ValueError:
269                 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
270                 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
271             
272             date = time.strftime('%Y-%m-%d')
273             data = {'value' : value,'date' : date,'vehicle_id' : id}
274             odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
275             return value
276         self.write(cr, uid, id, {'odometer_id': ''})
277         return False  
278
279     def str_to_date(self,strdate):
280         return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
281
282     def get_overdue_contract_reminder_fnc(self,cr,uid,ids,context=None):
283         if context is None:
284             context={}
285         if not ids:
286             return dict([])
287         reads = self.browse(cr,uid,ids,context=context)
288         res=[]
289         
290         for record in reads:
291             overdue=0
292             if (record.log_contracts):
293                 for element in record.log_contracts:
294                     if (element.state=='open' and element.expiration_date):
295                         current_date_str=time.strftime('%Y-%m-%d')
296                         due_time_str=element.expiration_date
297                             #due_time_str=element.browse()
298                         current_date=self.str_to_date(current_date_str)
299                         due_time=self.str_to_date(due_time_str)
300              
301                         diff_time=int((due_time-current_date).days)
302                         if diff_time<0:
303                             overdue = overdue +1;
304                 res.append((record.id,overdue))
305             else:
306                 res.append((record.id,0))
307
308         return dict(res)
309
310     def get_overdue_contract_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
311         res = self.get_overdue_contract_reminder_fnc(cr, uid, ids, context=context)
312         return res
313
314     def get_next_contract_reminder_fnc(self,cr,uid,ids,context=None):
315         if context is None:
316             context={}
317         if not ids:
318             return dict([])
319         reads = self.browse(cr,uid,ids,context=context)
320         res=[]
321
322         for record in reads:
323             due_soon=0
324             if (record.log_contracts):
325                 for element in record.log_contracts:
326                     if (element.state=='open' and element.expiration_date):
327                         current_date_str=time.strftime('%Y-%m-%d')
328                         due_time_str=element.expiration_date
329                             #due_time_str=element.browse()
330                         current_date=self.str_to_date(current_date_str)
331                         due_time=self.str_to_date(due_time_str)
332              
333                         diff_time=int((due_time-current_date).days)
334                         if diff_time<15 and diff_time>=0:
335                             due_soon = due_soon +1;
336                 res.append((record.id,due_soon))
337             else:
338                 res.append((record.id,0))
339         
340         return dict(res)
341
342     def get_next_contract_reminder(self, cr, uid, ids, prop, unknow_none, context=None):
343         res = self.get_next_contract_reminder_fnc(cr, uid, ids, context=context)
344         return res
345
346     def run_scheduler(self,cr,uid,context=None):
347         ids = self.pool.get('fleet.vehicle').search(cr, uid, [], offset=0, limit=None, order=None,context=None, count=False)
348         nexts = self.get_next_contract_reminder_fnc(cr,uid,ids,context=context)
349         overdues = self.get_overdue_contract_reminder_fnc(cr,uid,ids,context=context)
350         for key,value in nexts.items():
351             if value > 0 and overdues[key] > 0:
352                 self.message_post(cr, uid, [key], body=str(value) + ' contract(s) has to be renewed soon and '+str(overdues[key])+' contract(s) is (are) overdued', context=context)
353             elif value > 0:
354                 self.message_post(cr, uid, [key], body=str(value) + ' contract(s) has to be renewed soon!', context=context)
355             elif overdues[key] > 0 : 
356                 self.message_post(cr, uid, [key], body=str(overdues.key) + ' contract(s) is(are) overdued!', context=context)
357         return True
358
359     _name = 'fleet.vehicle'
360     _description = 'Fleet Vehicle'
361     #_order = 'contract_renewal_overdue desc, contract_renewal_due_soon desc'
362     _order= 'name asc'
363     _columns = {
364         'name' : fields.function(_vehicle_name_get_fnc, type="char", string='Name', store=True),
365
366         'company_id': fields.many2one('res.company', 'Company'),
367         'license_plate' : fields.char('License Plate', size=32, required=True, help='License plate number of the vehicle (ie: plate number for a car)'),
368         'vin_sn' : fields.char('Chassis Number', size=32, required=False, help='Unique number written on the vehicle motor (VIN/SN number)'),
369         'driver' : fields.many2one('res.partner', 'Driver',required=False, help='Driver of the vehicle', domain="['|',('customer','=',True),('employee','=',True)]"),
370         'model_id' : fields.many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle'),
371         'log_fuel' : fields.one2many('fleet.vehicle.log.fuel','vehicle_id', 'Fuel Logs'),
372         'log_services' : fields.one2many('fleet.vehicle.log.services','vehicle_id', 'Services Logs'),
373         'log_contracts' : fields.one2many('fleet.vehicle.log.contract','vehicle_id', 'Contracts'),
374         'acquisition_date' : fields.date('Acquisition Date', required=False, help='Date when the vehicle has been bought'),
375         'color' : fields.char('Color',size=32, help='Color of the vehicle'),
376         'state': fields.many2one('fleet.vehicle.state', 'State', help='Current state of the vehicle', ),
377         'location' : fields.char('Location',size=32, help='Location of the vehicle (garage, ...)'),
378         'doors' : fields.integer('Doors Number', help='Number of doors of the vehicle'),
379         'tag_ids' :fields.many2many('fleet.vehicle.tag','fleet_vehicle_vehicle_tag_rel','vehicle_tag_id','tag_id','Tags'),
380
381         'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer Value',store=False,help='Odometer measure of the vehicle at the moment of this log'),
382         'odometer_unit': fields.selection([('kilometers', 'Kilometers'),('miles','Miles')], 'Odometer Unit', help='Unit of the odometer ',required=False),
383
384         'transmission' : fields.selection([('manual', 'Manual'),('automatic','Automatic')], 'Transmission', help='Transmission Used by the vehicle',required=False),
385         'fuel_type' : fields.selection([('gasoline', 'Gasoline'),('diesel','Diesel'),('electric','Electric'),('hybrid','Hybrid')], 'Fuel Type', help='Fuel Used by the vehicle',required=False),
386         'horsepower' : fields.integer('Horsepower',required=False),
387         'horsepower_tax': fields.float('Horsepower Taxation'),
388         'power' : fields.integer('Power (kW)',required=False,help='Power in kW of the vehicle'),
389         'co2' : fields.float('CO2 Emissions',required=False,help='CO2 emissions of the vehicle'),
390
391         'image': fields.related('model_id','image',type="binary",string="Logo",store=False),
392         'image_medium': fields.related('model_id','image_medium',type="binary",string="Logo",store=False),
393         'image_small': fields.related('model_id','image_small',type="binary",string="Logo",store=False),
394
395         'contract_renewal_due_soon' : fields.function(get_next_contract_reminder,type="integer",string='Contract Renewal Due Soon',store=False),
396         'contract_renewal_overdue' : fields.function(get_overdue_contract_reminder,type="integer",string='Contract Renewal Overdue',store=False),
397         
398         'car_value': fields.float('Car value', help='Value of the bought vehicle'),
399         #'leasing_value': fields.float('Leasing value',help='Value of the leasing(Monthly, usually'),
400         }
401
402     _defaults = {
403         'doors' : 5,
404         'odometer_unit' : 'kilometers',
405     }
406
407     def copy(self, cr, uid, id, default=None, context=None):
408         if not default:
409             default = {}
410
411         default.update({
412         #    'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
413             #'log_ids':[],
414             'log_fuel':[],
415             'log_contracts':[],
416             'log_services':[],
417             'tag_ids':[],
418         })
419         return super(fleet_vehicle, self).copy(cr, uid, id, default, context=context)
420
421     def on_change_model(self, cr, uid, ids, model_id, context=None):
422
423         if not model_id:
424             return {}
425
426         model = self.pool.get('fleet.vehicle.model').browse(cr, uid, model_id, context=context)
427
428         return {
429             'value' : {
430                 'image' : model.image,
431             }
432         }
433     def create(self, cr, uid, data, context=None):
434         vehicle_id = super(fleet_vehicle, self).create(cr, uid, data, context=context)
435         try:
436             vehicle = self.browse(cr, uid, vehicle_id, context=context)
437             self.message_post(cr, uid, [vehicle_id], body='Vehicle %s has been added to the fleet!' % (vehicle.license_plate), context=context)
438         except:
439             pass # group deleted: do not push a message
440         return vehicle_id
441
442     def write(self, cr, uid, ids, vals, context=None):
443         changes = []
444         if 'driver' in vals:
445             value = self.pool.get('res.partner').browse(cr,uid,vals['driver'],context=context).name
446             changes.append('Driver: from \'' + self.browse(cr, uid, ids, context)[0].driver.name + '\' to \'' + value+'\'')
447         if 'state' in vals:
448             value = self.pool.get('fleet.vehicle.state').browse(cr,uid,vals['state'],context=context).name
449             changes.append('State: from \'' + self.browse(cr, uid, ids, context)[0].state.name + '\' to \'' + value+'\'')
450         if 'license_plate' in vals:
451             changes.append('License Plate: from \'' + self.browse(cr, uid, ids, context)[0].license_plate + '\' to \'' + vals['license_plate']+'\'')   
452        
453         vehicle_id = super(fleet_vehicle,self).write(cr, uid, ids, vals, context)
454
455         try:
456             if len(changes) > 0:
457                 self.message_post(cr, uid, [self.browse(cr, uid, ids, context)[0].id], body=", ".join(changes), context=context)
458         except Exception as e:
459             print e
460             pass
461         return vehicle_id
462
463 ############################
464 ############################
465 #Vehicle.odometer class
466 ############################
467 ############################
468
469 class fleet_vehicle_odometer(osv.Model):
470     _name='fleet.vehicle.odometer'
471     _description='Odometer log for a vehicle'
472
473     _order='date desc'
474
475     def name_get(self, cr, uid, ids, context=None):
476         if context is None:
477             context = {}
478         if not ids:
479             return []
480         reads = self.browse(cr, uid, ids, context=context)
481         res = []
482         for record in reads:
483             if record.vehicle_id.name:
484                 name = str(record.vehicle_id.name)
485             if record.date:
486                 name = name+ ' / '+ str(record.date)
487             res.append((record.id, name))
488         return res
489
490     def _vehicle_log_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
491         res = self.name_get(cr, uid, ids, context=context)
492         return dict(res)
493     def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
494
495         if not vehicle_id:
496             return {}
497
498         odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
499
500         return {
501             'value' : {
502                 'unit' : odometer_unit,
503             }
504         }
505
506     _columns = {
507         'name' : fields.function(_vehicle_log_name_get_fnc, type="char", string='Name', store=True),
508
509         'date' : fields.date('Purchase Date'),
510         'value' : fields.float('Odometer Value',group_operator="max"),
511         'vehicle_id' : fields.many2one('fleet.vehicle', 'Vehicle', required=True),
512         'unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
513         
514     }
515     _defaults = {
516         'date' : time.strftime('%Y-%m-%d')
517     }
518
519 ############################
520 ############################
521 #Vehicle.log classes
522 ############################
523 ############################
524
525
526 ############################
527 ############################
528 #Vehicle.log.fuel class
529 ############################
530 ############################
531
532
533 class fleet_vehicle_log_fuel(osv.Model):
534
535     #_inherits = {'fleet.vehicle.odometer': 'odometer_id'}
536     _inherits = {'fleet.vehicle.cost': 'cost_id'}
537
538     def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
539
540         if not vehicle_id:
541             return {}
542
543         odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
544
545         return {
546             'value' : {
547                 'odometer_unit' : odometer_unit,
548             }
549         }
550
551     def on_change_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
552
553         if liter > 0 and price_per_liter > 0:
554             return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
555         elif liter > 0 and amount > 0:
556             return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
557         elif price_per_liter > 0 and amount > 0:
558             return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
559         else :
560             return {}
561
562     def on_change_price_per_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
563
564         liter = float(liter);
565         price_per_liter = float(price_per_liter);
566         if price_per_liter > 0 and liter > 0:
567             return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
568         elif price_per_liter > 0 and amount > 0:
569             return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
570         elif liter > 0 and amount > 0:
571             return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
572         else :
573             return {}
574
575     def on_change_amount(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
576
577         if amount > 0 and liter > 0:
578             return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
579         elif amount > 0 and price_per_liter > 0:
580             return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
581         elif liter > 0 and price_per_liter > 0:
582             return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
583         else :
584             return {}
585         
586     def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
587         res = dict.fromkeys(ids, False)
588         for record in self.browse(cr,uid,ids,context=context):
589             if record.odometer_id:
590                 res[record.id] = record.odometer_id.value
591         return res
592
593     def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
594         if value:
595             try:
596                 value = float(value)
597             except ValueError:
598                 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
599                 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
600                
601             date = self.browse(cr, uid, id, context=context).date
602             if not(date):
603                 date = time.strftime('%Y-%m-%d')
604             vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
605             data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
606             odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
607             self.write(cr, uid, id, {'odometer_id': odometer_id})
608             return value
609         self.write(cr, uid, id, {'odometer_id': ''})
610         return False
611
612     def _get_default_service_type(self, cr, uid, context):
613         model, model_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'fleet', 'type_service_refueling')
614         return model_id
615
616     _name = 'fleet.vehicle.log.fuel'
617
618     _columns = {
619         #'name' : fields.char('Name',size=64),
620         'liter' : fields.float('Liter'),
621         'price_per_liter' : fields.float('Price Per Liter'),
622         'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
623         'inv_ref' : fields.char('Invoice Reference', size=64),
624         'vendor_id' : fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
625         'notes' : fields.text('Notes'),
626         'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
627         'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer',store=False),
628         'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
629     }
630     _defaults = {
631         'purchaser_id': lambda self, cr, uid, ctx: uid,
632         'date' : time.strftime('%Y-%m-%d'),
633         'cost_type': _get_default_service_type,
634     }
635
636 ############################
637 ############################
638 #Vehicle.log.service class
639 ############################
640 ############################
641
642
643 class fleet_vehicle_log_services(osv.Model):
644
645     _inherits = {'fleet.vehicle.cost': 'cost_id'}
646
647     def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
648
649         if not vehicle_id:
650             return {}
651
652         odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
653
654         return {
655             'value' : {
656                 'odometer_unit' : odometer_unit,
657             }
658         }
659
660     def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
661         res = dict.fromkeys(ids, False)
662         for record in self.browse(cr,uid,ids,context=context):
663             if record.odometer_id:
664                 res[record.id] = record.odometer_id.value
665         return res
666
667     def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
668         if value:
669             try:
670                 value = float(value)
671             except ValueError:
672                 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
673                 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
674                
675             date = self.browse(cr, uid, id, context=context).date
676             if not(date):
677                 date = time.strftime('%Y-%m-%d')
678             vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
679             data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
680             odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
681             self.write(cr, uid, id, {'odometer_id': odometer_id})
682             return value
683         self.write(cr, uid, id, {'odometer_id': ''})
684         return False
685
686     _name = 'fleet.vehicle.log.services'
687     _columns = {
688
689         #'name' : fields.char('Name',size=64),
690
691         'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
692         'inv_ref' : fields.char('Invoice Reference', size=64),
693         'vendor_id' :fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
694         'notes' : fields.text('Notes'),
695
696         'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
697         'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer Value',store=False),
698         'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
699     }
700     _defaults = {
701         'purchaser_id': lambda self, cr, uid, ctx: uid,
702         'date' : time.strftime('%Y-%m-%d'),
703     }
704
705 ############################
706 ############################
707 #Vehicle.service.type class
708 ############################
709 ############################
710
711 class fleet_service_type(osv.Model):
712     _name = 'fleet.service.type'
713     _columns = {
714         'name': fields.char('Name', required=True, translate=True),
715         'category': fields.selection([('contract', 'Contract'), ('service', 'Service'),('both', 'Both')], 'Category',required=True, help='Choose wheter the service refer to contracts, vehicle services or both'),
716     }
717     #_defaults = {
718     #    'category': 'both'
719     #}
720
721 ############################
722 ############################
723 #Vehicle.log.contract class
724 ############################
725 ############################
726
727 class fleet_vehicle_log_contract(osv.Model):
728
729     _inherits = {'fleet.vehicle.cost': 'cost_id'}
730     
731     def name_get(self, cr, uid, ids, context=None):
732         if context is None:
733             context = {}
734         if not ids:
735             return []
736         reads = self.browse(cr, uid, ids, context=context)
737         res = []
738         for record in reads:
739             if record.vehicle_id.name:
740                 name = str(record.vehicle_id.name)
741             if record.cost_type.name:
742                 name = name+ ' / '+ str(record.cost_type.name)
743             res.append((record.id, name))
744         return res
745
746     def _vehicle_contract_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
747         res = self.name_get(cr, uid, ids, context=context)
748         return dict(res)
749
750     def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
751         res = dict.fromkeys(ids, False)
752         for record in self.browse(cr,uid,ids,context=context):
753             if record.odometer_id:
754                 res[record.id] = record.odometer_id.value
755         return res
756
757     def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
758         if value:
759             try:
760                 value = float(value)
761             except ValueError:
762                 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
763                 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
764                
765             date = self.browse(cr, uid, id, context=context).date
766             if not(date):
767                 date = time.strftime('%Y-%m-%d')
768             vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
769             data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
770             odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
771             self.write(cr, uid, id, {'odometer_id': odometer_id})
772             return value
773         self.write(cr, uid, id, {'odometer_id': ''})
774         return False
775
776     def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
777
778         if not vehicle_id:
779             return {}
780
781         odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
782
783         return {
784             'value' : {
785                 'odometer_unit' : odometer_unit,
786             }
787         }
788
789     def compute_next_year_date(self, strdate):
790         oneyear=datetime.timedelta(days=365)
791         curdate = self.str_to_date(strdate)
792         nextyear=curdate+oneyear#int(strdate[:4])+1
793         return str(nextyear)#+strdate[4:]
794
795     def on_change_start_date(self, cr, uid, ids, strdate, context=None):
796         if (strdate):
797            
798             return {'value' : {'expiration_date' : self.compute_next_year_date(strdate),}}
799         else:
800             return {}
801
802     def str_to_date(self,strdate):
803         return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
804
805     def get_warning_date(self,cr,uid,ids,prop,unknow_none,context=None):
806         if context is None:
807             context={}
808         if not ids:
809             return dict([])
810         reads = self.browse(cr,uid,ids,context=context)
811         res=[]
812         for record in reads:
813             #if (record.reminder==True):
814             if (record.expiration_date and record.state=='open'):
815                 today=self.str_to_date(time.strftime('%Y-%m-%d'))
816                 renew_date = self.str_to_date(record.expiration_date)
817                 diff_time=int((renew_date-today).days)
818                 if (diff_time<=0):
819                     res.append((record.id,0))
820                 else:
821                     res.append((record.id,diff_time))
822             else:
823                 res.append((record.id,-1))
824             #else:
825             #    res.append((record.id,-1))
826         return dict(res)
827
828     _name = 'fleet.vehicle.log.contract'
829     _order='state,expiration_date'
830     _columns = {
831         'name' : fields.function(_vehicle_contract_name_get_fnc, type="text", string='Name', store=True),
832         #'name' : fields.char('Name',size=64),
833
834         #'cost_type': fields.many2one('fleet.service.type', 'Service type', required=False, help='Service type purchased with this cost', domain="[('category','=','contract')]"),
835
836         'start_date' : fields.date('Start Date', required=False, help='Date when the coverage of the contract begins'),
837         'expiration_date' : fields.date('Expiration Date', required=False, help='Date when the coverage of the contract expirates (by default, one year after begin date)'),
838         'warning_date' : fields.function(get_warning_date,type='integer',string='Warning Date',store=False),
839
840         'insurer_id' :fields.many2one('res.partner', 'Insurer', domain="[('supplier','=',True)]"),
841         'purchaser_id' : fields.many2one('res.partner', 'Contractor',domain="['|',('customer','=',True),('employee','=',True)]",help='Person to which the contract is signed for'),
842         'ins_ref' : fields.char('Contract Reference', size=64),
843         'state' : fields.selection([('open', 'In Progress'), ('closed', 'Terminated')], 'Status', readonly=True, help='Choose wheter the contract is still valid or not'),
844         #'reminder' : fields.boolean('Renewal Reminder', help="Warn the user a few days before the expiration date of this contract"),
845         'notes' : fields.text('Terms and Conditions', help='Write here all supplementary informations relative to this contract'),
846         'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
847         'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer Value',store=False,help='Odometer measure of the vehicle at the moment of this log'),
848         'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
849     }
850     _defaults = {
851         'purchaser_id': lambda self, cr, uid, ctx: uid,
852         'date' : time.strftime('%Y-%m-%d'),
853         'start_date' : time.strftime('%Y-%m-%d'),
854         'state':'open',
855         #'expiration_date' : self.compute_next_year_date(time.strftime('%Y-%m-%d')),
856     
857     }
858
859     def copy(self, cr, uid, id, default=None, context=None):
860         default = default or {}
861         current_object = self.browse(cr,uid,id,context)
862         default['start_date'] = time.strftime('%Y-%m-%d')
863         default['expiration_date'] = self.compute_next_year_date(time.strftime('%Y-%m-%d'))
864         #default['name'] = current_object.name
865         default['ins_ref'] = ''
866         default['state'] = 'open'
867         default['notes'] = ''
868         default['date'] = time.strftime('%Y-%m-%d')
869
870         #default['odometer'] = current_object.odometer
871         #default['odometer_unit'] = current_object.odometer_unit
872         return super(fleet_vehicle_log_contract, self).copy(cr, uid, id, default, context=context)
873
874     def contract_close(self, cr, uid, ids, *args):
875         self.write(cr, uid, ids, {'state': 'closed'})
876         return True
877
878     def contract_open(self, cr, uid, ids, *args):
879         self.write(cr, uid, ids, {'state': 'open'})
880         return True
881
882
883 ############################
884 ############################
885 #Vehicle.log.contract.state class
886 ############################
887 ############################
888
889 class fleet_contract_state(osv.Model):
890     _name = 'fleet.contract.state'
891     _columns = {
892         'name':fields.char('Contract Status',size=32),
893     }