1 # -*- coding: utf-8 -*-
2 from itertools import chain
3 from osv import osv, fields
7 from osv.orm import except_orm
8 from tools.translate import _
9 from dateutil.relativedelta import relativedelta
10 ############################
11 ############################
13 ############################
14 ############################
16 class fleet_vehicle_cost(osv.Model):
17 _name = 'fleet.vehicle.cost'
18 _description = 'Cost of vehicle'
19 _order = 'date desc, vehicle_id asc'
21 def name_get(self, cr, uid, ids, context=None):
26 reads = self.browse(cr, uid, ids, context=context)
29 if record.vehicle_id.license_plate:
30 name = record.vehicle_id.license_plate
31 if record.cost_subtype.name:
32 name = name + ' / '+ record.cost_subtype.name
34 name = name + ' / '+ record.date
35 res.append((record.id, name))
38 def _cost_name_get_fnc(self, cr, uid, ids, name, unknow_none, context=None):
39 res = self.name_get(cr, uid, ids, context=context)
43 'name' : fields.function(_cost_name_get_fnc, type="char", string='Name', store=True),
44 #'name' : fields.char('Name',size=32),
45 'vehicle_id': fields.many2one('fleet.vehicle', 'Vehicle', required=True, help='Vehicle concerned by this fuel log'),
46 'cost_subtype': fields.many2one('fleet.service.type', 'Cost type', required=False, help='Cost type purchased with this cost'),
47 'amount': fields.float('Total Price'),
48 'cost_type' : fields.selection([('contract', 'Contract'),('services','Services'),('fuel','Fuel'),('other','Other')], 'Category of the cost', help='For internal purpose only',required=True),
49 'parent_id': fields.many2one('fleet.vehicle.cost', 'Parent', required=False, help='Parent cost to this current cost'),
50 'cost_ids' : fields.one2many('fleet.vehicle.cost', 'parent_id', 'Included Services'),
52 'date' :fields.date('Date',help='Date when the cost has been executed'),
54 'contract_id' : fields.many2one('fleet.vehicle.log.contract', 'Contract', required=False, help='Contract attached to this cost'),
55 'auto_generated' : fields.boolean('automatically generated',readonly=True,required=True)
60 'auto_generated' : False,
61 'cost_type' : 'other',
66 def create(self, cr, uid, data, context=None):
67 if 'parent_id' in data and data['parent_id']:
68 parent = self.browse(cr, uid, data['parent_id'], context=context)
69 data['vehicle_id'] = parent.vehicle_id.id
70 data['date'] = parent.date
71 data['cost_type'] = parent.cost_type
72 if 'contract_id' in data and data['contract_id']:
73 contract = self.pool.get('fleet.vehicle.log.contract').browse(cr, uid, data['contract_id'], context=context)
74 data['vehicle_id'] = contract.vehicle_id.id
75 data['cost_subtype'] = contract.cost_subtype.id
76 data['cost_type'] = contract.cost_type
77 if not('cost_type' in data and data['cost_type']):
78 data['cost_type'] = 'other'
79 cost_id = super(fleet_vehicle_cost, self).create(cr, uid, data, context=context)
82 ############################
83 ############################
85 ############################
86 ############################
88 class fleet_vehicle_tag(osv.Model):
89 _name = 'fleet.vehicle.tag'
91 'name': fields.char('Name', required=True, translate=True),
94 ############################
95 ############################
97 ############################
98 ############################
100 class fleet_vehicle_state(osv.Model):
101 _name = 'fleet.vehicle.state'
103 'name': fields.char('Name', required=True),
104 'sequence': fields.integer('Order',help="Used to order the note stages")
106 _order = 'sequence asc'
107 _sql_constraints = [('fleet_state_name_unique','unique(name)','State name already exists')]
110 ############################
111 ############################
113 ############################
114 ############################
116 class fleet_vehicle_model(osv.Model):
118 def name_get(self, cr, uid, ids, context=None):
123 reads = self.browse(cr, uid, ids, context=context)
126 name = record.modelname
127 if record.brand.name:
128 name = record.brand.name+' / '+name
129 res.append((record.id, name))
132 def _model_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
133 res = self.name_get(cr, uid, ids, context=context)
136 def on_change_brand(self, cr, uid, ids, model_id, context=None):
141 brand = self.pool.get('fleet.vehicle.model.brand').browse(cr, uid, model_id, context=context)
145 'image' : brand.image,
149 _name = 'fleet.vehicle.model'
150 _description = 'Model of a vehicle'
153 'name' : fields.function(_model_name_get_fnc, type="char", string='Name', store=True),
154 'modelname' : fields.char('Model name', size=32, required=True),
155 'brand' : fields.many2one('fleet.vehicle.model.brand', 'Model Brand', required=True, help='Brand of the vehicle'),
156 'vendors': fields.many2many('res.partner','fleet_vehicle_model_vendors','model_id', 'partner_id',string='Vendors',required=False),
157 'image': fields.related('brand','image',type="binary",string="Logo",store=False),
158 'image_medium': fields.related('brand','image_medium',type="binary",string="Logo",store=False),
159 'image_small': fields.related('brand','image_small',type="binary",string="Logo",store=False),
162 ############################
163 ############################
165 ############################
166 ############################
168 class fleet_vehicle_model_brand(osv.Model):
169 _name = 'fleet.vehicle.model.brand'
170 _description = 'Brand model of the vehicle'
174 def _get_image(self, cr, uid, ids, name, args, context=None):
175 result = dict.fromkeys(ids, False)
176 for obj in self.browse(cr, uid, ids, context=context):
177 result[obj.id] = tools.image_get_resized_images(obj.image)
180 def _set_image(self, cr, uid, id, name, value, args, context=None):
181 return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
184 'name' : fields.char('Brand Name',size=32, required=True),
186 'image': fields.binary("Logo",
187 help="This field holds the image used as logo for the brand, limited to 1024x1024px."),
188 'image_medium': fields.function(_get_image, fnct_inv=_set_image,
189 string="Medium-sized photo", type="binary", multi="_get_image",
191 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
193 help="Medium-sized logo of the brand. It is automatically "\
194 "resized as a 128x128px image, with aspect ratio preserved. "\
195 "Use this field in form views or some kanban views."),
196 'image_small': fields.function(_get_image, fnct_inv=_set_image,
197 string="Smal-sized photo", type="binary", multi="_get_image",
199 'fleet.vehicle.model.brand': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
201 help="Small-sized photo of the brand. It is automatically "\
202 "resized as a 64x64px image, with aspect ratio preserved. "\
203 "Use this field anywhere a small image is required."),
206 ############################
207 ############################
209 ############################
210 ############################
213 class fleet_vehicle(osv.Model):
215 _inherit = 'mail.thread'
217 def name_get(self, cr, uid, ids, context=None):
222 reads = self.browse(cr, uid, ids, context=context)
225 if record.license_plate:
226 name = record.license_plate
227 if record.model_id.modelname:
228 name = record.model_id.modelname + ' / ' + name
229 if record.model_id.brand.name:
230 name = record.model_id.brand.name+' / '+ name
231 res.append((record.id, name))
234 def _vehicle_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
235 res = self.name_get(cr, uid, ids, context=context)
238 def act_show_log_services(self, cr, uid, ids, context=None):
239 """ This opens log view to view and add new log for this vehicle
240 @return: the service log view
242 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','fleet_vehicle_log_services_act', context)
244 'default_vehicle_id': ids[0]
246 res['domain']=[('vehicle_id','=', ids[0])]
249 def act_show_log_contract(self, cr, uid, ids, context=None):
250 """ This opens log view to view and add new log for this vehicle
251 @return: the contract log view
253 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','fleet_vehicle_log_contract_act', context)
255 'default_vehicle_id': ids[0]
257 res['domain']=[('vehicle_id','=', ids[0])]
260 def act_show_log_fuel(self, cr, uid, ids, context=None):
261 """ This opens log view to view and add new log for this vehicle
262 @return: the fuel log view
264 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','fleet_vehicle_log_fuel_act', context)
266 'default_vehicle_id': ids[0]
268 res['domain']=[('vehicle_id','=', ids[0])]
271 def act_show_log_cost(self, cr, uid, ids, context=None):
272 """ This opens log view to view and add new log for this vehicle
273 @return: the costs log view
275 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','fleet_vehicle_costs_act', context)
277 'default_vehicle_id': ids[0],
278 'search_default_parent_false' : True
280 res['domain']=[('vehicle_id','=', ids[0])]
283 def act_show_log_odometer(self, cr, uid, ids, context=None):
284 """ This opens log view to view and add new log for this vehicle
285 @return: the odometer log view
287 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','fleet_vehicle_odometer_act', context)
289 'default_vehicle_id': ids[0]
291 res['domain']=[('vehicle_id','=', ids[0])]
294 def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
295 res = dict.fromkeys(ids, False)
296 for record in self.browse(cr,uid,ids,context=context):
297 ids = self.pool.get('fleet.vehicle.odometer').search(cr,uid,[('vehicle_id','=',record.id)],limit=1, order='value desc')
299 res[record.id] = str(self.pool.get('fleet.vehicle.odometer').browse(cr,uid,ids[0],context=context).value)
301 res[record.id] = str(0)
304 def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
309 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
310 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
312 date = time.strftime('%Y-%m-%d')
313 data = {'value' : value,'date' : date,'vehicle_id' : id}
314 odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
316 self.write(cr, uid, id, {'odometer_id': ''})
319 def str_to_date(self,strdate):
320 return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
322 def get_overdue_contract_reminder_fnc(self,cr,uid,ids,context=None):
327 reads = self.browse(cr,uid,ids,context=context)
332 if (record.log_contracts):
333 for element in record.log_contracts:
334 if (element.state=='open' and element.expiration_date):
335 current_date_str=time.strftime('%Y-%m-%d')
336 due_time_str=element.expiration_date
337 #due_time_str=element.browse()
338 current_date=self.str_to_date(current_date_str)
339 due_time=self.str_to_date(due_time_str)
341 diff_time=int((due_time-current_date).days)
343 overdue = overdue +1;
344 res.append((record.id,overdue))
346 res.append((record.id,0))
350 def get_overdue_contract_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
351 res = self.get_overdue_contract_reminder_fnc(cr, uid, ids, context=context)
354 def get_next_contract_reminder_fnc(self,cr,uid,ids,context=None):
359 reads = self.browse(cr,uid,ids,context=context)
364 if (record.log_contracts):
365 for element in record.log_contracts:
366 if (element.state=='open' and element.expiration_date):
367 current_date_str=time.strftime('%Y-%m-%d')
368 due_time_str=element.expiration_date
369 #due_time_str=element.browse()
370 current_date=self.str_to_date(current_date_str)
371 due_time=self.str_to_date(due_time_str)
373 diff_time=int((due_time-current_date).days)
374 if diff_time<15 and diff_time>=0:
375 due_soon = due_soon +1;
376 res.append((record.id,due_soon))
378 res.append((record.id,0))
382 def _search_get_overdue_contract_reminder(self, cr, uid, obj, name, args, context):
384 for field, operator, value in args:
385 #assert field == name
386 vehicle_ids = self.search(cr, uid, [])
387 renew_ids = self.get_overdue_contract_reminder_fnc(cr,uid,vehicle_ids,context=context)
389 for renew_key,renew_value in renew_ids.items():
390 if eval(str(renew_value) + " " + str(operator) + " " + str(value)):
391 res_ids.append(renew_key)
392 res.append(('id', 'in', res_ids))
395 def _search_contract_renewal_due_soon(self, cr, uid, obj, name, args, context):
397 for field, operator, value in args:
398 #assert field == name
399 vehicle_ids = self.search(cr, uid, [])
400 renew_ids = self.get_next_contract_reminder_fnc(cr,uid,vehicle_ids,context=context)
402 for renew_key,renew_value in renew_ids.items():
403 if eval(str(renew_value) + " " + str(operator) + " " + str(value)):
404 res_ids.append(renew_key)
405 res.append(('id', 'in', res_ids))
408 def get_next_contract_reminder(self, cr, uid, ids, prop, unknow_none, context=None):
409 res = self.get_next_contract_reminder_fnc(cr, uid, ids, context=context)
412 def get_contract_renewal_names(self,cr,uid,ids,function_name,args,context=None):
415 reads = self.browse(cr,uid,ids,context=context)
418 if (record.log_contracts):
419 ids = self.pool.get('fleet.vehicle.log.contract').search(cr,uid,[('vehicle_id','=',record.id),('state','=','open')],limit=1,order='expiration_date asc')
421 res.append((record.id,self.pool.get('fleet.vehicle.log.contract').browse(cr,uid,ids[0],context=context).cost_subtype.name))
423 res.append((record.id,''))
426 def get_total_contract_reminder(self,cr,uid,ids,prop,unknow_none,context=None):
431 reads = self.browse(cr,uid,ids,context=context)
436 if (record.log_contracts):
437 for element in record.log_contracts:
438 if (element.state=='open' and element.expiration_date):
439 current_date_str=time.strftime('%Y-%m-%d')
440 due_time_str=element.expiration_date
442 current_date=self.str_to_date(current_date_str)
443 due_time=self.str_to_date(due_time_str)
445 diff_time=int((due_time-current_date).days)
447 due_soon = due_soon +1;
450 res.append((record.id,due_soon))
452 res.append((record.id,0))
456 def run_scheduler(self,cr,uid,context=None):
457 ids = self.pool.get('fleet.vehicle').search(cr, uid, [], offset=0, limit=None, order=None,context=None, count=False)
458 nexts = self.get_next_contract_reminder_fnc(cr,uid,ids,context=context)
459 overdues = self.get_overdue_contract_reminder_fnc(cr,uid,ids,context=context)
460 for key,value in nexts.items():
461 if value > 0 and overdues[key] > 0:
462 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)
464 self.message_post(cr, uid, [key], body=str(value) + ' contract(s) has to be renewed soon!', context=context)
465 elif overdues[key] > 0 :
466 self.message_post(cr, uid, [key], body=str(overdues[key]) + ' contract(s) is(are) overdued!', context=context)
469 _name = 'fleet.vehicle'
470 _description = 'Fleet Vehicle'
471 #_order = 'contract_renewal_overdue desc, contract_renewal_due_soon desc'
472 _order= 'license_plate asc'
474 'name' : fields.function(_vehicle_name_get_fnc, type="char", string='Name', store=True),
476 'company_id': fields.many2one('res.company', 'Company'),
477 'license_plate' : fields.char('License Plate', size=32, required=True, help='License plate number of the vehicle (ie: plate number for a car)'),
478 'vin_sn' : fields.char('Chassis Number', size=32, required=False, help='Unique number written on the vehicle motor (VIN/SN number)'),
479 'driver' : fields.many2one('res.partner', 'Driver',required=False, help='Driver of the vehicle'),
480 'model_id' : fields.many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle'),
481 'log_fuel' : fields.one2many('fleet.vehicle.log.fuel','vehicle_id', 'Fuel Logs'),
482 'log_services' : fields.one2many('fleet.vehicle.log.services','vehicle_id', 'Services Logs'),
483 'log_contracts' : fields.one2many('fleet.vehicle.log.contract','vehicle_id', 'Contracts'),
484 'acquisition_date' : fields.date('Acquisition Date', required=False, help='Date when the vehicle has been bought'),
485 'color' : fields.char('Color',size=32, help='Color of the vehicle'),
486 'state': fields.many2one('fleet.vehicle.state', 'State', help='Current state of the vehicle',ondelete="set null"),
487 'location' : fields.char('Location',size=32, help='Location of the vehicle (garage, ...)'),
488 'seats' : fields.integer('Seats Number', help='Number of seats of the vehicle'),
489 'doors' : fields.integer('Doors Number', help='Number of doors of the vehicle'),
490 'tag_ids' :fields.many2many('fleet.vehicle.tag','fleet_vehicle_vehicle_tag_rel','vehicle_tag_id','tag_id','Tags'),
492 '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'),
493 'odometer_unit': fields.selection([('kilometers', 'Kilometers'),('miles','Miles')], 'Odometer Unit', help='Unit of the odometer ',required=True),
495 'transmission' : fields.selection([('manual', 'Manual'),('automatic','Automatic')], 'Transmission', help='Transmission Used by the vehicle',required=False),
496 'fuel_type' : fields.selection([('gasoline', 'Gasoline'),('diesel','Diesel'),('electric','Electric'),('hybrid','Hybrid')], 'Fuel Type', help='Fuel Used by the vehicle',required=False),
497 'horsepower' : fields.integer('Horsepower',required=False),
498 'horsepower_tax': fields.float('Horsepower Taxation'),
499 'power' : fields.integer('Power (kW)',required=False,help='Power in kW of the vehicle'),
500 'co2' : fields.float('CO2 Emissions',required=False,help='CO2 emissions of the vehicle'),
502 'image': fields.related('model_id','image',type="binary",string="Logo",store=False),
503 'image_medium': fields.related('model_id','image_medium',type="binary",string="Logo",store=False),
504 'image_small': fields.related('model_id','image_small',type="binary",string="Logo",store=False),
506 'contract_renewal_due_soon' : fields.function(get_next_contract_reminder,fnct_search=_search_contract_renewal_due_soon,type="integer",string='Contracts to renew',store=False),
507 'contract_renewal_overdue' : fields.function(get_overdue_contract_reminder,fnct_search=_search_get_overdue_contract_reminder,type="integer",string='Contracts Overdued',store=False),
508 'contract_renewal_name' : fields.function(get_contract_renewal_names,type="text",string='Name of contract to renew soon',store=False),
509 'contract_renewal_total' : fields.function(get_total_contract_reminder,type="integer",string='Total of contracts due or overdue minus one',store=False),
511 'car_value': fields.float('Car Value', help='Value of the bought vehicle'),
512 #'leasing_value': fields.float('Leasing value',help='Value of the leasing(Monthly, usually'),
517 'odometer_unit' : 'kilometers',
518 'state' : lambda s,cr,uid,c:s.get_state(cr,uid,'Active',context=c),
521 def get_state(self,cr,uid,state_name,context=None):
522 states=self.pool.get('fleet.vehicle.state').search(cr,uid,[('name','=',state_name)],context=context,limit=1)
525 def copy(self, cr, uid, id, default=None, context=None):
530 # 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
538 return super(fleet_vehicle, self).copy(cr, uid, id, default, context=context)
540 def on_change_model(self, cr, uid, ids, model_id, context=None):
545 model = self.pool.get('fleet.vehicle.model').browse(cr, uid, model_id, context=context)
549 'image' : model.image,
552 def create(self, cr, uid, data, context=None):
553 vehicle_id = super(fleet_vehicle, self).create(cr, uid, data, context=context)
555 vehicle = self.browse(cr, uid, vehicle_id, context=context)
556 self.message_post(cr, uid, [vehicle_id], body='Vehicle %s has been added to the fleet!' % (vehicle.license_plate), context=context)
558 pass # group deleted: do not push a message
561 def write(self, cr, uid, ids, vals, context=None):
564 value = self.pool.get('res.partner').browse(cr,uid,vals['driver'],context=context).name
565 olddriver = self.browse(cr, uid, ids, context)[0].driver
567 olddriver = olddriver.name
570 changes.append('Driver: from \'' + olddriver + '\' to \'' + value+'\'')
572 value = self.pool.get('fleet.vehicle.state').browse(cr,uid,vals['state'],context=context).name
573 oldstate = self.browse(cr, uid, ids, context)[0].state
575 oldstate=oldstate.name
578 changes.append('State: from \'' + oldstate + '\' to \'' + value+'\'')
579 if 'license_plate' in vals:
580 old_license_plate = self.browse(cr, uid, ids, context)[0].license_plate
581 if not old_license_plate:
582 old_license_plate = 'None'
583 changes.append('License Plate: from \'' + old_license_plate + '\' to \'' + vals['license_plate']+'\'')
585 vehicle_id = super(fleet_vehicle,self).write(cr, uid, ids, vals, context)
589 self.message_post(cr, uid, [self.browse(cr, uid, ids, context)[0].id], body=", ".join(changes), context=context)
590 except Exception as e:
595 ############################
596 ############################
597 #Vehicle.odometer class
598 ############################
599 ############################
601 class fleet_vehicle_odometer(osv.Model):
602 _name='fleet.vehicle.odometer'
603 _description='Odometer log for a vehicle'
607 def name_get(self, cr, uid, ids, context=None):
612 reads = self.browse(cr, uid, ids, context=context)
615 if record.vehicle_id.name:
616 name = str(record.vehicle_id.name)
618 name = name+ ' / '+ str(record.date)
619 res.append((record.id, name))
622 def _vehicle_log_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
623 res = self.name_get(cr, uid, ids, context=context)
625 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
630 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
634 'unit' : odometer_unit,
639 'name' : fields.function(_vehicle_log_name_get_fnc, type="char", string='Name', store=True),
641 'date' : fields.date('Purchase Date'),
642 'value' : fields.float('Odometer Value',group_operator="max"),
643 'vehicle_id' : fields.many2one('fleet.vehicle', 'Vehicle', required=True),
644 'unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
648 'date' : time.strftime('%Y-%m-%d')
651 ############################
652 ############################
654 ############################
655 ############################
658 ############################
659 ############################
660 #Vehicle.log.fuel class
661 ############################
662 ############################
665 class fleet_vehicle_log_fuel(osv.Model):
667 #_inherits = {'fleet.vehicle.odometer': 'odometer_id'}
668 _inherits = {'fleet.vehicle.cost': 'cost_id'}
670 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
675 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
679 'odometer_unit' : odometer_unit,
683 def on_change_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
685 if liter > 0 and price_per_liter > 0:
686 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
687 elif liter > 0 and amount > 0:
688 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
689 elif price_per_liter > 0 and amount > 0:
690 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
694 def on_change_price_per_liter(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
696 liter = float(liter);
697 price_per_liter = float(price_per_liter);
698 if price_per_liter > 0 and liter > 0:
699 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
700 elif price_per_liter > 0 and amount > 0:
701 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
702 elif liter > 0 and amount > 0:
703 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
707 def on_change_amount(self, cr, uid, ids, liter, price_per_liter, amount, context=None):
709 if amount > 0 and liter > 0:
710 return {'value' : {'price_per_liter' : float(amount) / float(liter),}}
711 elif amount > 0 and price_per_liter > 0:
712 return {'value' : {'liter' : float(amount) / float(price_per_liter),}}
713 elif liter > 0 and price_per_liter > 0:
714 return {'value' : {'amount' : float(liter) * float(price_per_liter),}}
718 def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
719 res = dict.fromkeys(ids, False)
720 for record in self.browse(cr,uid,ids,context=context):
721 if record.odometer_id:
722 res[record.id] = record.odometer_id.value
725 def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
730 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
731 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
733 date = self.browse(cr, uid, id, context=context).date
735 date = time.strftime('%Y-%m-%d')
736 vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
737 data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
738 odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
739 self.write(cr, uid, id, {'odometer_id': odometer_id})
741 self.write(cr, uid, id, {'odometer_id': ''})
744 def _get_default_service_type(self, cr, uid, context):
745 model, model_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'fleet', 'type_service_refueling')
748 _name = 'fleet.vehicle.log.fuel'
751 #'name' : fields.char('Name',size=64),
752 'liter' : fields.float('Liter'),
753 'price_per_liter' : fields.float('Price Per Liter'),
754 'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
755 'inv_ref' : fields.char('Invoice Reference', size=64),
756 'vendor_id' : fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
757 'notes' : fields.text('Notes'),
758 'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
759 'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer',store=False),
760 'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
761 'cost_amount': fields.related('cost_id','amount',type="float",string="Amount",store=True, readonly=True),
764 'purchaser_id': lambda self, cr, uid, ctx: uid,
765 'date' : time.strftime('%Y-%m-%d'),
766 'cost_subtype': _get_default_service_type,
768 def create(self, cr, uid, data, context=None):
769 data['cost_type'] = 'fuel'
770 cost_id = super(fleet_vehicle_log_fuel, self).create(cr, uid, data, context=context)
773 ############################
774 ############################
775 #Vehicle.log.service class
776 ############################
777 ############################
780 class fleet_vehicle_log_services(osv.Model):
782 _inherits = {'fleet.vehicle.cost': 'cost_id'}
784 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
789 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
793 'odometer_unit' : odometer_unit,
797 def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
798 res = dict.fromkeys(ids, False)
799 for record in self.browse(cr,uid,ids,context=context):
800 if record.odometer_id:
801 res[record.id] = record.odometer_id.value
804 def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
809 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
810 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
812 date = self.browse(cr, uid, id, context=context).date
814 date = time.strftime('%Y-%m-%d')
815 vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
816 data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
817 odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
818 self.write(cr, uid, id, {'odometer_id': odometer_id})
820 self.write(cr, uid, id, {'odometer_id': ''})
823 def _get_default_service_type(self, cr, uid, context):
824 model, model_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'fleet', 'type_service_service_8')
827 _name = 'fleet.vehicle.log.services'
830 #'name' : fields.char('Name',size=64),
832 'purchaser_id' : fields.many2one('res.partner', 'Purchaser',domain="['|',('customer','=',True),('employee','=',True)]"),
833 'inv_ref' : fields.char('Invoice Reference', size=64),
834 'vendor_id' :fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
835 'notes' : fields.text('Notes'),
837 'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
838 'odometer' : fields.function(_get_odometer,fnct_inv=_set_odometer,type='char',string='Odometer Value',store=False),
839 'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
840 'cost_amount': fields.related('cost_id','amount',type="float",string="Amount",store=True, readonly=True),
843 'purchaser_id': lambda self, cr, uid, ctx: uid,
844 'date' : time.strftime('%Y-%m-%d'),
845 'cost_subtype' : _get_default_service_type
847 def create(self, cr, uid, data, context=None):
848 data['cost_type'] = 'services'
849 cost_id = super(fleet_vehicle_log_services, self).create(cr, uid, data, context=context)
852 ############################
853 ############################
854 #Vehicle.service.type class
855 ############################
856 ############################
858 class fleet_service_type(osv.Model):
859 _name = 'fleet.service.type'
861 'name': fields.char('Name', required=True, translate=True),
862 'category': fields.selection([('contract', 'Contract'), ('service', 'Service'),('both', 'Both')], 'Category',required=True, help='Choose wheter the service refer to contracts, vehicle services or both'),
868 ############################
869 ############################
870 #Vehicle.log.contract class
871 ############################
872 ############################
874 class fleet_vehicle_log_contract(osv.Model):
876 _inherits = {'fleet.vehicle.cost': 'cost_id'}
878 def run_scheduler(self,cr,uid,context=None):
880 d = datetime.date.today()
881 #d = datetime.datetime(2012, 12, 01)
883 contract_ids = self.pool.get('fleet.vehicle.log.contract').search(cr, uid, [('state','=','open')], offset=0, limit=None, order=None,context=None, count=False)
884 deltas = {'yearly' : relativedelta(years=+1),'monthly' : relativedelta(months=+1),'weekly' : relativedelta(weeks=+1),'daily' : relativedelta(days=+1)}
885 for contract in self.pool.get('fleet.vehicle.log.contract').browse(cr,uid,contract_ids,context=context):
886 if not contract.start_date or contract.cost_frequency == 'no':
888 if contract.generated_cost_ids != []:
889 last_cost_id = self.pool.get('fleet.vehicle.cost').search(cr, uid, ['&',('contract_id','=',contract.id),('auto_generated','=',True)], offset=0, limit=1, order='date desc',context=None, count=False)
890 last_cost_date = self.pool.get('fleet.vehicle.cost').browse(cr,uid,last_cost_id[0],context=None).date
894 last_cost_date = contract.start_date
895 startdate = datetime.datetime.strptime(last_cost_date,'%Y-%m-%d').date()
897 startdate += deltas.get(contract.cost_frequency)
898 while (startdate < d) & (startdate < datetime.datetime.strptime(contract.expiration_date,'%Y-%m-%d').date()):
899 data = {'amount' : contract.cost_generated,'date' : startdate.strftime('%Y-%m-%d'),'vehicle_id' : contract.vehicle_id.id,'cost_subtype' : contract.cost_subtype.id,'contract_id' : contract.id,'auto_generated' : True}
901 cost_id = self.pool.get('fleet.vehicle.cost').create(cr, uid, data, context=context)
902 startdate += deltas.get(contract.cost_frequency)
905 def name_get(self, cr, uid, ids, context=None):
910 reads = self.browse(cr, uid, ids, context=context)
913 if record.vehicle_id.name:
914 name = str(record.vehicle_id.name)
915 if record.cost_subtype.name:
916 name = name+ ' / '+ str(record.cost_subtype.name)
918 name = name+ ' / '+ record.date
919 res.append((record.id, name))
922 def _vehicle_contract_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
923 res = self.name_get(cr, uid, ids, context=context)
926 def _get_odometer(self, cr, uid, ids, odometer_id, arg, context):
927 res = dict.fromkeys(ids, False)
928 for record in self.browse(cr,uid,ids,context=context):
929 if record.odometer_id:
930 res[record.id] = record.odometer_id.value
933 def _set_odometer(self, cr, uid, id, name, value, args=None, context=None):
938 #_logger.exception(value+' is not a correct odometer value. Please, fill a float for this field')
939 raise except_orm(_('Error!'), value+' is not a correct odometer value. Please, fill a float for this field')
941 date = self.browse(cr, uid, id, context=context).date
943 date = time.strftime('%Y-%m-%d')
944 vehicle_id = self.browse(cr, uid, id, context=context).vehicle_id
945 data = {'value' : value,'date' : date,'vehicle_id' : vehicle_id.id}
946 odometer_id = self.pool.get('fleet.vehicle.odometer').create(cr, uid, data, context=context)
947 self.write(cr, uid, id, {'odometer_id': odometer_id})
949 self.write(cr, uid, id, {'odometer_id': ''})
952 def on_change_vehicle(self, cr, uid, ids, vehicle_id, context=None):
957 odometer_unit = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context).odometer_unit
961 'odometer_unit' : odometer_unit,
965 def compute_next_year_date(self, strdate):
966 oneyear=datetime.timedelta(days=365)
967 curdate = self.str_to_date(strdate)
968 nextyear=curdate+oneyear#int(strdate[:4])+1
969 return str(nextyear)#+strdate[4:]
971 def on_change_start_date(self, cr, uid, ids, strdate, enddate, context=None):
975 return {'value' : {'expiration_date' : self.compute_next_year_date(strdate),}}
979 def str_to_date(self,strdate):
980 return datetime.datetime(int(strdate[:4]),int(strdate[5:7]),int(strdate[8:]))
982 def get_warning_date(self,cr,uid,ids,prop,unknow_none,context=None):
987 reads = self.browse(cr,uid,ids,context=context)
990 #if (record.reminder==True):
991 if (record.expiration_date and record.state=='open'):
992 today=self.str_to_date(time.strftime('%Y-%m-%d'))
993 renew_date = self.str_to_date(record.expiration_date)
994 diff_time=int((renew_date-today).days)
996 res.append((record.id,0))
998 res.append((record.id,diff_time))
1000 res.append((record.id,-1))
1002 # res.append((record.id,-1))
1005 def act_renew_contract(self,cr,uid,ids,context=None):
1006 contracts = self.browse(cr,uid,ids,context=context)
1007 res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet','act_renew_contract', context)
1008 for element in contracts:
1010 temp.append(('default_vehicle_id',element.vehicle_id.id))
1011 temp.append(('default_cost_subtype',element.cost_subtype.id))
1012 temp.append(('default_amount',element.amount))
1013 temp.append(('default_odometer_id',element.odometer_id.id))
1014 temp.append(('default_odometer_unit',element.odometer_unit))
1015 temp.append(('default_insurer_id',element.insurer_id.id))
1017 for costs in element.cost_ids:
1018 cost_temp.append(costs.id)
1019 temp.append(('default_cost_ids',cost_temp))
1020 temp.append(('default_date',time.strftime('%Y-%m-%d')))
1021 temp.append(('default_start_date',str(self.str_to_date(element.expiration_date)+datetime.timedelta(days=1))))
1022 temp.append(('default_purchaser_id',element.purchaser_id.id))
1023 temp.append(('default_ins_ref',element.ins_ref))
1024 temp.append(('default_state','open'))
1025 temp.append(('default_notes',element.notes))
1026 temp.append(('default_cost_frequency',element.cost_frequency))
1028 for gen_cost in element.generated_cost_ids:
1029 generated_cost.append(gen_cost.id)
1030 temp.append(('default_generated_cost_ids',generated_cost))
1033 startdate = self.str_to_date(element.start_date)
1034 enddate = self.str_to_date(element.expiration_date)
1035 diffdate = (enddate-startdate)
1036 newenddate = enddate+diffdate
1037 temp.append(('default_expiration_date',str(newenddate)))
1039 res['context'] = dict(temp)
1045 _name = 'fleet.vehicle.log.contract'
1046 _order='state,expiration_date'
1048 'name' : fields.function(_vehicle_contract_name_get_fnc, type="text", string='Name', store=True),
1050 'start_date' : fields.date('Contract Start Date', required=False, help='Date when the coverage of the contract begins'),
1051 '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)'),
1052 'warning_date' : fields.function(get_warning_date,type='integer',string='Warning Date',store=False),
1054 'insurer_id' :fields.many2one('res.partner', 'Supplier', domain="[('supplier','=',True)]"),
1055 'purchaser_id' : fields.many2one('res.partner', 'Contractor',domain="['|',('customer','=',True),('employee','=',True)]",help='Person to which the contract is signed for'),
1056 'ins_ref' : fields.char('Contract Reference', size=64),
1057 'state' : fields.selection([('open', 'In Progress'), ('closed', 'Terminated')], 'Status', readonly=True, help='Choose wheter the contract is still valid or not'),
1058 #'reminder' : fields.boolean('Renewal Reminder', help="Warn the user a few days before the expiration date of this contract"),
1059 'notes' : fields.text('Terms and Conditions', help='Write here all supplementary informations relative to this contract'),
1060 'odometer_id' : fields.many2one('fleet.vehicle.odometer', 'Odometer', required=False, help='Odometer measure of the vehicle at the moment of this log'),
1061 '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'),
1062 'odometer_unit': fields.related('vehicle_id','odometer_unit',type="char",string="Unit",store=False, readonly=True),
1063 'cost_amount': fields.related('cost_id','amount',type="float",string="Amount",store=True, readonly=True),
1064 'cost_generated': fields.float('Recuring Costs Amount',help="Costs paid at regular intervals, depending on the cost frequency. If the cost frequency is set to unique, the cost will be logged at the start date"),
1065 'cost_frequency': fields.selection([('no','No'),('daily', 'Daily'),('weekly','Weekly'),('monthly','Monthly'),('yearly','Yearly')], 'Cost Frequency', help='Frequency of the costs',required=True),
1066 'generated_cost_ids' : fields.one2many('fleet.vehicle.cost', 'contract_id', 'Generated Costs',ondelete='cascade'),
1069 'purchaser_id': lambda self, cr, uid, ctx: uid,
1070 'date' : time.strftime('%Y-%m-%d'),
1071 'start_date' : time.strftime('%Y-%m-%d'),
1073 'expiration_date' : lambda self,cr,uid,ctx: self.compute_next_year_date(time.strftime('%Y-%m-%d')),
1077 def copy(self, cr, uid, id, default=None, context=None):
1078 default = default or {}
1079 current_object = self.browse(cr,uid,id,context)
1080 default['date'] = time.strftime('%Y-%m-%d')
1081 default['start_date'] = time.strftime('%Y-%m-%d')
1082 default['expiration_date'] = self.compute_next_year_date(time.strftime('%Y-%m-%d'))
1083 #default['name'] = current_object.name
1084 default['ins_ref'] = ''
1085 default['state'] = 'open'
1086 default['notes'] = ''
1087 default['date'] = time.strftime('%Y-%m-%d')
1089 #default['odometer'] = current_object.odometer
1090 #default['odometer_unit'] = current_object.odometer_unit
1091 return super(fleet_vehicle_log_contract, self).copy(cr, uid, id, default, context=context)
1093 def contract_close(self, cr, uid, ids, *args):
1094 self.write(cr, uid, ids, {'state': 'closed'})
1097 def contract_open(self, cr, uid, ids, *args):
1098 self.write(cr, uid, ids, {'state': 'open'})
1100 def create(self, cr, uid, data, context=None):
1101 data['cost_type'] = 'contract'
1102 cost_id = super(fleet_vehicle_log_contract, self).create(cr, uid, data, context=context)
1106 ############################
1107 ############################
1108 #Vehicle.log.contract.state class
1109 ############################
1110 ############################
1112 class fleet_contract_state(osv.Model):
1113 _name = 'fleet.contract.state'
1115 'name':fields.char('Contract Status',size=32),