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