1 from itertools import chain
2 from osv import osv, fields
8 class fleet_vehicle_cost(osv.Model):
9 _name = 'fleet.vehicle.cost'
10 _description = 'Cost of vehicle'
12 'price': fields.float('Price'),
13 'type': fields.many2one('fleet.service.type', 'Service type', required=True, help='Service type purchased with this cost'),
14 'vehicle_id': fields.many2one('fleet.vehicle', 'Vehicle', required=True, help='Vehicle concerned by this cost'),
17 class fleet_vehicle_model_type(osv.Model):
18 _name = 'fleet.vehicle.type'
19 _description = 'Type of the vehicle'
21 'name' : fields.char('Name', size=32, required=True),
24 class fleet_vehicle_tag(osv.Model):
25 _name = 'fleet.vehicle.tag'
27 'name': fields.char('Name', required=True, translate=True),
30 class fleet_vehicle_state(osv.Model):
31 _name = 'fleet.vehicle.state'
33 'name': fields.char('Name', required=True),
34 'sequence': fields.integer('Order',help="Used to order the note stages")
36 _order = 'sequence asc'
38 class fleet_vehicle_model(osv.Model):
40 def name_get(self, cr, uid, ids, context=None):
45 reads = self.browse(cr, uid, ids, context=context)
48 name = record.modelname
50 name = record.brand.name+' / '+name
51 res.append((record.id, name))
54 def _model_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
55 res = self.name_get(cr, uid, ids, context=context)
58 def on_change_brand(self, cr, uid, ids, model_id, context=None):
63 brand = self.pool.get('fleet.vehicle.model.brand').browse(cr, uid, model_id, context=context)
67 'image' : brand.image,
71 _name = 'fleet.vehicle.model'
72 _description = 'Model of a vehicle'
75 'name' : fields.function(_model_name_get_fnc, type="char", string='Name', store=True),
76 'modelname' : fields.char('Model name', size=32, required=True),
77 'brand' : fields.many2one('fleet.vehicle.model.brand', 'Model Brand', required=True, help='Brand of the vehicle'),
78 'vendors': fields.many2many('res.partner','fleet_vehicle_model_vendors','model_id', 'partner_id',string='Vendors',required=False),
79 'image': fields.related('brand','image',type="binary",string="Logo",store=False),
80 'image_medium': fields.related('brand','image_medium',type="binary",string="Logo",store=False),
81 'image_small': fields.related('brand','image_small',type="binary",string="Logo",store=False),
84 class fleet_vehicle_model_brand(osv.Model):
85 _name = 'fleet.vehicle.model.brand'
86 _description = 'Brand model of the vehicle'
90 def _get_image(self, cr, uid, ids, name, args, context=None):
91 result = dict.fromkeys(ids, False)
92 for obj in self.browse(cr, uid, ids, context=context):
93 result[obj.id] = tools.image_get_resized_images(obj.image)
96 def _set_image(self, cr, uid, id, name, value, args, context=None):
97 return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
100 'name' : fields.char('Brand Name',size=32, required=True),
102 'image': fields.binary("Logo",
103 help="This field holds the image used as logo for the brand, limited to 1024x1024px."),
104 'image_medium': fields.function(_get_image, fnct_inv=_set_image,
105 string="Medium-sized photo", type="binary", multi="_get_image",
107 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
109 help="Medium-sized logo of the brand. It is automatically "\
110 "resized as a 128x128px image, with aspect ratio preserved. "\
111 "Use this field in form views or some kanban views."),
112 'image_small': fields.function(_get_image, fnct_inv=_set_image,
113 string="Smal-sized photo", type="binary", multi="_get_image",
115 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
117 help="Small-sized photo of the brand. It is automatically "\
118 "resized as a 64x64px image, with aspect ratio preserved. "\
119 "Use this field anywhere a small image is required."),
122 class fleet_vehicle(osv.Model):
124 _inherit = 'mail.thread'
126 def name_get(self, cr, uid, ids, context=None):
131 reads = self.browse(cr, uid, ids, context=context)
134 if record.license_plate:
135 name = record.license_plate
136 if record.model_id.modelname:
137 name = record.model_id.modelname + ' / ' + name
138 if record.model_id.brand.name:
139 name = record.model_id.brand.name+' / '+ name
140 res.append((record.id, name))
143 def _vehicle_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
144 res = self.name_get(cr, uid, ids, context=context)
147 def act_show_log_services(self, cr, uid, ids, context=None):
148 """ This opens log view to view and add new log for this vehicle
149 @return: the service log view
151 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_services', context)
153 'default_vehicle_id': ids[0]
155 res['domain']=[('vehicle_id','=', ids[0])]
158 def act_show_log_contract(self, cr, uid, ids, context=None):
159 """ This opens log view to view and add new log for this vehicle
160 @return: the contract log view
162 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_contract', context)
164 'default_vehicle_id': ids[0]
166 res['domain']=[('vehicle_id','=', ids[0])]
169 def act_show_log_fuel(self, cr, uid, ids, context=None):
170 """ This opens log view to view and add new log for this vehicle
171 @return: the fuel log view
173 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_show_log_fuel', context)
175 'default_vehicle_id': ids[0]
177 res['domain']=[('vehicle_id','=', ids[0])]
180 def get_odometer(self, cr, uid, ids, context=None):
185 reads = self.browse(cr, uid, ids, context=context)
188 odometers = self.pool.get('fleet.vehicle.odometer').search(cr,uid,[('vehicle_id','=',record.id)], order='value desc')
189 if len(odometers) > 0:
190 res.append((record.id,self.pool.get('fleet.vehicle.odometer').browse(cr, uid, odometers[0], context=context).value))
192 res.append((record.id,0))
195 def _vehicle_odometer_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
196 res = self.get_odometer(cr, uid, ids, context=context)
199 def str_to_date(self,strdate):
200 return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
202 def get_overdue_contract_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
207 reads = self.browse(cr,uid,ids,context=context)
210 contracts = self.pool.get('fleet.vehicle.log.contract').search(cr,uid,[('vehicle_id','=',record.id),('state','=','In Progress')],order='expiration_date')
212 if (len(contracts) > 0):
213 for element in contracts:
214 current_date_str=time.strftime('%Y-%m-%d')
215 due_time_str=self.pool.get('fleet.vehicle.log.contract').browse(cr,uid,element,context=context).expiration_date
217 current_date=self.str_to_date(current_date_str)
218 due_time=self.str_to_date(due_time_str)
220 diff_time=int((due_time-current_date).days)
222 overdue = overdue +1;
225 res.append((record.id,overdue))
227 res.append((record.id,0))
230 def get_next_contract_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
235 reads = self.browse(cr,uid,ids,context=context)
238 contracts = self.pool.get('fleet.vehicle.log.contract').search(cr,uid,[('vehicle_id','=',record.id),('state','=','In Progress')],order='expiration_date')
240 if (len(contracts) > 0):
241 for element in contracts:
242 current_date_str=time.strftime('%Y-%m-%d')
243 due_time_str=self.pool.get('fleet.vehicle.log.contract').browse(cr,uid,element,context=context).expiration_date
245 current_date=self.str_to_date(current_date_str)
246 due_time=self.str_to_date(due_time_str)
248 diff_time=int((due_time-current_date).days)
249 if diff_time<15 and diff_time>=0:
250 due_soon = due_soon +1;
254 res.append((record.id,due_soon))
256 res.append((record.id,0))
259 def get_next_service_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
264 reads = self.browse(cr,uid,ids,context=context)
267 services = self.pool.get('fleet.vehicle.log.services').search(cr,uid,[('vehicle_id','=',record.id)],order='date')
268 if (len(services) > 0):
269 res.append((record.id,self.pool.get('fleet.vehicle.log.services').browse(cr,uid,services[0],context=context).date))
271 res.append((record.id,None))
274 _name = 'fleet.vehicle'
275 _description = 'Fleet Vehicle'
276 #_order = 'contract_renewal_overdue desc, contract_renewal_due_soon desc'
279 'name' : fields.function(_vehicle_name_get_fnc, type="char", string='Name', store=True),
280 'company_id': fields.many2one('res.company', 'Company'),
281 'license_plate' : fields.char('License Plate', size=32, required=True, help='License plate number of the vehicle (ie: plate number for a car)'),
282 'vin_sn' : fields.char('Chassis Number', size=32, required=False, help='Unique number written on the vehicle motor (VIN/SN number)'),
283 'driver' : fields.many2one('res.partner', 'Driver',required=False, help='Driver of the vehicle', domain="['|',('customer','=',True),('employee','=',True)]"),
284 'model_id' : fields.many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle'),
285 'log_ids' : fields.one2many('fleet.vehicle.log', 'vehicle_id', 'Other Logs'),
286 'log_fuel' : fields.one2many('fleet.vehicle.log.fuel','vehicle_id', 'Fuel Logs'),
287 'log_services' : fields.one2many('fleet.vehicle.log.services','vehicle_id', 'Services Logs'),
288 'log_contracts' : fields.one2many('fleet.vehicle.log.contract','vehicle_id', 'Contracts'),
289 'acquisition_date' : fields.date('Acquisition Date', required=False, help='Date when the vehicle has been bought'),
290 'color' : fields.char('Color',size=32, help='Color of the vehicle'),
291 'state': fields.many2one('fleet.vehicle.state', 'State', help='Current state of the vehicle', ),
292 'location' : fields.char('Location',size=32, help='Location of the vehicle (garage, ...)'),
293 'doors' : fields.integer('Doors Number', help='Number of doors of the vehicle'),
294 'tag_ids' :fields.many2many('fleet.vehicle.tag','fleet_vehicle_vehicle_tag_rel','vehicle_tag_id','tag_id','Tags'),
296 'odometer' : fields.function(_vehicle_odometer_get_fnc, type="float", string='Odometer', store=False),
297 'odometer_unit': fields.selection([('kilometers', 'Kilometers'),('miles','Miles')], 'Odometer Unit', help='Unit of the odometer ',required=False),
299 'transmission' : fields.selection([('manual', 'Manual'),('automatic','Automatic')], 'Transmission', help='Transmission Used by the vehicle',required=False),
300 'fuel_type' : fields.selection([('gasoline', 'Gasoline'),('diesel','Diesel'),('electric','Electric'),('hybrid','Hybrid')], 'Fuel Type', help='Fuel Used by the vehicle',required=False),
301 'horsepower' : fields.integer('Horsepower',required=False),
302 'horsepower_tax': fields.float('Horsepower Taxation'),
303 'power' : fields.integer('Power (kW)',required=False,help='Power in kW of the vehicle'),
304 'co2' : fields.float('CO2 Emissions',required=False,help='CO2 emissions of the vehicle'),
306 'image': fields.related('model_id','image',type="binary",string="Logo",store=False),
307 'image_medium': fields.related('model_id','image_medium',type="binary",string="Logo",store=False),
308 'image_small': fields.related('model_id','image_small',type="binary",string="Logo",store=False),
310 'contract_renewal_due_soon' : fields.function(get_next_contract_reminder,type="integer",string='Contract Renewal Due Soon',store=False),
311 'contract_renewal_overdue' : fields.function(get_overdue_contract_reminder,type="integer",string='Contract Renewal Overdue',store=False),
312 'next_service_date' : fields.function(get_next_service_reminder,type="date",string='Next Service Due Date',store=False),
314 'car_value': fields.float('Car value', help='Value of the bought vehicle'),
315 'leasing_value': fields.float('Leasing value',help='Value of the leasing(Monthly, usually'),
320 'odometer_unit' : 'kilometers',
325 def on_change_model(self, cr, uid, ids, model_id, context=None):
330 model = self.pool.get('fleet.vehicle.model').browse(cr, uid, model_id, context=context)
334 'image' : model.image,
337 def create(self, cr, uid, data, context=None):
338 vehicle_id = super(fleet_vehicle, self).create(cr, uid, data, context=context)
340 vehicle = self.browse(cr, uid, vehicle_id, context=context)
341 self.message_post(cr, uid, [vehicle_id], body='Vehicle %s has been added to the fleet!' % (vehicle.license_plate), context=context)
343 pass # group deleted: do not push a message
346 def write(self, cr, uid, ids, vals, context=None):
349 value = self.pool.get('res.partner').browse(cr,uid,vals['driver'],context=context).name
350 changes.append('Driver: from \'' + self.browse(cr, uid, ids, context)[0].driver.name + '\' to \'' + value+'\'')
352 value = self.pool.get('fleet.vehicle.state').browse(cr,uid,vals['state'],context=context).name
353 changes.append('State: from \'' + self.browse(cr, uid, ids, context)[0].state.name + '\' to \'' + value+'\'')
354 if 'license_plate' in vals:
355 changes.append('License Plate: from \'' + self.browse(cr, uid, ids, context)[0].license_plate + '\' to \'' + vals['license_plate']+'\'')
357 vehicle_id = super(fleet_vehicle,self).write(cr, uid, ids, vals, context)
361 self.message_post(cr, uid, [self.browse(cr, uid, ids, context)[0].id], body=", ".join(changes), context=context)
362 except Exception as e:
367 class fleet_vehicle_odometer(osv.Model):
368 _name='fleet.vehicle.odometer'
369 _description='Odometer log for a vehicle'
373 def name_get(self, cr, uid, ids, context=None):
378 reads = self.browse(cr, uid, ids, context=context)
381 if record.vehicle_id.name:
382 name = str(record.vehicle_id.name)
384 name = name+ ' / '+ str(record.date)
385 res.append((record.id, name))
388 def _vehicle_log_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
389 res = self.name_get(cr, uid, ids, context=context)
392 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
397 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
401 'unit' : odometer_unit,
406 'name' : fields.function(_vehicle_log_name_get_fnc, type="char", string='Name', store=True),
408 'date' : fields.date('Purchase Date'),
409 'value' : fields.float('Odometer Value',group_operator="max"),
410 'unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
411 'vehicle_id' : fields.many2one('fleet.vehicle', 'Vehicle', required=True),
415 'date' : time.strftime('%Y-%m-%d')
418 class fleet_vehicle_log_fuel(osv.Model):
420 _inherits = {'fleet.vehicle.odometer': 'odometer_id'}
422 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
427 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
431 'unit' : odometer_unit,
435 def on_change_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
437 if liter > 0 and price_per_liter > 0:
438 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
439 elif liter > 0 and amount > 0:
440 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
441 elif price_per_liter > 0 and amount > 0:
442 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
446 def on_change_price_per_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
448 liter = float(liter);
449 price_per_liter = float(price_per_liter);
450 if price_per_liter > 0 and liter > 0:
451 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
452 elif price_per_liter > 0 and amount > 0:
453 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
454 elif liter > 0 and amount > 0:
455 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
459 def on_change_amount(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
461 if amount > 0 and liter > 0:
462 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
463 elif amount > 0 and price_per_liter > 0:
464 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
465 elif liter > 0 and price_per_liter > 0:
466 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
471 _name = 'fleet.vehicle.log.fuel'
474 #'name' : fields.char('Name',size=64),
476 'liter' : fields.float('Liter'),
477 'price_per_liter' : fields.float('Price Per Liter'),
478 'amount': fields.float('Total price'),
479 'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
480 'inv_ref' : fields.char('Invoice Reference', size=64),
481 'vendor_id' : fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
482 'notes' : fields.text('Notes'),
485 'purchaser_id': lambda self, cr, uid, ctx: uid,
488 class fleet_vehicle_log_services(osv.Model):
490 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
495 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
499 'unit' : odometer_unit,
503 _inherits = {'fleet.vehicle.odometer': 'odometer_id'}
505 _name = 'fleet.vehicle.log.services'
508 #'name' : fields.char('Name',size=64),
509 'date' :fields.date('Service Date',help='Date when the service will be/has been performed'),
510 'amount' :fields.float('Cost', help="Total cost of the service"),
511 'service_ids' :fields.many2many('fleet.service.type','fleet_vehicle_service_type_rel','vehicle_service_type_id','service_id','Services completed'),
512 'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
513 'inv_ref' : fields.char('Invoice Reference', size=64),
514 'vendor_id' :fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
515 'notes' : fields.text('Notes'),
518 'purchaser_id': lambda self, cr, uid, ctx: uid,
519 'date' : time.strftime('%Y-%m-%d'),
522 class fleet_contract_type(osv.Model):
523 _name = 'fleet.contract.type'
525 'name': fields.char('Name', required=True, translate=True),
528 class fleet_contract_state(osv.Model):
529 _name = 'fleet.contract.state'
531 'name':fields.char('Contract Status',size=32),
534 class fleet_vehicle_log_contract(osv.Model):
535 _inherits = {'fleet.vehicle.odometer': 'odometer_id'}
537 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
542 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
546 'unit' : odometer_unit,
550 def compute_next_year_date(self, strdate):
551 nextyear=int(strdate[:4])+1
552 return str(nextyear)+strdate[4:]
554 def on_change_start_date(self, cr, uid, ids, strdate, context=None):
557 return {'value' : {'expiration_date' : self.compute_next_year_date(strdate),}}
561 def str_to_date(self,strdate):
562 return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
564 def get_warning_date(self,cr,uid,ids,prop,unknow_none,context=None):
569 reads = self.browse(cr,uid,ids,context=context)
573 if (record.expiration_date and record.state.name=='In Progress'):
574 today=self.str_to_date(time.strftime('%Y-%m-%d'))
575 renew_date = self.str_to_date(record.expiration_date)
576 diff_time=int((renew_date-today).days)
577 res.append((record.id,diff_time))
579 res.append((record.id,0))
581 res.append((record.id,0))
584 _name = 'fleet.vehicle.log.contract'
585 _order='state,expiration_date'
588 #'name' : fields.char('Name',size=64),
590 'contract_type' : fields.many2one('fleet.contract.type', 'Type', required=False, help='Type of the contract'),
591 'start_date' : fields.date('Start Date', required=False, help='Date when the coverage of the contract begins'),
592 'expiration_date' : fields.date('Expiration Date', required=False, help='Date when the coverage of the contract expirates (by default, one year after begin date)'),
593 'warning_date' : fields.function(get_warning_date,type='integer',string='Warning Date',store=False),
594 'price' : fields.float('Price', help="Cost of the contract for the specified period"),
595 'insurer_id' :fields.many2one('res.partner', 'Insurer', domain="[('supplier','=',True)]"),
596 'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
597 'ins_ref' : fields.char('Contract Reference', size=64),
598 'state' : fields.many2one('fleet.contract.state', 'Contract Status', help='Choose wheter the contract is still valid or not'),
599 'notes' : fields.text('Terms and Conditions'),
600 'costs' : fields.one2many('fleet.vehicle.cost', 'vehicle_id', 'Costs covered'),
603 'purchaser_id': lambda self, cr, uid, ctx: uid,
604 'start_date' : time.strftime('%Y-%m-%d'),
605 #'expiration_date' : self.compute_next_year_date(time.strftime('%Y-%m-%d')),
609 class fleet_service_type(osv.Model):
610 _name = 'fleet.service.type'
612 'name': fields.char('Name', required=True, translate=True),