[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:
@@ -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>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user