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