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