summaryrefslogtreecommitdiff
path: root/addons/fleet/models/fleet_vehicle_cost.py
diff options
context:
space:
mode:
Diffstat (limited to 'addons/fleet/models/fleet_vehicle_cost.py')
-rw-r--r--addons/fleet/models/fleet_vehicle_cost.py315
1 files changed, 60 insertions, 255 deletions
diff --git a/addons/fleet/models/fleet_vehicle_cost.py b/addons/fleet/models/fleet_vehicle_cost.py
index 69c55dce09a..ab073a6a877 100644
--- a/addons/fleet/models/fleet_vehicle_cost.py
+++ b/addons/fleet/models/fleet_vehicle_cost.py
@@ -6,78 +6,8 @@ from odoo.exceptions import UserError
from dateutil.relativedelta import relativedelta
-
-class FleetVehicleCost(models.Model):
- _name = 'fleet.vehicle.cost'
- _description = 'Cost related to a vehicle'
- _order = 'date desc, vehicle_id asc'
-
- name = fields.Char(related='vehicle_id.name', string='Name', store=True, readonly=False)
- vehicle_id = fields.Many2one('fleet.vehicle', 'Vehicle', required=True, help='Vehicle concerned by this log')
- cost_subtype_id = fields.Many2one('fleet.service.type', 'Type', help='Cost type purchased with this cost')
- amount = fields.Float('Total Price')
- cost_type = fields.Selection([
- ('contract', 'Contract'),
- ('services', 'Services'),
- ('fuel', 'Fuel'),
- ('other', 'Other')
- ], 'Category of the cost', default="other", help='For internal purpose only', required=True)
- parent_id = fields.Many2one('fleet.vehicle.cost', 'Parent', help='Parent cost to this current cost')
- cost_ids = fields.One2many('fleet.vehicle.cost', 'parent_id', 'Included Services', copy=True)
- odometer_id = fields.Many2one('fleet.vehicle.odometer', 'Odometer', help='Odometer measure of the vehicle at the moment of this log')
- odometer = fields.Float(compute="_get_odometer", inverse='_set_odometer', string='Odometer Value',
- help='Odometer measure of the vehicle at the moment of this log')
- odometer_unit = fields.Selection(related='vehicle_id.odometer_unit', string="Unit", readonly=True)
- date = fields.Date(help='Date when the cost has been executed')
- contract_id = fields.Many2one('fleet.vehicle.log.contract', 'Contract', help='Contract attached to this cost')
- auto_generated = fields.Boolean('Automatically Generated', readonly=True)
- description = fields.Char("Cost Description")
- company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
- currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
-
- def _get_odometer(self):
- self.odometer = 0.0
- for record in self:
- record.odometer = False
- if record.odometer_id:
- record.odometer = record.odometer_id.value
-
- def _set_odometer(self):
- for record in self:
- if not record.odometer:
- raise UserError(_('Emptying the odometer value of a vehicle is not allowed.'))
- odometer = self.env['fleet.vehicle.odometer'].create({
- 'value': record.odometer,
- 'date': record.date or fields.Date.context_today(record),
- 'vehicle_id': record.vehicle_id.id
- })
- self.odometer_id = odometer
-
- @api.model_create_multi
- def create(self, vals_list):
- for data in vals_list:
- # make sure that the data are consistent with values of parent and contract records given
- if 'parent_id' in data and data['parent_id']:
- parent = self.browse(data['parent_id'])
- data['vehicle_id'] = parent.vehicle_id.id
- data['date'] = parent.date
- data['cost_type'] = parent.cost_type
- if 'contract_id' in data and data['contract_id']:
- contract = self.env['fleet.vehicle.log.contract'].browse(data['contract_id'])
- data['vehicle_id'] = contract.vehicle_id.id
- data['cost_subtype_id'] = contract.cost_subtype_id.id
- data['cost_type'] = contract.cost_type
- if 'odometer' in data and not data['odometer']:
- # if received value for odometer is 0, then remove it from the
- # data as it would result to the creation of a
- # odometer log with 0, which is to be avoided
- del data['odometer']
- return super(FleetVehicleCost, self).create(vals_list)
-
-
class FleetVehicleLogContract(models.Model):
_inherit = ['mail.thread', 'mail.activity.mixin']
- _inherits = {'fleet.vehicle.cost': 'cost_id'}
_name = 'fleet.vehicle.log.contract'
_description = 'Contract information on a vehicle'
_order = 'state desc,expiration_date'
@@ -87,18 +17,13 @@ class FleetVehicleLogContract(models.Model):
start_date = fields.Date.from_string(strdate)
return fields.Date.to_string(start_date + oneyear)
- @api.model
- def default_get(self, default_fields):
- res = super(FleetVehicleLogContract, self).default_get(default_fields)
- contract = self.env.ref('fleet.type_contract_leasing', raise_if_not_found=False)
- res.update({
- 'date': fields.Date.context_today(self),
- 'cost_subtype_id': contract and contract.id or False,
- 'cost_type': 'contract'
- })
- return res
-
- name = fields.Text(compute='_compute_contract_name', store=True)
+ vehicle_id = fields.Many2one('fleet.vehicle', 'Vehicle', default=1, required=True, help='Vehicle concerned by this log')
+ cost_subtype_id = fields.Many2one('fleet.service.type', 'Type', help='Cost type purchased with this cost', domain=[('category', '=', 'contract')])
+ amount = fields.Float('Cost')
+ date = fields.Date(help='Date when the cost has been executed')
+ company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
+ currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
+ name = fields.Char(string='Name', compute='_compute_contract_name', store=True)
active = fields.Boolean(default=True)
user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user, index=True)
start_date = fields.Date('Contract Start Date', default=fields.Date.context_today,
@@ -108,13 +33,11 @@ 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', '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)
+ 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'),
('open', 'In Progress'),
- ('diesoon', 'Expiring Soon'),
('expired', 'Expired'),
('closed', 'Closed')
], 'Status', default='open', readonly=True,
@@ -122,34 +45,22 @@ class FleetVehicleLogContract(models.Model):
tracking=True,
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', tracking=True,
- 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_generated = fields.Float('Recurring Cost')
cost_frequency = fields.Selection([
('no', 'No'),
('daily', 'Daily'),
('weekly', 'Weekly'),
('monthly', 'Monthly'),
('yearly', 'Yearly')
- ], 'Recurring Cost Frequency', default='no', help='Frequency of the recuring cost', required=True)
- generated_cost_ids = fields.One2many('fleet.vehicle.cost', 'contract_id', 'Generated Costs')
- sum_cost = fields.Float(compute='_compute_sum_cost', string='Indicative Costs Total')
- cost_id = fields.Many2one('fleet.vehicle.cost', 'Cost', required=True, ondelete='cascade')
- # we need to keep this field as a related with store=True because the graph view doesn't support
- # (1) to address fields from inherited table
- # (2) fields that aren't stored in database
- cost_amount = fields.Float(related='cost_id.amount', string='Amount', store=True, readonly=False)
- odometer = fields.Float(string='Creation Contract Odometer',
- help='Odometer measure of the vehicle at the moment of the contract creation')
+ ], 'Recurring Cost Frequency', default='monthly', help='Frequency of the recuring cost', required=True)
+ service_ids = fields.Many2many('fleet.service.type', string="Included Services")
- @api.depends('vehicle_id', 'cost_subtype_id', 'date')
+ @api.depends('vehicle_id', 'cost_subtype_id')
def _compute_contract_name(self):
for record in self:
name = record.vehicle_id.name
if record.cost_subtype_id.name:
- name += ' / ' + record.cost_subtype_id.name
- if record.date:
- name += ' / ' + str(record.date)
+ name = record.cost_subtype_id.name + ' ' + name
record.name = name
@api.depends('expiration_date', 'state')
@@ -160,7 +71,7 @@ class FleetVehicleLogContract(models.Model):
otherwise return the number of days before the contract expires
"""
for record in self:
- if record.expiration_date and record.state in ['open', 'diesoon', 'expired']:
+ if record.expiration_date and record.state in ['open', 'expired']:
today = fields.Date.from_string(fields.Date.today())
renew_date = fields.Date.from_string(record.expiration_date)
diff_time = (renew_date - today).days
@@ -168,16 +79,6 @@ class FleetVehicleLogContract(models.Model):
else:
record.days_left = -1
- @api.depends('cost_ids.amount')
- def _compute_sum_cost(self):
- for contract in self:
- contract.sum_cost = sum(contract.cost_ids.mapped('amount'))
-
- @api.onchange('vehicle_id')
- def _onchange_vehicle(self):
- if self.vehicle_id:
- self.odometer_unit = self.vehicle_id.odometer_unit
-
def write(self, vals):
res = super(FleetVehicleLogContract, self).write(vals)
if vals.get('expiration_date') or vals.get('user_id'):
@@ -188,80 +89,14 @@ class FleetVehicleLogContract(models.Model):
for record in self:
record.state = 'closed'
+ def contract_draft(self):
+ for record in self:
+ record.state = 'futur'
+
def contract_open(self):
for record in self:
record.state = 'open'
- def act_renew_contract(self):
- assert len(self.ids) == 1, "This operation should only be done for 1 single contract at a time, as it it suppose to open a window as result"
- for element in self:
- # compute end date
- startdate = fields.Date.from_string(element.start_date)
- enddate = fields.Date.from_string(element.expiration_date)
- diffdate = (enddate - startdate)
- default = {
- 'date': fields.Date.context_today(self),
- 'start_date': fields.Date.to_string(fields.Date.from_string(element.expiration_date) + relativedelta(days=1)),
- 'expiration_date': fields.Date.to_string(enddate + diffdate),
- }
- newid = element.copy(default).id
- return {
- 'name': _("Renew Contract"),
- 'view_mode': 'form',
- 'view_id': self.env.ref('fleet.fleet_vehicle_log_contract_view_form').id,
- 'res_model': 'fleet.vehicle.log.contract',
- 'type': 'ir.actions.act_window',
- 'domain': '[]',
- 'res_id': newid,
- 'context': {'active_id': newid},
- }
-
- @api.model
- def scheduler_manage_auto_costs(self):
- # This method is called by a cron task
- # It creates costs for contracts having the "recurring cost" field setted, depending on their frequency
- # For example, if a contract has a reccuring cost of 200 with a weekly frequency, this method creates a cost of 200 on the
- # first day of each week, from the date of the last recurring costs in the database to today
- # If the contract has not yet any recurring costs in the database, the method generates the recurring costs from the start_date to today
- # The created costs are associated to a contract thanks to the many2one field contract_id
- # If the contract has no start_date, no cost will be created, even if the contract has recurring costs
- VehicleCost = self.env['fleet.vehicle.cost']
- deltas = {
- 'yearly': relativedelta(years=+1),
- 'monthly': relativedelta(months=+1),
- 'weekly': relativedelta(weeks=+1),
- 'daily': relativedelta(days=+1)
- }
- contracts = self.env['fleet.vehicle.log.contract'].search([('state', '!=', 'closed')], offset=0, limit=None, order=None)
- for contract in contracts:
- if not contract.start_date or contract.cost_frequency == 'no':
- continue
- found = False
- startdate = contract.start_date
- if contract.generated_cost_ids:
- last_autogenerated_cost = VehicleCost.search([
- ('contract_id', '=', contract.id),
- ('auto_generated', '=', True)
- ], offset=0, limit=1, order='date desc')
- if last_autogenerated_cost:
- found = True
- startdate = last_autogenerated_cost.date
- if found:
- startdate += deltas.get(contract.cost_frequency)
- today = fields.Date.context_today(self)
- while (startdate <= today) & (startdate <= contract.expiration_date):
- data = {
- 'amount': contract.cost_generated,
- 'date': fields.Date.context_today(self),
- 'vehicle_id': contract.vehicle_id.id,
- 'cost_subtype_id': contract.cost_subtype_id.id,
- 'contract_id': contract.id,
- 'auto_generated': True
- }
- self.env['fleet.vehicle.cost'].create(data)
- startdate += deltas.get(contract.cost_frequency)
- return True
-
@api.model
def scheduler_manage_contract_expiration(self):
# This method is called by a cron task
@@ -272,7 +107,6 @@ class FleetVehicleLogContract(models.Model):
outdated_days = fields.Date.to_string(date_today + relativedelta(days=+delay_alert_contract))
nearly_expired_contracts = self.search([('state', '=', 'open'), ('expiration_date', '<', outdated_days)])
- nearly_expired_contracts.write({'state': 'diesoon'})
for contract in nearly_expired_contracts.filtered(lambda contract: contract.user_id):
contract.activity_schedule(
'fleet.mail_act_fleet_contract_to_renew', contract.expiration_date,
@@ -288,67 +122,12 @@ class FleetVehicleLogContract(models.Model):
now_running_contracts.write({'state': 'open'})
def run_scheduler(self):
- self.scheduler_manage_auto_costs()
self.scheduler_manage_contract_expiration()
-
-class FleetVehicleLogFuel(models.Model):
- _name = 'fleet.vehicle.log.fuel'
- _description = 'Fuel log for vehicles'
- _inherits = {'fleet.vehicle.cost': 'cost_id'}
-
- @api.model
- def default_get(self, default_fields):
- res = super(FleetVehicleLogFuel, self).default_get(default_fields)
- service = self.env.ref('fleet.type_service_refueling', raise_if_not_found=False)
- res.update({
- 'date': fields.Date.context_today(self),
- 'cost_subtype_id': service and service.id or False,
- 'cost_type': 'fuel'
- })
- return res
-
- liter = fields.Float()
- price_per_liter = fields.Float()
- purchaser_id = fields.Many2one('res.partner', 'Purchaser')
- inv_ref = fields.Char('Invoice Reference', size=64)
- vendor_id = fields.Many2one('res.partner', 'Vendor')
- notes = fields.Text()
- cost_id = fields.Many2one('fleet.vehicle.cost', 'Cost', required=True, ondelete='cascade')
- # we need to keep this field as a related with store=True because the graph view doesn't support
- # (1) to address fields from inherited table
- # (2) fields that aren't stored in database
- cost_amount = fields.Float(related='cost_id.amount', string='Amount', store=True, readonly=False)
-
- @api.onchange('vehicle_id')
- def _onchange_vehicle(self):
- if self.vehicle_id:
- self.odometer_unit = self.vehicle_id.odometer_unit
- self.purchaser_id = self.vehicle_id.driver_id.id
-
- @api.onchange('liter', 'price_per_liter', 'amount')
- def _onchange_liter_price_amount(self):
- # need to cast in float because the value receveid from web client maybe an integer (Javascript and JSON do not
- # make any difference between 3.0 and 3). This cause a problem if you encode, for example, 2 liters at 1.5 per
- # liter => total is computed as 3.0, then trigger an onchange that recomputes price_per_liter as 3/2=1 (instead
- # of 3.0/2=1.5)
- # If there is no change in the result, we return an empty dict to prevent an infinite loop due to the 3 intertwine
- # onchange. And in order to verify that there is no change in the result, we have to limit the precision of the
- # computation to 2 decimal
- liter = float(self.liter)
- price_per_liter = float(self.price_per_liter)
- amount = float(self.amount)
- if liter > 0 and price_per_liter > 0 and round(liter * price_per_liter, 2) != amount:
- self.amount = round(liter * price_per_liter, 2)
- elif amount > 0 and liter > 0 and round(amount / liter, 2) != price_per_liter:
- self.price_per_liter = round(amount / liter, 2)
- elif amount > 0 and price_per_liter > 0 and round(amount / price_per_liter, 2) != liter:
- self.liter = round(amount / price_per_liter, 2)
-
-
class FleetVehicleLogServices(models.Model):
_name = 'fleet.vehicle.log.services'
- _inherits = {'fleet.vehicle.cost': 'cost_id'}
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _rec_name = 'service_type_id'
_description = 'Services for vehicles'
@api.model
@@ -357,22 +136,48 @@ class FleetVehicleLogServices(models.Model):
service = self.env.ref('fleet.type_service_service_8', raise_if_not_found=False)
res.update({
'date': fields.Date.context_today(self),
- 'cost_subtype_id': service and service.id or False,
- 'cost_type': 'services'
+ 'service_type_id': service.id if service else None,
})
return res
- purchaser_id = fields.Many2one('res.partner', 'Purchaser')
- inv_ref = fields.Char('Invoice Reference')
+ vehicle_id = fields.Many2one('fleet.vehicle', 'Vehicle', default=1, required=True, help='Vehicle concerned by this log')
+ amount = fields.Float('Cost')
+ odometer_id = fields.Many2one('fleet.vehicle.odometer', 'Odometer', help='Odometer measure of the vehicle at the moment of this log')
+ odometer = fields.Float(compute="_get_odometer", inverse='_set_odometer', string='Odometer Value',
+ help='Odometer measure of the vehicle at the moment of this log')
+ odometer_unit = fields.Selection(related='vehicle_id.odometer_unit', string="Unit", readonly=True)
+ date = fields.Date(help='Date when the cost has been executed')
+ company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
+ currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
+ purchaser_id = fields.Many2one(related='vehicle_id.driver_id', string="Driver")
+ inv_ref = fields.Char('Vendor Reference')
vendor_id = fields.Many2one('res.partner', 'Vendor')
- # we need to keep this field as a related with store=True because the graph view doesn't support
- # (1) to address fields from inherited table and (2) fields that aren't stored in database
- cost_amount = fields.Float(related='cost_id.amount', string='Amount', store=True, readonly=False)
notes = fields.Text()
- cost_id = fields.Many2one('fleet.vehicle.cost', 'Cost', required=True, ondelete='cascade')
+ service_type_id = fields.Many2one('fleet.service.type', 'Service Type', required=True)
- @api.onchange('vehicle_id')
- def _onchange_vehicle(self):
- if self.vehicle_id:
- self.odometer_unit = self.vehicle_id.odometer_unit
- self.purchaser_id = self.vehicle_id.driver_id.id
+ def _get_odometer(self):
+ self.odometer = 0
+ for record in self:
+ if record.odometer_id:
+ record.odometer = record.odometer_id.value
+
+ def _set_odometer(self):
+ for record in self:
+ if not record.odometer:
+ raise UserError(_('Emptying the odometer value of a vehicle is not allowed.'))
+ odometer = self.env['fleet.vehicle.odometer'].create({
+ 'value': record.odometer,
+ 'date': record.date or fields.Date.context_today(record),
+ 'vehicle_id': record.vehicle_id.id
+ })
+ self.odometer_id = odometer
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ for data in vals_list:
+ if 'odometer' in data and not data['odometer']:
+ # if received value for odometer is 0, then remove it from the
+ # data as it would result to the creation of a
+ # odometer log with 0, which is to be avoided
+ del data['odometer']
+ return super(FleetVehicleLogServices, self).create(vals_list) \ No newline at end of file