[REV] fleet: Revert multiple fleet mechanism
I don't get why you don't get it... Partial revert of the taskID: 2415309 Com PR: https://github.com/odoo/odoo/pull/70313 Ent PR: https://github.com/odoo/enterprise/pull/18114 Upg PR: https://github.com/odoo/upgrade/pull/2447 closes odoo/odoo#75475 Related: odoo/enterprise#20406 Related: odoo/upgrade#2765 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -35,8 +35,6 @@ Main Features
|
||||
'views/fleet_vehicle_views.xml',
|
||||
'views/fleet_vehicle_cost_views.xml',
|
||||
'views/fleet_board_view.xml',
|
||||
'views/fleet_category_views.xml',
|
||||
'views/fleet_category_tag_views.xml',
|
||||
'views/mail_activity_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'data/fleet_cars_data.xml',
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="fleet_category_tag_internal" model="fleet.category.tag">
|
||||
<field name="name">Internal</field>
|
||||
<field name="color" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_tag_external" model="fleet.category.tag">
|
||||
<field name="name">External</field>
|
||||
<field name="color" eval="2"/>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_internal" model="fleet.category">
|
||||
<field name="name">My Fleet</field>
|
||||
<field name="description">My Fleet</field>
|
||||
<field name="tag_ids" eval="[(4, ref('fleet.fleet_category_tag_internal'))]"/>
|
||||
<field name="manager_id" ref="base.user_admin"/>
|
||||
<field name="favorite_user_ids" eval="[(4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<record forcecreate="True" id="ir_cron_contract_costs_generator" model="ir.cron">
|
||||
<field name="name">Fleet: Generate contracts costs based on costs frequency</field>
|
||||
<field name="model_id" ref="model_fleet_vehicle_log_contract"/>
|
||||
|
||||
@@ -444,7 +444,6 @@
|
||||
<field name="location">Grand-Rosiere</field>
|
||||
<field name="doors">5</field>
|
||||
<field name="driver_id" ref="base.partner_demo" />
|
||||
<field name="fleet_id" ref="fleet_category_internal"/>
|
||||
<field name="acquisition_date" eval="(DateTime.now() - timedelta(days=336)).strftime('%Y-%m-%d')" />
|
||||
<field name="state_id" ref="fleet_vehicle_state_registered"/>
|
||||
<field name="odometer_unit">kilometers</field>
|
||||
@@ -460,7 +459,6 @@
|
||||
<field name="location">Grand-Rosiere</field>
|
||||
<field name="doors">5</field>
|
||||
<field name="driver_id" ref="base.res_partner_address_25" />
|
||||
<field name="fleet_id" ref="fleet_category_internal"/>
|
||||
<field name="acquisition_date" eval="(DateTime.now() - timedelta(days=233)).strftime('%Y-%m-%d')" />
|
||||
<field name="state_id" ref="fleet_vehicle_state_downgraded"/>
|
||||
<field name="odometer_unit">kilometers</field>
|
||||
@@ -476,7 +474,6 @@
|
||||
<field name="location">Grand-Rosiere</field>
|
||||
<field name="doors">3</field>
|
||||
<field name="driver_id" ref="base.res_partner_address_17" />
|
||||
<field name="fleet_id" ref="fleet_category_internal"/>
|
||||
<field name="acquisition_date" eval="time.strftime('%Y-%m-%d 2:00:00')" />
|
||||
<field name="state_id" ref="fleet_vehicle_state_registered"/>
|
||||
<field name="odometer_unit">kilometers</field>
|
||||
@@ -492,7 +489,6 @@
|
||||
<field name="location">Grand-Rosiere</field>
|
||||
<field name="doors">3</field>
|
||||
<field name="driver_id" ref="base.res_partner_address_16" />
|
||||
<field name="fleet_id" ref="fleet_category_internal"/>
|
||||
<field name="acquisition_date" eval="time.strftime('%Y-%m-%d 2:00:00')" />
|
||||
<field name="state_id" ref="fleet_vehicle_state_registered"/>
|
||||
<field name="odometer_unit">kilometers</field>
|
||||
@@ -508,7 +504,6 @@
|
||||
<field name="location">Grand-Rosiere</field>
|
||||
<field name="doors">5</field>
|
||||
<field name="driver_id" ref="base.res_partner_address_15" />
|
||||
<field name="fleet_id" ref="fleet_category_internal"/>
|
||||
<field name="acquisition_date" eval="time.strftime('%Y-%m-%d 2:00:00')" />
|
||||
<field name="state_id" ref="fleet_vehicle_state_registered"/>
|
||||
<field name="odometer_unit">kilometers</field>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import fleet_service_type
|
||||
from . import fleet_category
|
||||
from . import fleet_category_tag
|
||||
from . import fleet_vehicle
|
||||
from . import fleet_vehicle_assignation_log
|
||||
from . import fleet_vehicle_log_contract
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
class FleetCategory(models.Model):
|
||||
_name = 'fleet.category'
|
||||
_description = 'Vehicle Fleet'
|
||||
|
||||
def _get_default_favorite_user_ids(self):
|
||||
return [(6, 0, [self.env.uid])]
|
||||
|
||||
name = fields.Char()
|
||||
active = fields.Boolean(default=True)
|
||||
tag_ids = fields.Many2many('fleet.category.tag', string='Tags')
|
||||
description = fields.Text()
|
||||
manager_id = fields.Many2one(
|
||||
'res.users', string='Fleet Manager',
|
||||
default=lambda self: self.env.user,
|
||||
domain=lambda self: [('groups_id', 'in', self.env.ref('fleet.fleet_group_manager').id)],
|
||||
)
|
||||
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
color = fields.Integer()
|
||||
|
||||
vehicle_ids = fields.One2many('fleet.vehicle', 'fleet_id', string='Vehicles')
|
||||
car_count = fields.Integer(compute='_compute_vehicle_counts')
|
||||
bike_count = fields.Integer(compute='_compute_vehicle_counts')
|
||||
|
||||
favorite_user_ids = fields.Many2many('res.users', default=_get_default_favorite_user_ids)
|
||||
is_favorite = fields.Boolean(
|
||||
compute='_compute_is_favorite', inverse='_inverse_is_favorite',
|
||||
string='Add to favorite',
|
||||
help='Whether this fleet is in your favorites or not.',
|
||||
)
|
||||
|
||||
@api.depends('vehicle_ids')
|
||||
def _compute_vehicle_counts(self):
|
||||
for fleet in self:
|
||||
car_ids = fleet.vehicle_ids.filtered(lambda v: v.model_id.vehicle_type == 'car')
|
||||
fleet.car_count = len(car_ids)
|
||||
bike_ids = fleet.vehicle_ids - car_ids
|
||||
fleet.bike_count = len(bike_ids)
|
||||
|
||||
def _compute_is_favorite(self):
|
||||
for fleet in self:
|
||||
fleet.is_favorite = self.env.user in fleet.favorite_user_ids
|
||||
|
||||
def _inverse_is_favorite(self):
|
||||
# We may not have write access
|
||||
favorites = not_favorites = self.env['fleet.category'].sudo()
|
||||
for category in self:
|
||||
if self.env.user in category.favorite_user_ids:
|
||||
favorites |= category
|
||||
else:
|
||||
not_favorites |= category
|
||||
|
||||
not_favorites.write({'favorite_user_ids': [(4, self.env.uid)]})
|
||||
favorites.write({'favorite_user_ids': [(3, self.env.uid)]})
|
||||
|
||||
def toggle_active(self):
|
||||
res = super().toggle_active()
|
||||
archived = self.filtered(lambda c: not c.active)
|
||||
for category in archived:
|
||||
category.vehicle_ids.write({
|
||||
'fleet_id': False,
|
||||
})
|
||||
return res
|
||||
|
||||
def action_view_vehicles(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Fleet Vehicles'),
|
||||
'res_model': 'fleet.vehicle',
|
||||
'view_mode': 'tree,kanban,form',
|
||||
'views': [[False, 'list'], [False, 'kanban'], [False, 'form']],
|
||||
'domain': [('fleet_id', '=', self.id)],
|
||||
'context': {'default_fleet_id': self.id},
|
||||
}
|
||||
|
||||
def write(self, vals):
|
||||
# directly compute is_favorite to dodge allow write access right
|
||||
if 'is_favorite' in vals:
|
||||
vals.pop('is_favorite')
|
||||
self._fields['is_favorite'].determine_inverse(self)
|
||||
return super().write(vals) if vals else True
|
||||
@@ -1,15 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
class FleetCategoryTag(models.Model):
|
||||
_name = 'fleet.category.tag'
|
||||
_description = 'Vehicle Fleet Category'
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_name', 'UNIQUE(name)', 'Name must be unique.'),
|
||||
]
|
||||
|
||||
name = fields.Char()
|
||||
color = fields.Integer()
|
||||
@@ -29,19 +29,14 @@ class FleetVehicle(models.Model):
|
||||
name = fields.Char(compute="_compute_vehicle_name", store=True)
|
||||
description = fields.Html("Vehicle Description", help="Add a note about this vehicle")
|
||||
active = fields.Boolean('Active', default=True, tracking=True)
|
||||
fleet_id = fields.Many2one('fleet.category', string='Fleet')
|
||||
manager_id = fields.Many2one(
|
||||
'res.users', 'Manager',
|
||||
'res.users', 'Fleet Manager',
|
||||
compute='_compute_manager_id', store=True, readonly=False,
|
||||
domain=lambda self: [('groups_id', 'in', self.env.ref('fleet.fleet_group_manager').id)],
|
||||
)
|
||||
#Company should always be the same as fleet_id, but since we do have a case where fleet_id is null
|
||||
# and we want the vehicle to be company bound we need to store it aswell, fleet_id.company_id is not editable
|
||||
company_id = fields.Many2one(
|
||||
'res.company', 'Company',
|
||||
compute='_compute_company_id',
|
||||
default=lambda self: self.env.company,
|
||||
store=True,
|
||||
)
|
||||
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
|
||||
country_id = fields.Many2one('res.country', related='company_id.country_id')
|
||||
@@ -50,8 +45,8 @@ class FleetVehicle(models.Model):
|
||||
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)
|
||||
trailer_hook = fields.Boolean(default=False, string='Trailer Hitch', compute='_compute_model_fields', store=True, readonly=False)
|
||||
driver_id = fields.Many2one('res.partner', 'Driver', tracking=True, help='Driver of the vehicle', copy=False)
|
||||
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)]")
|
||||
driver_id = fields.Many2one('res.partner', 'Driver', tracking=True, help='Driver address of the vehicle', copy=False)
|
||||
future_driver_id = fields.Many2one('res.partner', 'Future Driver', tracking=True, help='Next Driver Address 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')
|
||||
|
||||
@@ -115,18 +110,6 @@ class FleetVehicle(models.Model):
|
||||
electric_assistance = fields.Boolean(compute='_compute_model_fields', store=True, readonly=False)
|
||||
frame_size = fields.Float()
|
||||
|
||||
@api.depends('fleet_id')
|
||||
def _compute_manager_id(self):
|
||||
for record in self:
|
||||
record.manager_id = record.fleet_id.manager_id
|
||||
|
||||
@api.depends('fleet_id')
|
||||
def _compute_company_id(self):
|
||||
for record in self:
|
||||
# Retain company when fleet is unset
|
||||
if record.fleet_id:
|
||||
record.company_id = record.fleet_id.company_id
|
||||
|
||||
@api.depends('model_id')
|
||||
def _compute_model_fields(self):
|
||||
'''
|
||||
|
||||
@@ -35,7 +35,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(related='vehicle_id.driver_id', string='Current Driver')
|
||||
purchaser_id = fields.Many2one(related='vehicle_id.driver_id', string='Driver')
|
||||
ins_ref = fields.Char('Reference', size=64, copy=False)
|
||||
state = fields.Selection(
|
||||
[('futur', 'Incoming'),
|
||||
|
||||
@@ -12,7 +12,6 @@ class FleetReport(models.Model):
|
||||
_auto = False
|
||||
_order = 'date_start desc'
|
||||
|
||||
fleet_id = fields.Many2one('fleet.category', 'Fleet', readonly=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', readonly=True)
|
||||
vehicle_id = fields.Many2one('fleet.vehicle', 'Vehicle', readonly=True)
|
||||
name = fields.Char('Vehicle Name', readonly=True)
|
||||
@@ -32,8 +31,7 @@ class FleetReport(models.Model):
|
||||
WITH service_costs AS (
|
||||
SELECT
|
||||
ve.id AS vehicle_id,
|
||||
fl.id as fleet_id,
|
||||
fl.company_id AS company_id,
|
||||
ve.company_id AS company_id,
|
||||
ve.name AS name,
|
||||
ve.driver_id AS driver_id,
|
||||
ve.fuel_type AS fuel_type,
|
||||
@@ -46,8 +44,6 @@ WITH service_costs AS (
|
||||
fleet_vehicle ve
|
||||
JOIN
|
||||
fleet_vehicle_model vem ON vem.id = ve.model_id
|
||||
JOIN
|
||||
fleet_category fl ON fl.id = ve.fleet_id
|
||||
CROSS JOIN generate_series((
|
||||
SELECT
|
||||
min(date)
|
||||
@@ -58,8 +54,7 @@ WITH service_costs AS (
|
||||
ve.active AND se.active AND se.state != 'cancelled'
|
||||
GROUP BY
|
||||
ve.id,
|
||||
fl.id,
|
||||
fl.company_id,
|
||||
ve.company_id,
|
||||
vem.vehicle_type,
|
||||
ve.name,
|
||||
date_start,
|
||||
@@ -71,8 +66,7 @@ WITH service_costs AS (
|
||||
contract_costs AS (
|
||||
SELECT
|
||||
ve.id AS vehicle_id,
|
||||
fl.id as fleet_id,
|
||||
fl.company_id AS company_id,
|
||||
ve.company_id AS company_id,
|
||||
ve.name AS name,
|
||||
ve.driver_id AS driver_id,
|
||||
ve.fuel_type AS fuel_type,
|
||||
@@ -85,8 +79,6 @@ contract_costs AS (
|
||||
fleet_vehicle ve
|
||||
JOIN
|
||||
fleet_vehicle_model vem ON vem.id = ve.model_id
|
||||
JOIN
|
||||
fleet_category fl ON fl.id = ve.fleet_id
|
||||
CROSS JOIN generate_series((
|
||||
SELECT
|
||||
min(acquisition_date)
|
||||
@@ -110,8 +102,7 @@ contract_costs AS (
|
||||
ve.active
|
||||
GROUP BY
|
||||
ve.id,
|
||||
fl.id,
|
||||
fl.company_id,
|
||||
ve.company_id,
|
||||
vem.vehicle_type,
|
||||
ve.name,
|
||||
date_start,
|
||||
@@ -122,7 +113,6 @@ contract_costs AS (
|
||||
)
|
||||
SELECT
|
||||
vehicle_id AS id,
|
||||
fleet_id,
|
||||
company_id,
|
||||
vehicle_id,
|
||||
name,
|
||||
@@ -137,7 +127,6 @@ FROM
|
||||
UNION ALL (
|
||||
SELECT
|
||||
vehicle_id AS id,
|
||||
fleet_id,
|
||||
company_id,
|
||||
vehicle_id,
|
||||
name,
|
||||
|
||||
@@ -80,11 +80,6 @@
|
||||
<field name="model_id" ref="model_fleet_vehicle"/>
|
||||
<field name="groups" eval="[(4, ref('fleet_group_manager'))]"/>
|
||||
</record>
|
||||
<record id="ir_rule_fleet_category" model="ir.rule">
|
||||
<field name="name">Fleet: Multi Company</field>
|
||||
<field name="model_id" ref="model_fleet_category"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="ir_rule_fleet_vehicle" model="ir.rule">
|
||||
<field name="name">Fleet vehicle: Multi Company</field>
|
||||
<field name="model_id" ref="model_fleet_vehicle"/>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
|
||||
fleet_category_access_right_user,fleet_category_access_right,model_fleet_category,fleet_group_user,1,0,0,0
|
||||
fleet_category_tag_model_acces_right_user,fleet_category_tag_access_right,model_fleet_category_tag,fleet_group_user,1,0,0,0
|
||||
fleet_vehicle_model_access_right_user,fleet_vehicle_model_access_right,model_fleet_vehicle_model,fleet_group_user,1,0,0,0
|
||||
fleet_vehicle_tag_access_right_user,fleet_vehicle_tag_access_right,model_fleet_vehicle_tag,fleet_group_user,1,0,0,0
|
||||
fleet_vehicle_state_access_right_user,fleet_vehicle_state_access_right,model_fleet_vehicle_state,fleet_group_user,1,0,0,0
|
||||
@@ -10,8 +8,6 @@ fleet_vehicle_access_right_user,fleet_vehicle_access_right,model_fleet_vehicle,f
|
||||
fleet_vehicle_log_services_access_right_user,fleet_vehicle_log_services_access_right,model_fleet_vehicle_log_services,fleet_group_user,1,0,0,0
|
||||
fleet_vehicle_log_contract_access_right_user,fleet_vehicle_log_contract_access_right,model_fleet_vehicle_log_contract,fleet_group_user,1,0,0,0
|
||||
fleet_service_type_access_right_user,fleet_service_type_access_right,model_fleet_service_type,fleet_group_user,1,0,0,0
|
||||
fleet_category_access_right,fleet_category_access_right,model_fleet_category,fleet_group_manager,1,1,1,1
|
||||
fleet_category_tag_model_acces_right,fleet_category_tag_access_right,model_fleet_category_tag,fleet_group_manager,1,1,1,1
|
||||
fleet_vehicle_model_access_right,fleet_vehicle_model_access_right,model_fleet_vehicle_model,fleet_group_manager,1,1,1,1
|
||||
fleet_vehicle_tag_access_right,fleet_vehicle_tag_access_right,model_fleet_vehicle_tag,fleet_group_manager,1,1,1,1
|
||||
fleet_vehicle_state_access_right,fleet_vehicle_state_access_right,model_fleet_vehicle_state,fleet_group_manager,1,1,1,1
|
||||
|
||||
|
@@ -1,37 +0,0 @@
|
||||
odoo.define('fleet.many2one_fleet_category', function (require) {
|
||||
"use strict;"
|
||||
|
||||
const relationalFields = require('web.relational_fields');
|
||||
const FieldMany2One = relationalFields.FieldMany2One;
|
||||
const FieldRegistry = require('web.field_registry');
|
||||
|
||||
const FieldMany2OneFleetCategory = FieldMany2One.extend({
|
||||
/**
|
||||
* Opens the list view on the vehicles in the fleet instead of the form view
|
||||
*
|
||||
* @param {*} event
|
||||
* @private
|
||||
*/
|
||||
_onClick: function (event) {
|
||||
var self = this;
|
||||
if (this.mode === 'readonly') {
|
||||
event.preventDefault();
|
||||
if (this.noOpen) {
|
||||
this._super(...arguments);
|
||||
} else {
|
||||
event.stopPropagation();
|
||||
this._rpc({
|
||||
model: 'fleet.category',
|
||||
method: 'action_view_vehicles',
|
||||
args: [[this.value.res_id]],
|
||||
}).then(function (action) {
|
||||
self.trigger_up('do_action', {action: action});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
FieldRegistry.add('many2one_fleet_category', FieldMany2OneFleetCategory);
|
||||
return FieldMany2OneFleetCategory;
|
||||
});
|
||||
@@ -10,9 +10,7 @@ odoo.define('fleet.fleet_kanban', function (require) {
|
||||
* @private
|
||||
*/
|
||||
_openRecord() {
|
||||
if (this.modelName === 'fleet.category' && this.$(".oe_kanban_fleet_vehicle").length) {
|
||||
this.$(".oe_kanban_fleet_vehicle").first().click();
|
||||
} else if (this.modelName === 'fleet.vehicle.model.brand' && this.$(".oe_kanban_fleet_model").length) {
|
||||
if (this.modelName === 'fleet.vehicle.model.brand' && this.$(".oe_kanban_fleet_model").length) {
|
||||
this.$('.oe_kanban_fleet_model').first().click();
|
||||
} else {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.o_fleet_category_kanban {
|
||||
.o_kanban_fleet_category_footer {
|
||||
z-index: 0;
|
||||
|
||||
.o_fleet_avatar_user {
|
||||
margin-right: -10px
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,6 @@ class TestFleet(common.TransactionCase):
|
||||
def test_manager_create_vehicle(self):
|
||||
manager = new_test_user(self.env, "test fleet manager", groups="fleet.fleet_group_manager,base.group_partner_manager")
|
||||
user = new_test_user(self.env, "test base user", groups="base.group_user")
|
||||
fleet = self.env['fleet.category'].with_user(manager).create({
|
||||
'name': 'Test Fleet',
|
||||
'description': 'Testing Fleet',
|
||||
})
|
||||
brand = self.env["fleet.vehicle.model.brand"].create({
|
||||
"name": "Audi",
|
||||
})
|
||||
@@ -22,6 +18,5 @@ class TestFleet(common.TransactionCase):
|
||||
self.env["fleet.vehicle"].with_user(manager).create({
|
||||
"model_id": model.id,
|
||||
"driver_id": user.partner_id.id,
|
||||
"fleet_id": fleet.id,
|
||||
"plan_to_change_car": False
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<field name="name" filter_domain="[('name', 'ilike', self)]"/>
|
||||
<field name="driver_id" filter_domain="[('driver_id', 'ilike', self)]"/>
|
||||
<field name="date_start"/>
|
||||
<field name="fleet_id" filter_domain="[('fleet_id', 'ilike', self)]"/>
|
||||
<filter string="Service" name="service" domain="[('cost_type', '=', 'service')]"/>
|
||||
<filter string="Contract" name="contract" domain="[('cost_type', '=', 'contract')]"/>
|
||||
<separator/>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fleet_category_tag_action" model="ir.actions.act_window">
|
||||
<field name="name">Fleet Tags</field>
|
||||
<field name="res_model">fleet.category.tag</field>
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_tag_view_form" model="ir.ui.view">
|
||||
<field name="name">fleet.category.tag.view.form</field>
|
||||
<field name="model">fleet.category.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<h1><field name="name"/></h1>
|
||||
<field name="color" widget="color_picker"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_tag_view_tree" model="ir.ui.view">
|
||||
<field name="name">fleet.category.tag.view.tree</field>
|
||||
<field name="model">fleet.category.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Tags" editable="bottom" sample="1">
|
||||
<field name="name"/>
|
||||
<field name="color" widget="color_picker"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="fleet_category_tag_menu" name="Fleet Tags"
|
||||
parent="fleet.fleet_configuration" sequence="1"
|
||||
action="fleet_category_tag_action" groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fleet_category_action" model="ir.actions.act_window">
|
||||
<field name="name">Fleets</field>
|
||||
<field name="res_model">fleet.category</field>
|
||||
<field name="view_mode">kanban,form</field>
|
||||
<field name="context">{'search_default_my_favorites': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new fleet
|
||||
</p><p>
|
||||
Manage multiple fleets!
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="fleet_category_action" parent="fleet_vehicles"
|
||||
id="fleet_fleet_menu" groups="fleet_group_user" sequence="0"/>
|
||||
|
||||
<record id="fleet_category_view_form" model="ir.ui.view">
|
||||
<field name="name">fleet.category.view.form</field>
|
||||
<field name="model">fleet.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Fleet" delete="0">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<h1><field name="name"/></h1>
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<group>
|
||||
<field name="description"/>
|
||||
<field name="manager_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
<field name="company_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_view_kanban" model="ir.ui.view">
|
||||
<field name="name">fleet.category.view.kanban</field>
|
||||
<field name="model">fleet.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="oe_background_grey o_kanban_dashboard" sample="1">
|
||||
<field name="color"/>
|
||||
<field name="is_favorite"/>
|
||||
<field name="name"/>
|
||||
<field name="bike_count"/>
|
||||
<field name="car_count"/>
|
||||
<field name="active"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click o_fleet_category_kanban">
|
||||
<div class="o_dropdown_kanban dropdown" groups="fleet.fleet_group_manager">
|
||||
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" data-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu">
|
||||
<span class="fa fa-ellipsis-v"/>
|
||||
</a>
|
||||
<div class="dropdown-menu" role="menu" groups="fleet.fleet_group_manager">
|
||||
<a t-if="widget.editable" class="dropdown-item" role="menuitem" type="edit">Edit</a>
|
||||
<a t-if="widget.editable" role="menuitem" class="dropdown-item" name="toggle_active" type="object">
|
||||
<t t-if="record.active.raw_value">Archive</t>
|
||||
<t t-if="!record.active.raw_value">Restore</t>
|
||||
</a>
|
||||
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
|
||||
<div role="separator" class="dropdown-divider"/>
|
||||
<ul class="oe_kanban_colorpicker" data-field="color" role="popup"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- mb-4 to give some space for the footer-->
|
||||
<div class="o_kanban_card_content mb-4">
|
||||
<div class="o_primary col-9">
|
||||
<h2><strong><field name="name"/></strong></h2>
|
||||
</div>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
<field name="description"/>
|
||||
</div>
|
||||
<!-- margin to try to align with the content -->
|
||||
<div class="oe_kanban_content position-absolute fixed-bottom mr-2 ml-3 mb-2 o_kanban_fleet_category_footer">
|
||||
<div class="o_kanban_record_bottom">
|
||||
<div class="oe_kanban_bottom_left">
|
||||
<a class="oe_kanban_fleet_vehicle" name="action_view_vehicles" type="object"/>
|
||||
<strong>
|
||||
<t t-if="record.car_count.raw_value > 0">
|
||||
<field name="car_count"/>
|
||||
<i class="fa fa-car" role="img" aria-label="car" title="Car Icon"/>
|
||||
</t>
|
||||
<t t-if="record.bike_count.raw_value > 0">
|
||||
<field name="bike_count"/>
|
||||
<i class="fa fa-bicycle" role="img" aria-label="bicycle" title="Bike Icon"/>
|
||||
</t>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<field class="o_fleet_avatar_user" name="manager_id" widget="many2one_avatar_user"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="o_right"><field name="is_favorite" widget="boolean_favorite" nolabel="1" force_save="1"/></span>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_view_search" model="ir.ui.view">
|
||||
<field name="name">fleet.category.view.search</field>
|
||||
<field name="model">fleet.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Fleet">
|
||||
<field name="name" string="Fleet"/>
|
||||
<field name="manager_id" string="Fleet Manager"/>
|
||||
<filter string="My Favorites" name="my_favorites" domain="[('favorite_user_ids', 'in', uid)]"/>
|
||||
<separator/>
|
||||
<filter name="archived" string="Archived" domain="[('active', '=', False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -77,9 +77,9 @@
|
||||
<field name="start_date" />
|
||||
<field name="expiration_date" widget="remaining_days"/>
|
||||
<field name="days_left" invisible="1"/>
|
||||
<field name="vehicle_id" widget="many2one_avatar" />
|
||||
<field name="vehicle_id"/>
|
||||
<field name="insurer_id" />
|
||||
<field name="purchaser_id"/>
|
||||
<field name="purchaser_id" widget="many2one_avatar"/>
|
||||
<field name="cost_generated" widget="monetary"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="cost_frequency"/>
|
||||
|
||||
@@ -68,13 +68,11 @@
|
||||
<group string="Driver">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="vehicle_type" invisible="1"/>
|
||||
<field name="fleet_id" widget="many2one_fleet_category"/>
|
||||
<field name="driver_id" domain="['|', ('company_id', '=', False ), ('company_id', '=', company_id)]"/>
|
||||
<field name="future_driver_id"/>
|
||||
<field name="plan_to_change_car" groups="fleet.fleet_group_manager" attrs="{'invisible': [('vehicle_type', '!=', 'car')]}"/>
|
||||
<field name="plan_to_change_bike" groups="fleet.fleet_group_manager" attrs="{'invisible': [('vehicle_type', '!=', 'bike')]}"/>
|
||||
<field name="next_assignation_date"/>
|
||||
<field name="location"/>
|
||||
</group>
|
||||
<group string="Vehicle">
|
||||
<field name="acquisition_date" attrs="{'invisible': [('vehicle_type', '!=', 'car')]}"/>
|
||||
@@ -85,6 +83,7 @@
|
||||
<field name="odometer_unit"/>
|
||||
</div>
|
||||
<field name="manager_id" domain="[('share', '=', False)]"/>
|
||||
<field name="location"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
@@ -159,7 +158,7 @@
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="license_plate" readonly="1"/>
|
||||
<field name="model_id" widget="many2one_avatar" readonly="1"/>
|
||||
<field name="fleet_id" widget="many2one_fleet_category" options="{'no_open': True}"/>
|
||||
<field name="manager_id" optional="hide"/>
|
||||
<field name="driver_id" widget="many2one_avatar" readonly="1"/>
|
||||
<field name="future_driver_id" widget="many2one_avatar" readonly="1"/>
|
||||
<field name="log_drivers" invisible="1"/>
|
||||
@@ -174,7 +173,6 @@
|
||||
<field name="contract_state" widget="badge" decoration-info="contract_state == 'open'"
|
||||
decoration-danger="contract_state == 'expired'" optional="hide"/>
|
||||
<field name="activity_exception_decoration" widget="activity_exception"/>
|
||||
<field name="manager_id" optional="hide"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -193,7 +191,6 @@
|
||||
<field string="Status" name="state_id"/>
|
||||
<filter string="Available" name="available"
|
||||
domain="['&', ('future_driver_id', '=', False), '|', ('driver_id', '=', False), '|', '&', ('plan_to_change_car', '=', True), ('vehicle_type', '=', 'car'), '&', ('plan_to_change_bike', '=', True), ('vehicle_type', '=', 'bike')]"/>
|
||||
<filter name="no_fleet" string="No Fleet" domain="[('fleet_id', '=', False)]"/>
|
||||
<filter string="Bikes" name="bikes" domain="[('vehicle_type', '=', 'bike')]"/>
|
||||
<filter string="Cars" name="cars" domain="[('vehicle_type', '=', 'car')]"/>
|
||||
<filter string="Trailer Hook" name="trailer_hook" domain="[('trailer_hook', '=', True)]"/>
|
||||
@@ -335,7 +332,7 @@
|
||||
</record>
|
||||
|
||||
<menuitem name="Fleet" parent="menu_root" id="fleet_vehicles" sequence="2" groups="fleet_group_user"/>
|
||||
<menuitem action="fleet_vehicle_action" parent="fleet_vehicles" name="Search"
|
||||
<menuitem action="fleet_vehicle_action" parent="fleet_vehicles" name="Fleet"
|
||||
id="fleet_vehicle_menu" groups="fleet_group_user" sequence="0"/>
|
||||
|
||||
<record id='fleet_vehicle_odometer_view_form' model='ir.ui.view'>
|
||||
@@ -536,7 +533,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Assignment Logs" editable="bottom">
|
||||
<field name="vehicle_id" invisible="1"/>
|
||||
<field name="driver_id"/>
|
||||
<field name="driver_id" widget="many2one_avatar"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
</tree>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/mail_channel_views.xml',
|
||||
'views/res_users.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'data/hr_data.xml',
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo import fields, models, _
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
|
||||
_inherit = ['res.partner']
|
||||
|
||||
employee_ids = fields.One2many(
|
||||
'hr.employee', 'address_home_id', string='Employees', groups="hr.group_hr_user",
|
||||
help="Related employees based on their private address")
|
||||
employees_count = fields.Integer(compute='_compute_employees_count', groups="hr.group_hr_user")
|
||||
|
||||
def name_get(self):
|
||||
""" Override to allow an employee to see its private address in his profile.
|
||||
This avoids to relax access rules on `res.parter` and to add an `ir.rule`.
|
||||
@@ -21,3 +25,17 @@ class Partner(models.Model):
|
||||
if len(self) == 1 and self in self.env.user.employee_ids.mapped('address_home_id'):
|
||||
return super(Partner, self.sudo()).name_get()
|
||||
raise e
|
||||
|
||||
def _compute_employees_count(self):
|
||||
for partner in self:
|
||||
partner.employees_count = len(partner.employee_ids)
|
||||
|
||||
def action_open_employees(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _('Related Employees'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.employee',
|
||||
'view_mode': 'kanban,tree,form',
|
||||
'domain': [('id', 'in', self.employee_ids.ids)],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="res_partner_view_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.view.form.inherit.hr</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button name="action_open_employees" type="object" class="oe_stat_button" icon="fa-id-card-o" groups="hr.group_hr_user" attrs="{'invisible': [('employees_count', '=', 0)]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value"><field name="employees_count"/></span>
|
||||
<span class="o_stat_text">Employee(s)</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -8,15 +8,12 @@
|
||||
'description': "",
|
||||
'depends': ['hr', 'fleet'],
|
||||
'data': [
|
||||
'data/hr_fleet_data.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/hr_fleet_security.xml',
|
||||
'views/employee_views.xml',
|
||||
'views/fleet_category_views.xml',
|
||||
'views/fleet_vehicle_views.xml',
|
||||
'views/fleet_vehicle_cost_views.xml',
|
||||
'wizard/hr_departure_wizard_views.xml',
|
||||
'wizard/fleet_convert_wizard_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/hr_fleet_demo.xml',
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fleet.fleet_category_internal" model="fleet.category">
|
||||
<field name="internal" eval="True"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -4,7 +4,6 @@
|
||||
from . import employee
|
||||
from . import res_users
|
||||
from . import fleet_vehicle_assignation_log
|
||||
from . import fleet_category
|
||||
from . import fleet_vehicle
|
||||
from . import fleet_vehicle_log_contract
|
||||
from . import fleet_vehicle_log_services
|
||||
|
||||
@@ -46,7 +46,6 @@ class Employee(models.Model):
|
||||
no_address = self.filtered(lambda r: not r.address_home_id)
|
||||
car_ids = self.env['fleet.vehicle'].sudo().search([
|
||||
('driver_employee_id', 'in', no_address.ids),
|
||||
('fleet_id.internal', '=', True),
|
||||
])
|
||||
# Prevent from removing employee address when linked to a car
|
||||
if car_ids:
|
||||
@@ -60,7 +59,6 @@ class Employee(models.Model):
|
||||
car_ids = self.env['fleet.vehicle'].sudo().search([
|
||||
('driver_employee_id', 'in', self.ids),
|
||||
('driver_id', 'in', self.mapped('address_home_id').ids),
|
||||
('fleet_id.internal', '=', True),
|
||||
])
|
||||
if car_ids:
|
||||
car_ids.write({'driver_id': vals['address_home_id']})
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
class FleetCategory(models.Model):
|
||||
_inherit = 'fleet.category'
|
||||
|
||||
internal = fields.Boolean(default=True)
|
||||
|
||||
def action_view_vehicles(self):
|
||||
res = super().action_view_vehicles()
|
||||
res['context']['internal'] = self.internal
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if 'internal' in vals and not vals['internal']:
|
||||
self.vehicle_ids.write({
|
||||
'driver_employee_id': False,
|
||||
'future_driver_employee_id': False,
|
||||
})
|
||||
return res
|
||||
@@ -10,15 +10,37 @@ class FleetVehicle(models.Model):
|
||||
mobility_card = fields.Char(compute='_compute_mobility_card', store=True)
|
||||
driver_employee_id = fields.Many2one(
|
||||
'hr.employee', 'Driver (Employee)',
|
||||
compute='_compute_driver_employee_id', store=True,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
|
||||
tracking=True,
|
||||
)
|
||||
driver_employee_name = fields.Char(related="driver_employee_id.name")
|
||||
future_driver_employee_id = fields.Many2one(
|
||||
'hr.employee', 'Future Driver (Employee)',
|
||||
compute='_compute_future_driver_employee_id', store=True,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
|
||||
tracking=True,
|
||||
)
|
||||
fleet_is_internal = fields.Boolean(related='fleet_id.internal')
|
||||
|
||||
@api.depends('driver_id')
|
||||
def _compute_driver_employee_id(self):
|
||||
for vehicle in self:
|
||||
if vehicle.driver_id:
|
||||
vehicle.driver_employee_id = self.env['hr.employee'].search([
|
||||
('address_home_id', '=', vehicle.driver_id.id),
|
||||
], limit=1)
|
||||
else:
|
||||
vehicle.driver_employee_id = False
|
||||
|
||||
@api.depends('future_driver_id')
|
||||
def _compute_future_driver_employee_id(self):
|
||||
for vehicle in self:
|
||||
if vehicle.future_driver_id:
|
||||
vehicle.future_driver_employee_id = self.env['hr.employee'].search([
|
||||
('address_home_id', '=', vehicle.future_driver_id.id),
|
||||
], limit=1)
|
||||
else:
|
||||
vehicle.future_driver_employee_id = False
|
||||
|
||||
@api.depends('driver_id')
|
||||
def _compute_mobility_card(self):
|
||||
@@ -30,57 +52,14 @@ class FleetVehicle(models.Model):
|
||||
employee = employee.search([('user_id.partner_id', '=', vehicle.driver_id.id)], limit=1)
|
||||
vehicle.mobility_card = employee.mobility_card
|
||||
|
||||
@api.constrains('driver_employee_id', 'future_driver_employee_id')
|
||||
def _constrain_employee_has_address(self):
|
||||
wrongs = self.filtered(lambda v: (
|
||||
(v.driver_employee_id and not v.driver_employee_id.address_home_id) or
|
||||
(v.future_driver_employee_id and not v.future_driver_employee_id.address_home_id)
|
||||
))
|
||||
if wrongs:
|
||||
raise ValidationError(_(
|
||||
'The following vehicles have invalid employees: %s\n'
|
||||
'Please assign addresses to the employees you want to assign a car to.'
|
||||
) % wrongs.mapped('name'))
|
||||
|
||||
def _get_driver_history_data(self, vals):
|
||||
res = super()._get_driver_history_data(vals)
|
||||
if self.fleet_is_internal and 'driver_employee_id' in vals:
|
||||
res['driver_employee_id'] = vals['driver_employee_id']
|
||||
return res
|
||||
|
||||
def action_accept_driver_change(self):
|
||||
super(FleetVehicle, self.filtered(lambda v: not v.fleet_id.internal)).action_accept_driver_change()
|
||||
#Unfortunate code duplication to avoid creating unwanted vehicle logs
|
||||
internals = self.filtered(lambda v: v.fleet_id.internal)
|
||||
for vehicle in internals:
|
||||
if vehicle.vehicle_type == 'bike':
|
||||
vehicle.future_driver_id.sudo().write({'plan_to_change_bike': False})
|
||||
if vehicle.vehicle_type == 'car':
|
||||
vehicle.future_driver_id.sudo().write({'plan_to_change_car': False})
|
||||
vehicle.driver_employee_id = vehicle.future_driver_employee_id
|
||||
vehicle.future_driver_employee_id = False
|
||||
|
||||
def _update_create_write_vals(self, vals):
|
||||
# wbr NOTE: can't use inverse without breaking history
|
||||
# We do not want to link vehicles to employee when their fleet is not internal
|
||||
# but since this method is called within the write function we are not guaranteed
|
||||
# to always have one record, check if all fleet are internal
|
||||
# This is an edge case, it is better not to link an employee to a car when they should than to link
|
||||
# against cars when they shouldn't, usually you will not update multiple vehicles from many different fleets
|
||||
is_internal = len(self) > 0 and len(self.filtered(lambda v: not v.fleet_id.internal)) == 0
|
||||
# Use fleet_id in vals if it gets updated
|
||||
if 'fleet_id' in vals:
|
||||
fleet_id = self.env['fleet.category'].sudo().browse(vals['fleet_id'])
|
||||
is_internal = fleet_id.internal
|
||||
|
||||
# Compute partner from employee
|
||||
if 'driver_employee_id' in vals:
|
||||
partner = False
|
||||
if vals['driver_employee_id']:
|
||||
employee = self.env['hr.employee'].sudo().browse(vals['driver_employee_id'])
|
||||
partner = employee.address_home_id.id
|
||||
vals['driver_id'] = partner
|
||||
elif 'driver_id' in vals and is_internal:
|
||||
elif 'driver_id' in vals:
|
||||
# Reverse the process if we can find a single employee
|
||||
employee = False
|
||||
if vals['driver_id']:
|
||||
@@ -99,7 +78,7 @@ class FleetVehicle(models.Model):
|
||||
employee = self.env['hr.employee'].sudo().browse(vals['future_driver_employee_id'])
|
||||
partner = employee.address_home_id.id
|
||||
vals['future_driver_id'] = partner
|
||||
elif 'future_driver_id' in vals and is_internal:
|
||||
elif 'future_driver_id' in vals:
|
||||
# Reverse the process if we can find a single employee
|
||||
employee = False
|
||||
if vals['future_driver_id']:
|
||||
@@ -111,44 +90,6 @@ class FleetVehicle(models.Model):
|
||||
employee = employee_ids[0].id
|
||||
vals['future_driver_employee_id'] = employee
|
||||
|
||||
# Cases when fleet_id changes but driver_id and future_driver_id do not
|
||||
# 1: not internal -> remove eployee related data
|
||||
# 2: internal -> compute employee for each vehicle in the case no employee can be computed for the address
|
||||
# we remove it's current driver_id
|
||||
if 'fleet_id' in vals:
|
||||
if not is_internal:
|
||||
vals['driver_employee_id'] = False
|
||||
vals['future_driver_employee_id'] = False
|
||||
else:
|
||||
# Prefetch all employees we will need
|
||||
employees = self.env['hr.employee'].search([
|
||||
('address_home_id', 'in', (self.driver_id | self.future_driver_id).ids),
|
||||
])
|
||||
for vehicle in self:
|
||||
if 'driver_employee_id' not in vals:
|
||||
driver_id = vals['driver_id'] if 'driver_id' in vals else vehicle.driver_id.id
|
||||
driver_employee_id = employees.filtered(lambda e: e.address_home_id.id == driver_id)
|
||||
if len(driver_employee_id) == 1:
|
||||
vehicle.driver_employee_id = driver_employee_id
|
||||
else:
|
||||
vehicle.write({
|
||||
'driver_id': False,
|
||||
'driver_employee_id': False,
|
||||
})
|
||||
if 'future_driver_employee_id' not in vals:
|
||||
future_driver_id = vals['future_driver_id'] if 'future_driver_id' in vals else \
|
||||
vehicle.future_driver_id.id
|
||||
future_driver_employee_id = employees.filtered(lambda e: (
|
||||
e.address_home_id.id == future_driver_id
|
||||
))
|
||||
if len(future_driver_employee_id) == 1:
|
||||
vehicle.future_driver_employee_id = future_driver_employee_id
|
||||
else:
|
||||
vehicle.write({
|
||||
'future_driver_id': False,
|
||||
'future_driver_employee_id': False,
|
||||
})
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
self._update_create_write_vals(vals)
|
||||
@@ -165,3 +106,13 @@ class FleetVehicle(models.Model):
|
||||
partners_to_unsubscribe.append(employee.user_id.partner_id.id)
|
||||
vehicle.message_unsubscribe(partner_ids=partners_to_unsubscribe)
|
||||
return super().write(vals)
|
||||
|
||||
def action_open_employee(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _('Related Employee'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.employee',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.driver_employee_id.id,
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ from odoo import fields, models
|
||||
class FleetVehicleAssignationLog(models.Model):
|
||||
_inherit = 'fleet.vehicle.assignation.log'
|
||||
|
||||
driver_employee_id = fields.Many2one('hr.employee', 'Driver (Employee)')
|
||||
fleet_is_internal = fields.Boolean(related='vehicle_id.fleet_is_internal')
|
||||
driver_employee_id = fields.Many2one(related="vehicle_id.driver_employee_id", string='Driver (Employee)')
|
||||
attachment_number = fields.Integer('Number of Attachments', compute='_compute_attachment_number')
|
||||
|
||||
def _compute_attachment_number(self):
|
||||
|
||||
@@ -8,6 +8,5 @@ class FleetVehicleLogContract(models.Model):
|
||||
|
||||
purchaser_employee_id = fields.Many2one(
|
||||
related='vehicle_id.driver_employee_id',
|
||||
string='Current Driver (Employee)',
|
||||
string='Driver (Employee)',
|
||||
)
|
||||
fleet_is_internal = fields.Boolean(related='vehicle_id.fleet_is_internal')
|
||||
|
||||
@@ -10,13 +10,12 @@ class FleetVehicleLogServices(models.Model):
|
||||
'hr.employee', string="Driver (Employee)",
|
||||
compute='_compute_purchaser_employee_id', readonly=False, store=True,
|
||||
)
|
||||
fleet_is_internal = fields.Boolean(related='vehicle_id.fleet_is_internal')
|
||||
|
||||
@api.depends('vehicle_id', 'purchaser_employee_id')
|
||||
def _compute_purchaser_id(self):
|
||||
internal = self.filtered(lambda r: r.fleet_is_internal)
|
||||
super(FleetVehicleLogServices, (self - internal))._compute_purchaser_id()
|
||||
for service in internal:
|
||||
internals = self.filtered(lambda r: r.purchaser_employee_id)
|
||||
super(FleetVehicleLogServices, (self - internals))._compute_purchaser_id()
|
||||
for service in internals:
|
||||
service.purchaser_id = service.purchaser_employee_id.address_home_id
|
||||
|
||||
@api.depends('vehicle_id')
|
||||
|
||||
@@ -8,6 +8,6 @@ class FleetVehicleOdometer(models.Model):
|
||||
_inherit = 'fleet.vehicle.odometer'
|
||||
|
||||
driver_employee_id = fields.Many2one(
|
||||
related='vehicle_id.driver_employee_id', string='Current Driver (Employee)',
|
||||
related='vehicle_id.driver_employee_id', string='Driver (Employee)',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
|
||||
hr_fleet_convert_wizard_access_right_user,hr_fleet_convert_wizard_access_right,model_hr_fleet_convert_wizard,fleet.fleet_group_manager,1,1,1,1
|
||||
hr_fleet_convert_wizard_line_access_right_user,hr_fleet_convert_wizard_line_access_right,model_hr_fleet_convert_wizard_line,fleet.fleet_group_manager,1,1,1,1
|
||||
hr_fleet_vehicle_access_right_hr_officer,hr_fleet_vehicle_access_right_hr_officer,model_fleet_vehicle,hr.group_hr_user,1,0,0,0
|
||||
|
||||
|
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_fleet_category
|
||||
@@ -1,146 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import new_test_user
|
||||
from odoo.tests.common import tagged, TransactionCase
|
||||
|
||||
@tagged('post_install', '-at_install', 'hr_fleet_categories')
|
||||
class TestFleetCategory(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.company = self.env['res.company'].create({
|
||||
'name': 'Taxi Dermi',
|
||||
})
|
||||
self.manager = new_test_user(self.env, login='dermi', groups='fleet.fleet_group_manager', company_id=self.company.id)
|
||||
self.addresses = self.env['res.partner'].create([
|
||||
{
|
||||
'name': 'Address 1',
|
||||
'company_id': self.company.id,
|
||||
'type': 'private',
|
||||
},
|
||||
{
|
||||
'name': 'Address 2',
|
||||
'company_id': self.company.id,
|
||||
'type': 'private',
|
||||
},
|
||||
{
|
||||
'name': 'Address 3',
|
||||
'company_id': self.company.id,
|
||||
'type': 'private',
|
||||
},
|
||||
{
|
||||
'name': 'Address 4',
|
||||
'company_id': self.company.id,
|
||||
'type': 'private',
|
||||
},
|
||||
])
|
||||
self.employees = self.env['hr.employee'].create([
|
||||
{
|
||||
'name': 'Employee 1',
|
||||
'company_id': self.company.id,
|
||||
'address_home_id': self.addresses[0].id,
|
||||
},
|
||||
{
|
||||
'name': 'Employee 2',
|
||||
'company_id': self.company.id,
|
||||
'address_home_id': self.addresses[1].id,
|
||||
},
|
||||
{
|
||||
'name': 'Employee 3',
|
||||
'company_id': self.company.id,
|
||||
'address_home_id': self.addresses[2].id,
|
||||
},
|
||||
{
|
||||
'name': 'Employee 4',
|
||||
'company_id': self.company.id,
|
||||
'address_home_id': self.addresses[3].id,
|
||||
},
|
||||
])
|
||||
|
||||
self.brand = self.env['fleet.vehicle.model.brand'].create({
|
||||
'name': 'Test Brand',
|
||||
})
|
||||
self.model = self.env['fleet.vehicle.model'].create({
|
||||
'name': 'Test Model',
|
||||
'brand_id': self.brand.id,
|
||||
})
|
||||
|
||||
self.external_fleet = self.env['fleet.category'].create({
|
||||
'name': 'External Fleet',
|
||||
'company_id': self.company.id,
|
||||
'internal': False,
|
||||
})
|
||||
self.internal_fleet = self.env['fleet.category'].create({
|
||||
'name': 'Internal Fleet',
|
||||
'company_id': self.company.id,
|
||||
'internal': True,
|
||||
})
|
||||
|
||||
vehicle_create_vals = []
|
||||
for fleet in (self.external_fleet | self.internal_fleet):
|
||||
for i in range(4):
|
||||
vehicle_create_vals.append({
|
||||
'name': 'Test Car ' + str(i) + ' ' + fleet.name,
|
||||
'fleet_id': fleet.id,
|
||||
'company_id': self.company.id,
|
||||
'model_id': self.model.id,
|
||||
})
|
||||
self.env['fleet.vehicle'].create(vehicle_create_vals)
|
||||
|
||||
def test_fleet_conversion(self):
|
||||
#Assign employee addresses to the fleet vehicle
|
||||
for i in range(4):
|
||||
self.external_fleet.vehicle_ids[i].driver_id = self.employees[i].address_home_id
|
||||
wizard = self.env['hr.fleet.convert.wizard'].with_context(active_id=self.external_fleet.id).new({})
|
||||
|
||||
#Wizard should have a line per vehicle in the fleet
|
||||
self.assertEqual(len(self.external_fleet.vehicle_ids), len(wizard.line_ids))
|
||||
#All lines should be considered valid
|
||||
self.assertFalse(any(wizard.line_ids.filtered('invalid_driver')))
|
||||
|
||||
wizard.action_validate()
|
||||
|
||||
#Fleet should be internal now
|
||||
self.assertTrue(self.external_fleet.internal)
|
||||
#All vehicles should be linked to employees
|
||||
self.assertFalse(self.external_fleet.vehicle_ids.filtered(lambda v: not v.driver_employee_id))
|
||||
|
||||
def test_individual_fleet_change(self):
|
||||
vehicle_id = self.external_fleet.vehicle_ids[0]
|
||||
vehicle_id.driver_id = self.addresses[0]
|
||||
#Also test future_driver
|
||||
vehicle_id.future_driver_id = self.addresses[1]
|
||||
|
||||
#Manually change the fleet of the vehicle, it should compute the employee from the driver
|
||||
vehicle_id.fleet_id = self.internal_fleet
|
||||
self.assertEqual(vehicle_id.driver_employee_id, self.employees[0])
|
||||
self.assertEqual(vehicle_id.future_driver_employee_id, self.employees[1])
|
||||
|
||||
#Now change it back, driver should be kept but not the employee
|
||||
vehicle_id.fleet_id = self.external_fleet
|
||||
self.assertFalse(vehicle_id.driver_employee_id)
|
||||
self.assertFalse(vehicle_id.future_driver_employee_id)
|
||||
|
||||
def test_partial_conversion(self):
|
||||
#Assign employee addresses to the fleet vehicle
|
||||
for i in range(4):
|
||||
self.external_fleet.vehicle_ids[i].driver_id = self.employees[i].address_home_id
|
||||
#Remove address from employee, making the vehicle not be able to match the employee
|
||||
self.employees[2].address_home_id = False
|
||||
self.external_fleet.vehicle_ids[3].driver_id = False
|
||||
|
||||
wizard = self.env['hr.fleet.convert.wizard'].with_context(active_id=self.external_fleet.id).new({})
|
||||
|
||||
#Wizard should have a line per vehicle in the fleet except those without drivers
|
||||
self.assertEqual(len(self.external_fleet.vehicle_ids) - 1, len(wizard.line_ids))
|
||||
#There should be 2 valid and 1 invalid lines
|
||||
self.assertEqual(1, len(wizard.line_ids.filtered('invalid_driver')))
|
||||
self.assertEqual(2, len(wizard.line_ids.filtered(lambda l: not l.invalid_driver)))
|
||||
|
||||
wizard.action_validate()
|
||||
#Fleet should be internal now
|
||||
self.assertTrue(self.external_fleet.internal)
|
||||
#There should be 2 vehicles with a driver and 2 without any
|
||||
self.assertEqual(2, len(self.external_fleet.vehicle_ids.filtered('driver_id')))
|
||||
self.assertEqual(2, len(self.external_fleet.vehicle_ids.filtered(lambda l: not l.driver_id)))
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fleet_category_view_form_inherit_hr_fleet" model="ir.ui.view">
|
||||
<field name="name">fleet.category.view.form.inherit.hr.fleet</field>
|
||||
<field name="model">fleet.category</field>
|
||||
<field name="inherit_id" ref="fleet.fleet_category_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='manager_id']" position="after">
|
||||
<field name="internal"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_category_view_search_inherit_hr_fleet" model="ir.ui.view">
|
||||
<field name="name">fleet.category.view.search.inherit.hr.fleet</field>
|
||||
<field name="model">fleet.category</field>
|
||||
<field name="inherit_id" ref="fleet.fleet_category_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='archived']" position="after">
|
||||
<filter string="Internal" name="filter_internal" domain="[('internal', '=', True)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -6,12 +6,7 @@
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_log_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='purchaser_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="purchaser_employee_id" string="Driver"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='purchaser_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('fleet_is_internal', '=', True)]}</attribute>
|
||||
<field name="purchaser_employee_id" string="Driver" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -22,9 +17,7 @@
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_log_contract_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='purchaser_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="purchaser_employee_id"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
<field name="purchaser_employee_id" optional="hide" widget="many2one_avatar_employee"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -47,12 +40,7 @@
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_log_services_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='purchaser_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="purchaser_employee_id" string="Driver"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='purchaser_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('fleet_is_internal', '=', True)]}</attribute>
|
||||
<field name="purchaser_employee_id" string="Driver (Employee)" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -63,9 +51,7 @@
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_log_services_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='purchaser_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="purchaser_employee_id" readonly="1" widget="many2one_avatar"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
<field name="purchaser_employee_id" readonly="1" widget="many2one_avatar" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -75,15 +61,8 @@
|
||||
<field name="model">fleet.vehicle.log.services</field>
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_log_services_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban/field[@name='amount']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//div/field[@name='purchaser_id']" position="after">
|
||||
<field name="purchaser_employee_id"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//div/field[@name='purchaser_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('fleet_is_internal', '=', True)]}</attribute>
|
||||
<field name="purchaser_employee_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fleet_vehicle_odometer_view_tree" model="ir.ui.view">
|
||||
<field name="name">fleet.vehicle.odometer.view.tree.inherit.hr.fleet</field>
|
||||
<field name="model">fleet.vehicle.odometer</field>
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_odometer_view_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='driver_id']" position="after">
|
||||
<field name="driver_employee_id" widget="many2one_avatar" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_vehicle_assignation_log_view_list" model="ir.ui.view">
|
||||
<field name="name">fleet.vehicle.assignation.log.view.tree.inherit.hr.fleet</field>
|
||||
<field name="model">fleet.vehicle.assignation.log</field>
|
||||
@@ -10,9 +21,7 @@
|
||||
<button name="action_get_attachment_view" string="Attachments" type="object" icon="fa-paperclip"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='driver_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="driver_employee_id" widget="many2one_avatar"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
<field name="driver_employee_id" widget="many2one_avatar" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -23,44 +32,20 @@
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='driver_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="driver_employee_id" string="Driver"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
<field name="driver_employee_id" invisible="1"/>
|
||||
<field name="mobility_card" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='future_driver_id']" position="after">
|
||||
<field name="future_driver_employee_id" string="Future Driver"
|
||||
attrs="{'invisible': [('fleet_is_internal', '=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='driver_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('fleet_is_internal', '=', True)]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='future_driver_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('fleet_is_internal', '=', True)]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="fleet_vehicle_view_tree_inherit_hr" model="ir.ui.view">
|
||||
<field name="name">fleet.vehicle.tree.inherit.hr</field>
|
||||
<field name="model">fleet.vehicle</field>
|
||||
<field name="inherit_id" ref="fleet.fleet_vehicle_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='driver_id']" position="after">
|
||||
<field name="fleet_is_internal" invisible="1"/>
|
||||
<field name="driver_employee_id" widget="many2one_avatar" readonly="1"
|
||||
invisible="'internal' in context and context.get('internal',False) == False"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='future_driver_id']" position="after">
|
||||
<field name="future_driver_employee_id" widget="many2one_avatar" readonly="1"
|
||||
invisible="'internal' in context and context.get('internal',False) == False"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='driver_id']" position="attributes">
|
||||
<attribute name="invisible">'internal' in context and context.get('internal',False)</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='future_driver_id']" position="attributes">
|
||||
<attribute name="invisible">'internal' in context and context.get('internal',False)</attribute>
|
||||
<field name="future_driver_employee_id" invisible="1"/>
|
||||
</xpath>
|
||||
<button name="open_assignation_logs" position="before">
|
||||
<button name="action_open_employee" type="object" class="oe_stat_button" icon="fa-id-card-o" groups="hr.group_hr_user" attrs="{'invisible': [('driver_employee_id', '=', False)]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value"><field name="driver_employee_name"/></span>
|
||||
<span class="o_stat_text">Employee</span>
|
||||
</div>
|
||||
</button>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import fleet_convert_wizard
|
||||
from . import hr_departure_wizard
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
class FleetConvertWizard(models.TransientModel):
|
||||
_name = 'hr.fleet.convert.wizard'
|
||||
_description = 'Convert external fleet to internal'
|
||||
|
||||
@api.model
|
||||
def default_get(self, field_list=None):
|
||||
if self.env['fleet.category'].browse(self.env.context.get('active_id', None)).internal:
|
||||
raise UserError(_('This wizard can only be executed if the \'internal\' checkbox is unchecked.'))
|
||||
return super().default_get(field_list)
|
||||
|
||||
def _default_line_ids(self):
|
||||
fleet_id = self.env['fleet.category'].browse(self.env.context.get('active_id', False))
|
||||
line_ids = []
|
||||
if fleet_id:
|
||||
lines = self.env['hr.fleet.convert.wizard.line']
|
||||
vehicles = fleet_id.vehicle_ids.filtered(lambda v: v.driver_id and not v.driver_employee_id)
|
||||
partners = vehicles.driver_id | vehicles.future_driver_id
|
||||
employees = self.env['hr.employee'].search([
|
||||
('address_home_id', 'in', partners.ids)
|
||||
])
|
||||
partner_employee_map = \
|
||||
{partner: employees.filtered(lambda e: e.address_home_id == partner) for partner in partners}
|
||||
for vehicle in vehicles:
|
||||
lines |= lines.new({
|
||||
'vehicle_id': vehicle,
|
||||
'employee_id': partner_employee_map[vehicle.driver_id][0] \
|
||||
if vehicle.driver_id and partner_employee_map[vehicle.driver_id] else False,
|
||||
'future_employee_id': partner_employee_map[vehicle.future_driver_id][0] \
|
||||
if vehicle.future_driver_id and partner_employee_map[vehicle.future_driver_id] else False,
|
||||
})
|
||||
# Order invalids first, _order doesn't seem to affect the view
|
||||
line_ids = lines.sorted(key=lambda l: (
|
||||
not l.invalid_driver, not l.invalid_future_driver, l.license_plate
|
||||
)
|
||||
)
|
||||
return line_ids
|
||||
|
||||
fleet_id = fields.Many2one(
|
||||
'fleet.category',
|
||||
string='Fleet',
|
||||
default=lambda self: self.env.context.get('active_id', None),
|
||||
readonly=True,
|
||||
)
|
||||
line_ids = fields.One2many(
|
||||
'hr.fleet.convert.wizard.line',
|
||||
'convert_wizard_id',
|
||||
default=_default_line_ids,
|
||||
)
|
||||
|
||||
def action_validate(self):
|
||||
for wizard in self:
|
||||
lines = wizard.line_ids
|
||||
wizard.fleet_id.write({'internal': True})
|
||||
lines.filtered(lambda l: l.invalid_driver).vehicle_id.write({'driver_id': False})
|
||||
lines.filtered(lambda l: l.invalid_future_driver).vehicle_id.write({'future_driver_id': False})
|
||||
for line in lines.filtered(lambda l: not l.invalid_driver):
|
||||
line.vehicle_id.write({
|
||||
'driver_employee_id': line.employee_id.id,
|
||||
'future_driver_employee_id': line.future_employee_id.id,
|
||||
})
|
||||
return True
|
||||
|
||||
class FleetConvertWizardLine(models.TransientModel):
|
||||
_name = 'hr.fleet.convert.wizard.line'
|
||||
_description = 'External fleet conversion data'
|
||||
_order = 'invalid_driver asc'
|
||||
|
||||
convert_wizard_id = fields.Many2one('hr.fleet.convert.wizard', required=True, ondelete='cascade')
|
||||
vehicle_id = fields.Many2one('fleet.vehicle', required=False, ondelete='cascade')
|
||||
vehicle_name = fields.Char(related='vehicle_id.name', readonly=True)
|
||||
license_plate = fields.Char(related='vehicle_id.license_plate')
|
||||
driver_id = fields.Many2one(related='vehicle_id.driver_id')
|
||||
future_driver_id = fields.Many2one(related='vehicle_id.future_driver_id')
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee')
|
||||
future_employee_id = fields.Many2one('hr.employee', string='Future Employee')
|
||||
invalid_driver = fields.Boolean(compute='_compute_invalid')
|
||||
invalid_future_driver = fields.Boolean(compute='_compute_invalid')
|
||||
|
||||
@api.depends('employee_id', 'future_employee_id')
|
||||
def _compute_invalid(self):
|
||||
invalids = self.filtered(lambda r: not r.employee_id or (r.employee_id and not r.employee_id.address_home_id))
|
||||
valids = (self - invalids)
|
||||
invalids.write({'invalid_driver': True})
|
||||
valids.write({'invalid_driver': False})
|
||||
invalids = self.filtered(lambda r: (r.future_driver_id and not r.future_employee_id) or \
|
||||
(r.future_employee_id and not r.future_employee_id.address_home_id))
|
||||
valids = (self - invalids)
|
||||
invalids.write({'invalid_future_driver': True})
|
||||
valids.write({'invalid_future_driver': False})
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="hr_fleet_convert_wizard_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.fleet.convert.wizard.view.form</field>
|
||||
<field name="model">hr.fleet.convert.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Convert Fleet">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="fleet_id" widget="many2one_fleet_category" options="{'no_open': True}"/>
|
||||
</group>
|
||||
<field name="line_ids">
|
||||
<tree decoration-danger="invalid_driver" decoration-warning="invalid_future_driver"
|
||||
create="false" delete="false" editable="bottom">
|
||||
<field name="invalid_driver" invisible="1"/>
|
||||
<field name="invalid_future_driver" invisible="1"/>
|
||||
<field name="convert_wizard_id" invisible="1"/>
|
||||
<field name="vehicle_id" invisible="1"/>
|
||||
<field name="vehicle_name" string="Vehicle"/>
|
||||
<field name="license_plate"/>
|
||||
<field name="driver_id" options="{'no_open': True}"/>
|
||||
<field name="future_driver_id" options="{'no_open': True}"/>
|
||||
<field name="employee_id" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="future_employee_id" options="{'no_create': True, 'no_open': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_validate" string="Apply" type="object" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_fleet_convert_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Convert Fleet</field>
|
||||
<field name="res_model">hr.fleet.convert.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="model_fleet_category"/>
|
||||
<field name="binding_view_types">form</field>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_no_one'))]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user