Skip to content

Commit

Permalink
[IMP] fleet: model cleanup, dashboard
Browse files Browse the repository at this point in the history
Purpose
=======

It is currently difficult to know exactly what is used for costs
and services on the contracts.

Hence it make it difficult to evolve the fleet application
We should clean the models in order to have something that is clearer

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

Cleanup of models: removed Fuel and Cost. Contract and Services are no
longer inheriting Cost and are seperate models simplifying the use of
Fleet. A contract now has included services and they are no longer
generating cost.

closes odoo#34090

Taskid: 1931775
Related: odoo/enterprise#4572
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
  • Loading branch information
Kevin Baptiste authored and tivisse committed Nov 27, 2019
1 parent c5398bd commit 82df1df
Show file tree
Hide file tree
Showing 15 changed files with 479 additions and 1,417 deletions.
1 change: 1 addition & 0 deletions addons/fleet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import report
8 changes: 4 additions & 4 deletions addons/fleet/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
Vehicle, leasing, insurances, cost
==================================
With this module, Odoo helps you managing all your vehicles, the
contracts associated to those vehicle as well as services, fuel log
entries, costs and many other features necessary to the management
of your fleet of vehicle(s)
contracts associated to those vehicle as well as services, costs
and many other features necessary to the management of your fleet
of vehicle(s)
Main Features
-------------
* Add vehicles to your fleet
* Manage contracts for vehicles
* Reminder when a contract reach its expiration date
* Add services, fuel log entry, odometer values for all vehicles
* Add services, odometer values for all vehicles
* Show all costs associated to a vehicle or to a type of service
* Analysis graph for costs
""",
Expand Down
15 changes: 15 additions & 0 deletions addons/fleet/data/fleet_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,22 @@

<record id="fleet_vehicle_state_new_request" model="fleet.vehicle.state">
<field name="name">New Request</field>
<field name="sequence">4</field>
</record>

<record id="fleet_vehicle_state_to_order" model="fleet.vehicle.state">
<field name="name">To Order</field>
<field name="sequence">5</field>
</record>

<record id="fleet_vehicle_state_registered" model="fleet.vehicle.state">
<field name="name">Registered</field>
<field name="sequence">7</field>
</record>

<record id="fleet_vehicle_state_downgraded" model="fleet.vehicle.state">
<field name="name">Downgraded</field>
<field name="sequence">8</field>
</record>
</data>
</odoo>
679 changes: 73 additions & 606 deletions addons/fleet/data/fleet_demo.xml

Large diffs are not rendered by default.

81 changes: 50 additions & 31 deletions addons/fleet/models/fleet_vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def _get_default_state(self):
return state if state and state.id else False

name = fields.Char(compute="_compute_vehicle_name", store=True)
description = fields.Text("Vehicle Description")
active = fields.Boolean('Active', default=True, tracking=True)
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
Expand All @@ -28,16 +29,21 @@ def _get_default_state(self):
future_driver_id = fields.Many2one('res.partner', 'Future Driver', tracking=True, help='Next Driver of the vehicle', copy=False, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
model_id = fields.Many2one('fleet.vehicle.model', 'Model',
tracking=True, required=True, help='Model of the vehicle')
manager_id = fields.Many2one('res.users', related='model_id.manager_id')
manager_id = fields.Many2one('res.users', compute='_compute_manager_id', domain=lambda self: [('groups_id', 'in', self.env.ref('fleet.fleet_group_manager').id)], store=True, readonly=False)

@api.depends('model_id')
def _compute_manager_id(self):
if self.model_id:
self.manager_id = self.model_id.manager_id
else:
self.manager_id = None

brand_id = fields.Many2one('fleet.vehicle.model.brand', 'Brand', related="model_id.brand_id", store=True, readonly=False)
log_drivers = fields.One2many('fleet.vehicle.assignation.log', 'vehicle_id', string='Assignation Logs')
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')
cost_count = fields.Integer(compute="_compute_count_all", string="Costs")
contract_count = fields.Integer(compute="_compute_count_all", string='Contract Count')
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')
history_count = fields.Integer(compute="_compute_count_all", string="Drivers History Count")
next_assignation_date = fields.Date('Assignation Date', help='This is the date at which the car will be available, if not set it means available instantly')
Expand Down Expand Up @@ -108,16 +114,12 @@ def _set_odometer(self):

def _compute_count_all(self):
Odometer = self.env['fleet.vehicle.odometer']
LogFuel = self.env['fleet.vehicle.log.fuel']
LogService = self.env['fleet.vehicle.log.services']
LogContract = self.env['fleet.vehicle.log.contract']
Cost = self.env['fleet.vehicle.cost']
for record in self:
record.odometer_count = Odometer.search_count([('vehicle_id', '=', record.id)])
record.fuel_logs_count = LogFuel.search_count([('vehicle_id', '=', record.id)])
record.service_count = LogService.search_count([('vehicle_id', '=', record.id)])
record.contract_count = LogContract.search_count([('vehicle_id', '=', record.id), ('state', '!=', 'closed')])
record.cost_count = Cost.search_count([('vehicle_id', '=', record.id), ('parent_id', '=', False)])
record.history_count = self.env['fleet.vehicle.assignation.log'].search_count([('vehicle_id', '=', record.id)])

@api.depends('log_contracts')
Expand All @@ -130,7 +132,7 @@ def _compute_contract_reminder(self):
total = 0
name = ''
for element in record.log_contracts:
if element.state in ('open', 'diesoon', 'expired') and element.expiration_date:
if element.state in ('open', 'expired') and element.expiration_date:
current_date_str = fields.Date.context_today(record)
due_time_str = element.expiration_date
current_date = fields.Date.from_string(current_date_str)
Expand All @@ -145,11 +147,11 @@ def _compute_contract_reminder(self):
if overdue or due_soon:
log_contract = self.env['fleet.vehicle.log.contract'].search([
('vehicle_id', '=', record.id),
('state', 'in', ('open', 'diesoon', 'expired'))
('state', 'in', ('open', 'expired'))
], limit=1, order='expiration_date asc')
if log_contract:
# we display only the name of the oldest overdue/due soon contract
name = log_contract.cost_subtype_id.name
name = log_contract.name

record.contract_renewal_overdue = overdue
record.contract_renewal_due_soon = due_soon
Expand All @@ -168,16 +170,11 @@ def _search_contract_renewal_due_soon(self, operator, value):
today = fields.Date.context_today(self)
datetime_today = fields.Datetime.from_string(today)
limit_date = fields.Datetime.to_string(datetime_today + relativedelta(days=+delay_alert_contract))
self.env.cr.execute("""SELECT cost.vehicle_id,
count(contract.id) AS contract_number
FROM fleet_vehicle_cost cost
LEFT JOIN fleet_vehicle_log_contract contract ON contract.cost_id = cost.id
WHERE contract.expiration_date IS NOT NULL
AND contract.expiration_date > %s
AND contract.expiration_date < %s
AND contract.state IN ('open', 'diesoon', 'expired')
GROUP BY cost.vehicle_id""", (today, limit_date))
res_ids = [x[0] for x in self.env.cr.fetchall()]
res_ids = self.env['fleet.vehicle.log.contract'].search([
('expiration_date', '>', today),
('expiration_date', '<', limit_date),
('state', 'in', ['open', 'expired'])
]).mapped('id')
res.append(('id', search_operator, res_ids))
return res

Expand All @@ -189,26 +186,26 @@ def _search_get_overdue_contract_reminder(self, operator, value):
else:
search_operator = 'not in'
today = fields.Date.context_today(self)
self.env.cr.execute('''SELECT cost.vehicle_id,
count(contract.id) AS contract_number
FROM fleet_vehicle_cost cost
LEFT JOIN fleet_vehicle_log_contract contract ON contract.cost_id = cost.id
WHERE contract.expiration_date IS NOT NULL
AND contract.expiration_date < %s
AND contract.state IN ('open', 'diesoon', 'expired')
GROUP BY cost.vehicle_id ''', (today,))
res_ids = [x[0] for x in self.env.cr.fetchall()]
res_ids = self.env['fleet.vehicle.log.contract'].search([
('expiration_date', '!=', False),
('expiration_date', '<', today),
('state', 'in', ['open', 'expired'])
]).mapped('id')
res.append(('id', search_operator, res_ids))
return res

@api.model
def create(self, vals):
res = super(FleetVehicle, self).create(vals)
res = super(FleetVehicle, self.with_context({'odometer_no_chatter': True})).create(vals)
if 'driver_id' in vals and vals['driver_id']:
res.create_driver_history(vals['driver_id'])
if 'future_driver_id' in vals and vals['future_driver_id']:
future_driver = self.env['res.partner'].browse(vals['future_driver_id'])
future_driver.write({'plan_to_change_car': True})
for rec in res:
if rec.odometer:
date = fields.Date.context_today(rec)
rec.message_post(body=_("Odometer on %s: %s %s") % (date, rec.odometer, rec.odometer_unit))
return res

def write(self, vals):
Expand Down Expand Up @@ -256,6 +253,12 @@ def action_accept_driver_change(self):
def _read_group_stage_ids(self, stages, domain, order):
return self.env['fleet.vehicle.state'].search([], order=order)

@api.model
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
if 'co2' in fields:
fields.remove('co2')
return super(FleetVehicle, self).read_group(domain, fields, groupby, offset, limit, orderby, lazy)

@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
args = args or []
Expand Down Expand Up @@ -337,6 +340,22 @@ def _onchange_vehicle(self):
if self.vehicle_id:
self.unit = self.vehicle_id.odometer_unit

@api.model
def create(self, vals):
odometers = super(FleetVehicleOdometer, self).create(vals)

for odometer in odometers:
if not self.env.context.get('odometer_no_chatter'):
odometer.vehicle_id.message_post(body=_("Odometer on %s: %s %s") % (odometer.date, odometer.value, odometer.unit))

return odometers

def write(self, vals):
odometers = super(FleetVehicleOdometer, self).write(vals)
if 'value' in vals and not self.env.context.get('odometer_no_chatter'):
for odometer in self:
odometer.vehicle_id.message_post(body=_("Odometer on %s: %s %s") % (odometer.date, odometer.value, odometer.unit))
return odometers

class FleetVehicleState(models.Model):
_name = 'fleet.vehicle.state'
Expand Down
Loading

0 comments on commit 82df1df

Please sign in to comment.