[MERGE] fleet: Improve views + introduce some new features

Purpose
=======

Move some internal development into the standard solution.

Specification
=============

- Add a default value for 'acquisition_date'
- Make the license plate not required
- Display all the stages on the fleet.vehicle kanban view
- Remove the unicity constraint on the driver
- Order cars by plate, then by acquisition date
- Add an informative 'First Contract Date' field
- Add 'Next Activities' on vehicles
- Create a related contract when creating a new car
- Archive related contracts when archiving a vehicle
- Allow to search vehicles based on the driver name
- Set default values on model onchange
- Add subtype to track driver modifications
- Introduce fleet.vehicle.assignation.log model
- Add track_visibility='onchange' on some fields
- Rename purchaser_id from 'Contractor' to 'Driver'
- Modify slightly some fleet views

TASK ID: 1839868
This commit is contained in:
Yannick Tivisse
2018-04-27 17:31:34 +02:00
8 changed files with 142 additions and 49 deletions
+16 -8
View File
@@ -1,10 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<odoo>
<data noupdate="1">
<record id="mail_act_fleet_contract_to_renew" model="mail.activity.type">
<field name="name">Contract to Renew</field>
<field name="icon">fa-car</field>
<field name="res_model_id" ref="fleet.model_fleet_vehicle_log_contract"/>
</record>
<record id="mail_act_fleet_contract_to_renew" model="mail.activity.type">
<field name="name">Contract to Renew</field>
<field name="icon">fa-car</field>
<field name="res_model_id" ref="fleet.model_fleet_vehicle_log_contract"/>
</record>
</data></odoo>
<record id="mt_fleet_driver_updated" model="mail.message.subtype">
<field name="name">Changed Driver</field>
<field name="sequence">0</field>
<field name="res_model">fleet.vehicle</field>
<field name="default" eval="True"/>
<field name="description">Changed Driver</field>
</record>
</data>
</odoo>
+74 -33
View File
@@ -4,13 +4,14 @@
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.osv import expression
class FleetVehicle(models.Model):
_inherit = 'mail.thread'
_inherit = ['mail.thread', 'mail.activity.mixin']
_name = 'fleet.vehicle'
_description = 'Information on a vehicle'
_order = 'license_plate asc'
_order = 'license_plate asc, acquisition_date asc'
def _get_default_state(self):
state = self.env.ref('fleet.vehicle_state_active', raise_if_not_found=False)
@@ -19,11 +20,12 @@ class FleetVehicle(models.Model):
name = fields.Char(compute="_compute_vehicle_name", store=True)
active = fields.Boolean('Active', default=True, track_visibility="onchange")
company_id = fields.Many2one('res.company', 'Company')
license_plate = fields.Char(required=True, track_visibility="onchange",
license_plate = fields.Char(track_visibility="onchange",
help='License plate number of the vehicle (i = plate number for a car)')
vin_sn = fields.Char('Chassis Number', help='Unique number written on the vehicle motor (VIN/SN number)', copy=False)
driver_id = fields.Many2one('res.partner', 'Driver', track_visibility="onchange", help='Driver of the vehicle', copy=False)
model_id = fields.Many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle')
model_id = fields.Many2one('fleet.vehicle.model', 'Model',
track_visibility="onchange", required=True, help='Model of the vehicle')
log_fuel = fields.One2many('fleet.vehicle.log.fuel', 'vehicle_id', 'Fuel Logs')
log_services = fields.One2many('fleet.vehicle.log.services', 'vehicle_id', 'Services Logs')
log_contracts = fields.One2many('fleet.vehicle.log.contract', 'vehicle_id', 'Contracts')
@@ -32,9 +34,13 @@ class FleetVehicle(models.Model):
service_count = fields.Integer(compute="_compute_count_all", string='Services')
fuel_logs_count = fields.Integer(compute="_compute_count_all", string='Fuel Log Count')
odometer_count = fields.Integer(compute="_compute_count_all", string='Odometer')
acquisition_date = fields.Date('Immatriculation Date', required=False, help='Date when the vehicle has been immatriculated')
acquisition_date = fields.Date('Immatriculation Date', required=False,
default=fields.Date.today, help='Date when the vehicle has been immatriculated')
first_contract_date = fields.Date(string="First Contract Date", default=fields.Date.today)
color = fields.Char(help='Color of the vehicle')
state_id = fields.Many2one('fleet.vehicle.state', 'State', default=_get_default_state,
state_id = fields.Many2one('fleet.vehicle.state', 'State',
default=_get_default_state, group_expand='_read_group_stage_ids',
track_visibility="onchange",
help='Current state of the vehicle', ondelete="set null")
location = fields.Char(help='Location of the vehicle (garage, ...)')
seats = fields.Integer('Seats Number', help='Number of seats of the vehicle')
@@ -71,10 +77,6 @@ class FleetVehicle(models.Model):
car_value = fields.Float(string="Catalog Value (VAT Incl.)", help='Value of the bought vehicle')
residual_value = fields.Float()
_sql_constraints = [
('driver_id_unique', 'UNIQUE(driver_id)', 'Only one car can be assigned to the same employee!')
]
@api.depends('model_id.brand_id.name', 'model_id.name', 'license_plate')
def _compute_vehicle_name(self):
for record in self:
@@ -193,34 +195,44 @@ class FleetVehicle(models.Model):
else:
self.image_medium = False
@api.model
def create(self, vals):
res = super(FleetVehicle, self).create(vals)
if 'driver_id' in vals and vals['driver_id']:
res.create_driver_history(vals['driver_id'])
return res
@api.multi
def write(self, vals):
"""
This function write an entry in the openchatter whenever we change important information
on the vehicle like the model, the drive, the state of the vehicle or its license plate
"""
res = super(FleetVehicle, self).write(vals)
if 'driver_id' in vals and vals['driver_id']:
self.create_driver_history(vals['driver_id'])
if 'active' in vals and not vals['active']:
self.mapped('log_contracts').write({'active': False})
return res
def create_driver_history(self, driver_id):
for vehicle in self:
changes = []
if 'model_id' in vals and vehicle.model_id.id != vals['model_id']:
value = self.env['fleet.vehicle.model'].browse(vals['model_id']).name
oldmodel = vehicle.model_id.name or _('None')
changes.append(_("Model: from '%s' to '%s'") % (oldmodel, value))
if 'driver_id' in vals and vehicle.driver_id.id != vals['driver_id']:
value = self.env['res.partner'].browse(vals['driver_id']).name
olddriver = (vehicle.driver_id.name) or _('None')
changes.append(_("Driver: from '%s' to '%s'") % (olddriver, value))
if 'state_id' in vals and vehicle.state_id.id != vals['state_id']:
value = self.env['fleet.vehicle.state'].browse(vals['state_id']).name
oldstate = vehicle.state_id.name or _('None')
changes.append(_("State: from '%s' to '%s'") % (oldstate, value))
if 'license_plate' in vals and vehicle.license_plate != vals['license_plate']:
old_license_plate = vehicle.license_plate or _('None')
changes.append(_("License Plate: from '%s' to '%s'") % (old_license_plate, vals['license_plate']))
self.env['fleet.vehicle.assignation.log'].create({
'vehicle_id': vehicle.id,
'driver_id': driver_id,
'date_start': fields.Date.today(),
})
if len(changes) > 0:
self.message_post(body=", ".join(changes))
@api.model
def _read_group_stage_ids(self, stages, domain, order):
return self.env['fleet.vehicle.state'].search([], order=order)
return super(FleetVehicle, self).write(vals)
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
domain = args or []
domain = expression.AND([domain, [('name', operator, name)]])
access_rights_uid = name_get_uid or self._uid
partners = self.env['res.partner']._search([('name', operator, name)], access_rights_uid=access_rights_uid)
if partners:
domain = expression.OR([domain, ['|', ('driver_id', 'in', partners.ids), ('driver_id', '=', False)]])
rec = self._search(domain, limit=limit, access_rights_uid=access_rights_uid)
return rec.name_get()
@api.multi
def return_action_to_open(self):
@@ -251,6 +263,24 @@ class FleetVehicle(models.Model):
)
return res
@api.multi
def _track_subtype(self, init_values):
self.ensure_one()
if 'driver_id' in init_values:
return 'fleet.mt_fleet_driver_updated'
return super(FleetVehicle, self)._track_subtype(init_values)
def open_assignation_logs(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': 'Assignation Logs',
'view_mode': 'tree',
'res_model': 'fleet.vehicle.assignation.log',
'domain': [('vehicle_id', '=', self.id)],
'context': {'default_driver_id': self.driver_id.id, 'default_vehicle_id': self.id}
}
class FleetVehicleOdometer(models.Model):
_name = 'fleet.vehicle.odometer'
@@ -308,3 +338,14 @@ class FleetServiceType(models.Model):
('contract', 'Contract'),
('service', 'Service')
], 'Category', required=True, help='Choose whether the service refer to contracts, vehicle services or both')
class FleetVehicleAssignationLog(models.Model):
_name = "fleet.vehicle.assignation.log"
_description = "Drivers history on a vehicle"
_order = "date_start"
vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", required=True)
driver_id = fields.Many2one('res.partner', string="Driver", required=True)
date_start = fields.Date(string="Start Date")
date_end = fields.Date(string="End Date")
+2 -2
View File
@@ -103,7 +103,7 @@ class FleetVehicleLogContract(models.Model):
help='Date when the coverage of the contract expirates (by default, one year after begin date)')
days_left = fields.Integer(compute='_compute_days_left', string='Warning Date')
insurer_id = fields.Many2one('res.partner', 'Vendor')
purchaser_id = fields.Many2one('res.partner', 'Contractor', default=lambda self: self.env.user.partner_id.id,
purchaser_id = fields.Many2one('res.partner', 'Driver', default=lambda self: self.env.user.partner_id.id,
help='Person to which the contract is signed for')
ins_ref = fields.Char('Contract Reference', size=64, copy=False)
state = fields.Selection([
@@ -117,7 +117,7 @@ class FleetVehicleLogContract(models.Model):
track_visibility="onchange",
copy=False)
notes = fields.Text('Terms and Conditions', help='Write here all supplementary information relative to this contract', copy=False)
cost_generated = fields.Float('Recurring Cost Amount',
cost_generated = fields.Float('Recurring Cost Amount', track_visibility="onchange",
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")
cost_frequency = fields.Selection([
@@ -20,3 +20,4 @@ fleet_vehicle_log_contract_access_right,fleet_vehicle_log_contract_access_right,
fleet_service_type_access_right,fleet_service_type_access_right,model_fleet_service_type,fleet_group_manager,1,1,1,1
access_fleet_vehicle_cost,access_fleet_vehicle_cost,model_fleet_vehicle_cost,fleet_group_manager,1,1,1,1
access_mail_activity_type_fleet_manager,mail.activity.type.fleet.manager,mail.model_mail_activity_type,fleet.fleet_group_manager,1,1,1,1
access_fleet_vehicle_assignation_log_fleet_group_user,fleet_vehicle_assignation_log fleet_group_user,fleet.model_fleet_vehicle_assignation_log,fleet.fleet_group_user,1,1,1,1
Can't render this file because it contains an unexpected character in line 6 and column 59.
@@ -174,7 +174,7 @@
<header>
<button name="contract_close" states="open,expired,future" type="object" class="oe_highlight" string="Close Contract" groups="fleet.fleet_group_manager"/>
<button name="contract_open" states="closed" type="object" class="oe_highlight" string="Set Contract In Progress" groups="fleet.fleet_group_manager"/>
<button class="oe_highlight" name="act_renew_contract" type="object" string="Renew Contract" help="Create a new contract automatically with all the same informations except for the date that will start at the end of current contract" groups="fleet.fleet_group_manager"/>
<button class="oe_highlight" name="act_renew_contract" type="object" string="Renew Contract" attrs="{'invisible': [('state', '=', 'open')]}" help="Create a new contract automatically with all the same informations except for the date that will start at the end of current contract" groups="fleet.fleet_group_manager"/>
<field name="state" widget="statusbar" />
</header>
<sheet>
@@ -279,7 +279,7 @@
<field name="vehicle_id" />
<field name="cost_subtype_id"/>
<field name="insurer_id" />
<field name="amount" string="Activation Cost"/>
<field name="purchaser_id"/>
<field name="cost_generated"/>
<field name="cost_frequency"/>
<field name="state" />
@@ -334,6 +334,10 @@
<field name="model">fleet.vehicle.log.contract</field>
<field name="arch" type="xml">
<search string="Vehicles Contracts">
<field name="vehicle_id" string="Vehicle" filter_domain="[('vehicle_id.name','ilike', self)]"/>
<field name="purchaser_id" string="Driver" filter_domain="[('purchaser_id','child_of', self)]"/>
<field name="insurer_id" string="Vendor" filter_domain="[('insurer_id','child_of', self)]"/>
<field name="cost_subtype_id" string="Service Type" filter_domain="[('cost_ids.cost_subtype_id', 'ilike', self)]"/>
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
<filter string="In Progress" name="open" domain="[('state', '=', 'open')]"/>
<filter string="Expired" name="expired" domain="[('state', '=', 'expired')]"/>
@@ -84,7 +84,6 @@
<menuitem name="Fleet" id="menu_root" sequence="115" groups="fleet_group_user" web_icon="fleet,static/description/icon.png"/>
<menuitem name="Configuration" parent="menu_root" id="fleet_configuration" sequence="100" groups="fleet_group_manager"/>
<menuitem action="fleet_vehicle_model_action" parent="fleet_configuration" id="fleet_vehicle_model_menu" sequence="1"/>
<record id='fleet_vehicle_model_brand_view_tree' model='ir.ui.view'>
<field name="name">fleet.vehicle.model.brand.tree</field>
+24 -2
View File
@@ -11,6 +11,7 @@
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="open_assignation_logs" string="Drivers History" type="object" class="oe_stat_button" icon="fa-history"/>
<button name="return_action_to_open"
type="object"
class="oe_stat_button"
@@ -83,6 +84,7 @@
<field name="odometer_unit"/>
</div>
<field name="acquisition_date" />
<field name="first_contract_date"/>
<field name="car_value" />
<field name="residual_value"/>
</group>
@@ -111,6 +113,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
</div>
</form>
@@ -128,8 +131,6 @@
<field name="vin_sn" />
<field name="acquisition_date" />
<field name="state_id"/>
<field name="odometer"/>
<field name="odometer_unit" />
<field name="contract_renewal_due_soon" invisible="1"/>
<field name="contract_renewal_overdue" invisible="1" />
<field name="contract_renewal_total" invisible="1"/>
@@ -177,6 +178,9 @@
<field name="contract_renewal_name" />
<field name="contract_renewal_total" />
<field name="contract_count" />
<field name="activity_ids"/>
<field name="activity_state"/>
<progressbar field="activity_state" colors='{"planned": "success", "today": "warning", "overdue": "danger"}'/>
<templates>
<t t-name="kanban-box">
@@ -214,6 +218,9 @@
</span>
</a>
</div>
<div class="o_kanban_inline_block">
<field name="activity_ids" widget="kanban_activity"/>
</div>
</div>
</t>
</templates>
@@ -227,6 +234,7 @@
<field name="res_model">fleet.vehicle</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="context">{'search_default_groupby_status': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new vehicle
@@ -238,6 +246,7 @@
<menuitem name="Vehicles" parent="menu_root" id="fleet_vehicles" sequence="2" groups="fleet_group_user"/>
<menuitem action="fleet_vehicle_action" parent="fleet_vehicles" id="fleet_vehicle_menu" groups="fleet_group_user"/>
<menuitem action="fleet_vehicle_model_action" parent="fleet_vehicles" id="fleet_vehicle_model_menu" sequence="11"/>
<record id='fleet_vehicle_odometer_view_form' model='ir.ui.view'>
<field name="name">fleet.vehicle.odometer.form</field>
@@ -436,4 +445,17 @@
<menuitem id="fleet_vehicle_tag_menu" parent="fleet_configuration" action="fleet_vehicle_tag_action" sequence="89"/>
<record id="fleet_vehicle_assignation_log_view_list" model="ir.ui.view">
<field name="name">fleet.vehicle.assignation.log.view.tree</field>
<field name="model">fleet.vehicle.assignation.log</field>
<field name="arch" type="xml">
<tree string="Assignation Logs" editable="bottom">
<field name="vehicle_id" invisible="1"/>
<field name="driver_id"/>
<field name="date_start"/>
<field name="date_end"/>
</tree>
</field>
</record>
</odoo>
@@ -66,6 +66,17 @@ class FleetVehicle(models.Model):
acquisition_date = vehicle._get_acquisition_date()
vehicle.name += u" \u2022 " + str(round(vehicle.total_depreciated_cost, 2)) + u" \u2022 " + acquisition_date
@api.model
def create(self, vals):
res = super(FleetVehicle, self).create(vals)
if not res.log_contracts:
self.env['fleet.vehicle.log.contract'].create({
'vehicle_id': res.id,
'recurring_cost_amount_depreciated': res.model_id.default_recurring_cost_amount_depreciated,
'purchaser_id': res.driver_id.id,
})
return res
def _get_acquisition_date(self):
self.ensure_one()
return babel.dates.format_date(
@@ -109,11 +120,18 @@ class FleetVehicle(models.Model):
atn = car_value * min(0.18, (0.055 + 0.001 * (co2 - reference))) * magic_coeff
return max(1280, atn) / 12.0
@api.onchange('model_id')
def _onchange_model_id(self):
self.car_value = self.model_id.default_car_value
self.co2 = self.model_id.default_co2
self.fuel_type = self.model_id.default_fuel_type
class FleetVehicleLogContract(models.Model):
_inherit = 'fleet.vehicle.log.contract'
recurring_cost_amount_depreciated = fields.Float("Recurring Cost Amount (depreciated)")
recurring_cost_amount_depreciated = fields.Float("Recurring Cost Amount (depreciated)", track_visibility="onchange")
class FleetVehicleModel(models.Model):
_inherit = 'fleet.vehicle.model'