You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
28 KiB
677 lines
28 KiB
# -*- coding: utf-8 -*-
|
|
###############################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
#
|
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
|
# Author: Anfas Faisal K (odoo@cybrosys.com)
|
|
#
|
|
# This program is under the terms of the Odoo Proprietary License v1.0(OPL-1)
|
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the
|
|
# Software or modified copies of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
# DEALINGS IN THE SOFTWARE.
|
|
#
|
|
###############################################################################
|
|
from datetime import date
|
|
from odoo import api, fields, models, _
|
|
from dateutil.relativedelta import relativedelta
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class FleetRentalContract(models.Model):
|
|
"""
|
|
Represents a car rental contract, including details about the customer,
|
|
vehicle, rental period, charges, and various related information.
|
|
"""
|
|
_name = 'fleet.rental.contract'
|
|
_description = 'Fleet Rental Contract'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
|
|
name = fields.Char(string="Sequence",
|
|
default=lambda self: _('New'),
|
|
copy=False, readonly=True, tracking=True,
|
|
help='Unique contract number for the rental agreement.')
|
|
|
|
state = fields.Selection(
|
|
[('new', 'New'),
|
|
('in_progress', 'In Progress'),
|
|
('return', 'Return'), ('cancel', 'Cancel')],
|
|
string='State', default='new',
|
|
help='Contract Progress',
|
|
group_expand='_group_expand_states',tracking=True)
|
|
customer_id = fields.Many2one(
|
|
'res.partner', string='Customer',
|
|
help='The customer who is renting the vehicle.',
|
|
required=True, tracking=True)
|
|
email = fields.Char(string='Email',
|
|
related='customer_id.email',
|
|
help='Email address of the customer.',
|
|
readonly=False)
|
|
phone = fields.Char('Phone', related='customer_id.phone',
|
|
help="Phone Number of the customer")
|
|
pickup_date = fields.Datetime(
|
|
string='Pick-up Date',
|
|
help='Date and time when the vehicle will be picked up.',
|
|
required=True, tracking=True)
|
|
dropoff_date = fields.Datetime(
|
|
string='Drop-off Date',
|
|
help='Date and time when the vehicle will be returned.',
|
|
required=True, tracking=True)
|
|
pickup_location = fields.Char(
|
|
string='Pick-Up Location',
|
|
help='Location where the vehicle will be picked up.',
|
|
required=True)
|
|
dropoff_location = fields.Char(
|
|
string='Drop-Off Location',
|
|
help='Location where the vehicle will be dropped off.',
|
|
required=True)
|
|
pickup_street = fields.Char(
|
|
string='Pick-Up Street',
|
|
help='Street address for the pick-up location.',
|
|
required=True)
|
|
dropoff_street = fields.Char(
|
|
string='Drop-Off Street',
|
|
help='Street address for the drop-off location.',
|
|
required=True)
|
|
pickup_city = fields.Char(
|
|
string='Pick-Up City',
|
|
help='City where the vehicle will be picked up.',
|
|
required=True)
|
|
dropoff_city = fields.Char(
|
|
string='Drop-Off City',
|
|
help='City where the vehicle will be dropped off.',
|
|
required=True)
|
|
pickup_state_id = fields.Many2one(
|
|
'res.country.state',
|
|
string='Pick-Up State',
|
|
help='State where the vehicle will be picked up.',
|
|
required=True)
|
|
dropoff_state_id = fields.Many2one(
|
|
'res.country.state',
|
|
string='Drop-Off State',
|
|
help='State where the vehicle will be dropped off.',
|
|
required=True)
|
|
pickup_zip = fields.Char(
|
|
string='Pick-Up ZIP',
|
|
help='ZIP code for the pick-up location.',
|
|
required=True)
|
|
dropoff_zip = fields.Char(string='Drop-Off ZIP',
|
|
help='ZIP code for the drop-off location.')
|
|
pickup_country_id = fields.Many2one(
|
|
'res.country',
|
|
string='Pick-Up Country',
|
|
help='Country where the vehicle will be picked up.',
|
|
required=True)
|
|
dropoff_country_id = fields.Many2one(
|
|
'res.country',
|
|
string='Drop-Off Country',
|
|
help='Country where the vehicle will be dropped off.',
|
|
required=True)
|
|
vehicle_id = fields.Many2one(
|
|
'fleet.vehicle', string='Vehicle',
|
|
help='The vehicle being rented.',
|
|
required=True)
|
|
model = fields.Char(related='vehicle_id.model_id.name', string='Model',
|
|
help='Model of the rented vehicle.')
|
|
transmission = fields.Selection(
|
|
[('automatic', 'Automatic'), ('manual', 'Manual')],
|
|
string='Transmission', related='vehicle_id.transmission',
|
|
help='Transmission type of the rented vehicle.')
|
|
fuel_type = fields.Selection(
|
|
[
|
|
('diesel', 'Diesel'),
|
|
('gasoline', 'Gasoline'),
|
|
('full_hybrid', 'Full Hybrid'),
|
|
('plug_in_hybrid_diesel', 'Plug-in Hybrid Diesel'),
|
|
('plug_in_hybrid_gasoline', 'Plug-in Hybrid Gasoline'),
|
|
('cng', 'CNG'),
|
|
('lpg', 'LPG'),
|
|
('hydrogen', 'Hydrogen'),
|
|
('electric', 'Electric'),
|
|
], string='Fuel Type', related='vehicle_id.fuel_type',
|
|
help='Fuel type of the rented vehicle.')
|
|
last_odometer = fields.Float(
|
|
string='Last Odometer',
|
|
related='vehicle_id.odometer',
|
|
help='Last recorded odometer reading of the vehicle.')
|
|
odometer_unit = fields.Selection(
|
|
[('kilometers', 'km'), ('miles', 'mi')],
|
|
string='Odometer Unit', default='kilometers',
|
|
related='vehicle_id.odometer_unit',
|
|
help='Unit of measurement for the odometer reading.')
|
|
|
|
driver_required = fields.Boolean(
|
|
string='Driver Required',
|
|
help='Indicates if a driver is required for the rental.')
|
|
driver_id = fields.Many2one(
|
|
'res.partner', string='Driver',
|
|
help='Driver assigned to the rental if required.')
|
|
charge_type = fields.Selection(
|
|
[('excluding', 'Excluding in rent charge'),
|
|
('including', 'Including in rent charge')],
|
|
string='Charge Type',
|
|
help='Specifies if the driver charge is included in the rental '
|
|
'charge or not.')
|
|
driver_charge = fields.Float(
|
|
string='Driver Charge',
|
|
help='Charge for the driver if not included '
|
|
'in the rental charge.')
|
|
|
|
# Rent Details
|
|
rent_type = fields.Selection(
|
|
[('hours', 'Hours'), ('days', 'Days'),
|
|
('kilometers', 'Kilometers')],
|
|
string='Rent Type',
|
|
default='hours',
|
|
help='Type of rent calculation (per day, per kilometer, or per mile).')
|
|
rent_per_hour = fields.Float(string='Rent / Hour',
|
|
help='Rental charge per hour.',
|
|
related='vehicle_id.rent_hour')
|
|
total_hours = fields.Float(string='Total Hours ',
|
|
help="Total Hours Taken for Rent",
|
|
compute='_compute_rental_period', store=True,
|
|
readonly=False)
|
|
rent_per_day = fields.Float(string='Rent / Day',
|
|
help='Rental charge per day.',
|
|
related='vehicle_id.rent_day')
|
|
total_days = fields.Integer(string='Total Days',
|
|
help='Total number of rental days.',
|
|
compute='_compute_rental_period', store=True,
|
|
readonly=False)
|
|
rent_per_km = fields.Float(string='Rent / KM',
|
|
help='Rental charge per km.'
|
|
, related='vehicle_id.rent_kilometer')
|
|
total_km = fields.Integer(string='Total KM',
|
|
help='Total Kilometers.')
|
|
total_rental_charge = fields.Float(
|
|
string='Total Rental Charge',
|
|
compute='_compute_total_rental_charge', store=True,
|
|
help='Total rental charge for the contract.')
|
|
|
|
payment_type = fields.Selection(
|
|
[('daily', 'Daily'), ('weekly', 'Weekly'),
|
|
('monthly', 'Monthly'),
|
|
('full', 'Fully')],
|
|
string='Payment Type',
|
|
help='Payment schedule for the rental charge.',
|
|
default='full')
|
|
invoice_item_id = fields.Many2one(
|
|
'product.product',
|
|
string='Invoice Item',
|
|
help='Description of the item to be invoiced.',
|
|
default=lambda self: self.env.ref(
|
|
'advanced_fleet_rental.product_product_vehicle_rental_charge'))
|
|
is_extra_charge = fields.Boolean(string='Is any extra charge',
|
|
help='Indicates if there are any extra '
|
|
'charges applicable.')
|
|
extra_per_hour = fields.Float(string='Extra Charges / Hour',
|
|
help='Rental charge per hour.',
|
|
related='vehicle_id.charge_hour')
|
|
total_extra_hours = fields.Integer(string='Total Extra Hours',
|
|
help='Total number of rental hours.')
|
|
extra_per_day = fields.Float(string='Extra Charges / Day',
|
|
help='Rental charge per hour.',
|
|
related='vehicle_id.charge_day')
|
|
total_extra_days = fields.Integer(string='Total Extra Days',
|
|
help='Total number of rental days.')
|
|
extra_per_km = fields.Float(string='Extra Charges / KM',
|
|
help='Rental charge per hour.',
|
|
related='vehicle_id.charge_kilometer')
|
|
|
|
total_extra_km = fields.Float(string='Total Extra K/M',
|
|
help="Total Extra K/M taken")
|
|
total_extra_charge = fields.Float(string="Total Extra Charge",
|
|
help="Extra Charges per K/M",
|
|
compute='_compute_total_extra_charge'
|
|
, store=True)
|
|
|
|
rental_payment_plan_ids = (fields.One2many
|
|
('rental.payment.plan',
|
|
'contract_id',
|
|
string="Vehicle Payment Details",
|
|
help="Details of the paymentplans for the"
|
|
" vehicle rental."))
|
|
extra_service_ids = (fields.One2many
|
|
('extra.service',
|
|
'contract_id',
|
|
string="Extra Services",
|
|
help="List of extra services associated with this"
|
|
"vehicle rental."
|
|
))
|
|
is_extra_invoice_created = fields.Boolean(
|
|
string='Extra Invoice Created',
|
|
help="Indicates whether an extra invoice has been created for the"
|
|
" extra services.")
|
|
image_ids = fields.One2many(
|
|
'multi.image',
|
|
'contract_id',
|
|
string="Images of the Vehicle",
|
|
help="Images related to the Vehicles of vehicle rental."
|
|
)
|
|
insurance_ids = fields.One2many(
|
|
'insurance.policy',
|
|
'contract_id',
|
|
string="Insurance Policy",
|
|
help="Insurance policies associated with the vehicle rental.")
|
|
|
|
vehicle_to_invoice_count = fields.Integer(
|
|
string='Number of vehicle rent to invoice',
|
|
compute='_compute_vehicle_to_invoice',
|
|
readonly=True,
|
|
help="Number of vehicle rental invoices that is created."
|
|
)
|
|
is_damaged_invoiced = fields.Boolean(
|
|
string='Damage Invoice Created',
|
|
help="Indicates whether an extra invoice has been created for the"
|
|
" extra services.")
|
|
cancellation_policy_id = fields.Many2one(
|
|
'cancellation.policy', string='Cancellation Policy',
|
|
help='Select the cancellation policy applicable for this record. '
|
|
'The cancellation charges will be calculated based on the '
|
|
'selected policy.')
|
|
cancellation_charge = fields.Float(string='Cancellation Charge')
|
|
|
|
cancellation_terms = fields.Text(
|
|
string='Cancellation Terms and Conditions',
|
|
related="cancellation_policy_id.terms_conditions",
|
|
readonly=False)
|
|
is_cancelled_invoiced = fields.Boolean(
|
|
string='Cancelled Invoice Created',
|
|
help="Indicates whether an cancelled invoice has been created for the"
|
|
"Cancellation Policy.")
|
|
digital_sign = fields.Binary(string='Signature', help="Binary field to "
|
|
"store digital "
|
|
"signatures.")
|
|
sign_date = fields.Datetime(
|
|
string='Sign Date',
|
|
help='Date and time of the signature signed.')
|
|
currency_id = fields.Many2one(
|
|
'res.currency', 'Currency',
|
|
default=lambda self: self.env.user.company_id.currency_id.id,
|
|
help="if you select this currency bidding will be on that currency "
|
|
"itself")
|
|
damage_description = fields.Text(string="Damage Description")
|
|
damage_amount = fields.Float(string="Damage Amount",
|
|
help="Total Amount for the damages")
|
|
# Responsible
|
|
responsible_id = fields.Many2one(
|
|
'res.users', string='Responsible',
|
|
help='User responsible for managing the rental contract.',
|
|
required=True, tracking=True)
|
|
|
|
@api.onchange('pickup_state_id')
|
|
def _onchange_pickup_state(self):
|
|
"""
|
|
Automatically updates the 'Pick-Up Country' field based on the selected 'Pick-Up State'.
|
|
|
|
When a user selects a state for vehicle pick-up, this method fetches the corresponding
|
|
country from the selected state and sets it in the 'Pick-Up Country' field.
|
|
"""
|
|
if self.pickup_state_id:
|
|
self.pickup_country_id = self.pickup_state_id.country_id
|
|
|
|
@api.onchange('dropoff_state_id')
|
|
def _onchange_dropoff_state(self):
|
|
"""
|
|
Automatically updates the 'Drop-Off Country' field based on the selected 'Drop-Off State'.
|
|
|
|
When a user selects a state for vehicle drop-off, this method fetches the corresponding
|
|
country from the selected state and sets it in the 'Drop-Off Country' field.
|
|
"""
|
|
if self.dropoff_state_id:
|
|
self.dropoff_country_id = self.dropoff_state_id.country_id
|
|
|
|
def _group_expand_states(self, states, domain, order):
|
|
"""
|
|
Expands the available group states for selection.
|
|
"""
|
|
return ['new', 'in_progress', 'return', 'cancel']
|
|
|
|
@api.model
|
|
def create(self, vals_list):
|
|
"""
|
|
Override the create method to set a default sequence number if not
|
|
provided.
|
|
"""
|
|
if vals_list.get('name', 'New') == 'New':
|
|
vals_list['name'] = self.env['ir.sequence'].next_by_code(
|
|
'vehicle.sequence') or 'New'
|
|
return super().create(vals_list)
|
|
|
|
@api.depends('extra_per_hour', 'total_extra_hours', 'extra_per_day',
|
|
'total_extra_days', 'extra_per_km', 'total_extra_km')
|
|
def _compute_total_extra_charge(self):
|
|
"""
|
|
Compute the total extra charge based on the rent type and extra usage
|
|
(hours, days, kilometers).
|
|
"""
|
|
for record in self:
|
|
if record.rent_type == 'hours':
|
|
record.total_extra_charge = (record.extra_per_hour *
|
|
record.total_extra_hours)
|
|
elif record.rent_type == 'days':
|
|
record.total_extra_charge = (record.extra_per_day *
|
|
record.total_extra_days)
|
|
elif record.rent_type == 'kilometers':
|
|
record.total_extra_charge = (
|
|
record.extra_per_km * record.total_extra_km)
|
|
else:
|
|
record.total_extra_charge = 0
|
|
|
|
@api.depends(
|
|
'rent_type',
|
|
'rent_per_hour', 'total_hours',
|
|
'rent_per_day', 'total_days',
|
|
'rent_per_km', 'total_km',
|
|
'driver_charge', 'charge_type',
|
|
'driver_required'
|
|
)
|
|
def _compute_total_rental_charge(self):
|
|
"""
|
|
Compute the total rental charge based on the rent type and usage
|
|
(hours, days, kilometers). Include driver charge if applicable.
|
|
"""
|
|
for record in self:
|
|
if record.rent_type == 'hours':
|
|
record.total_rental_charge = (
|
|
record.rent_per_hour * record.total_hours)
|
|
elif record.rent_type == 'days':
|
|
record.total_rental_charge = (
|
|
record.rent_per_day * record.total_days)
|
|
elif record.rent_type == 'kilometers':
|
|
record.total_rental_charge = (
|
|
record.rent_per_km * record.total_km)
|
|
else:
|
|
record.total_rental_charge = 0
|
|
if record.charge_type == 'including' and record.driver_required:
|
|
record.total_rental_charge += record.driver_charge
|
|
|
|
def action_create_extra_invoice(self):
|
|
"""
|
|
Create an invoice for extra charges incurred during the rental period.
|
|
"""
|
|
product_id = self.env.ref(
|
|
'advanced_fleet_rental.product_product_vehicle_extra_rental_charge')
|
|
invoice_vals = {
|
|
'partner_id': self.customer_id.id,
|
|
'move_type': 'out_invoice',
|
|
'vehicle_rental_id': self.id,
|
|
'invoice_date': date.today(),
|
|
'invoice_line_ids': [(0, 0, {
|
|
'product_id': product_id.id,
|
|
'name': product_id.name,
|
|
'quantity': 1,
|
|
'price_unit': self.total_extra_charge,
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(invoice_vals)
|
|
invoice.action_post()
|
|
|
|
def action_installment(self):
|
|
"""
|
|
Generate the rental payment plan based on the selected payment type.
|
|
"""
|
|
self.ensure_one()
|
|
self.rental_payment_plan_ids.unlink()
|
|
if self.rent_type == 'kilometers' and self.total_km == 0:
|
|
raise ValidationError(
|
|
_('If the rent type is "kilometers", the total '
|
|
'kilometers cannot be 0.'))
|
|
if self.rent_type == 'hours' and self.payment_type != 'full':
|
|
raise ValidationError(
|
|
_('If the rent type is "hours", the payment type must be '
|
|
'"full".'))
|
|
if self.rent_type == 'kilometers' and self.payment_type != 'full':
|
|
raise ValidationError(
|
|
_('If the rent type is "kilometers", the payment type must be '
|
|
'"full".'))
|
|
if (self.rent_type == 'days' and self.payment_type == 'weekly'
|
|
and self.total_days < 7):
|
|
raise ValidationError(_(
|
|
'The total days are less than a week. '
|
|
'Please select a valid payment type.'))
|
|
if (self.rent_type == 'days' and self.payment_type == 'monthly'
|
|
and self.total_days < 30):
|
|
raise ValidationError(_(
|
|
'The total days are less than a month. '
|
|
'Please select a valid payment type.'))
|
|
pick_up = self.pickup_date
|
|
drop_date = self.dropoff_date
|
|
total_amount = self.total_rental_charge
|
|
|
|
if self.payment_type == 'full':
|
|
self.env['rental.payment.plan'].create({
|
|
'contract_id': self.id,
|
|
'invoice_item_id': self.invoice_item_id.id,
|
|
'payment_date': pick_up,
|
|
'payment_amount': total_amount,
|
|
'payment_state': 'not_paid',
|
|
})
|
|
return
|
|
|
|
# Calculate interval and number of installments based on rent_type
|
|
if self.rent_type == 'hours':
|
|
amount_per_unit = self.rent_per_hour
|
|
base_interval = relativedelta(hours=1)
|
|
elif self.rent_type == 'days':
|
|
amount_per_unit = self.rent_per_day
|
|
base_interval = relativedelta(days=1)
|
|
|
|
if self.payment_type == 'daily':
|
|
payment_interval = relativedelta(days=1)
|
|
current_date = pick_up
|
|
elif self.payment_type == 'weekly':
|
|
payment_interval = relativedelta(weeks=1)
|
|
current_date = pick_up
|
|
elif self.payment_type == 'monthly':
|
|
payment_interval = relativedelta(months=1)
|
|
current_date = pick_up + payment_interval
|
|
else:
|
|
payment_interval = base_interval
|
|
while current_date < drop_date:
|
|
next_date = min(current_date + payment_interval, drop_date)
|
|
|
|
# Calculate units in this payment period
|
|
if self.rent_type == 'hours':
|
|
units_in_period = (
|
|
next_date - current_date).total_seconds() / 3600
|
|
elif self.rent_type == 'days':
|
|
units_in_period = (next_date - current_date).days
|
|
|
|
installment_amount = units_in_period * amount_per_unit
|
|
|
|
if installment_amount > 0:
|
|
self.env['rental.payment.plan'].create({
|
|
'contract_id': self.id,
|
|
'invoice_item_id': self.invoice_item_id.id,
|
|
'payment_date': current_date,
|
|
'payment_amount': installment_amount,
|
|
'payment_state': 'not_paid',
|
|
})
|
|
|
|
current_date = next_date
|
|
# Handle any remaining amount due to rounding
|
|
remaining_amount = total_amount - sum(
|
|
self.rental_payment_plan_ids.mapped('payment_amount'))
|
|
if remaining_amount > 0:
|
|
self.env['rental.payment.plan'].create({
|
|
'contract_id': self.id,
|
|
'invoice_item_id': self.invoice_item_id.id,
|
|
'payment_date': drop_date,
|
|
'payment_amount': remaining_amount,
|
|
'payment_state': 'not_paid',
|
|
})
|
|
|
|
@api.depends('pickup_date', 'dropoff_date')
|
|
def _compute_rental_period(self):
|
|
"""
|
|
Compute the total rental period in hours and days based on pickup
|
|
and drop-off dates.
|
|
"""
|
|
for record in self:
|
|
if record.pickup_date and record.dropoff_date:
|
|
pickup = fields.Datetime.from_string(record.pickup_date)
|
|
dropoff = fields.Datetime.from_string(record.dropoff_date)
|
|
delta = dropoff - pickup
|
|
|
|
# Calculate total days
|
|
total_days = delta.days + 1
|
|
record.total_days = total_days
|
|
|
|
# Calculate total hours
|
|
total_hours = delta.total_seconds() / 3600
|
|
record.total_hours = total_hours
|
|
|
|
else:
|
|
record.total_days = 0
|
|
record.total_hours = 0
|
|
|
|
@api.constrains('rent_type', 'pickup_date', 'dropoff_date', 'total_hours',
|
|
'total_days', )
|
|
def _check_rental_period(self):
|
|
"""
|
|
Ensure the drop-off date is not before the pick-up date.
|
|
"""
|
|
for record in self:
|
|
if record.pickup_date and record.dropoff_date:
|
|
pickup = fields.Datetime.from_string(record.pickup_date)
|
|
dropoff = fields.Datetime.from_string(record.dropoff_date)
|
|
delta = dropoff - pickup
|
|
|
|
if record.rent_type == 'hours':
|
|
total_hours_computed = delta.total_seconds() / 3600
|
|
if record.total_hours > total_hours_computed:
|
|
raise ValidationError(
|
|
f'The total hours ({record.total_hours})'
|
|
f' exceed the period between the pickup '
|
|
f'and dropoff dates.')
|
|
if record.total_hours == 0:
|
|
raise ValidationError(
|
|
f'The total hours cannot be zero.')
|
|
if record.rent_type == 'days':
|
|
total_days_computed = delta.days + 1
|
|
if record.total_days > total_days_computed:
|
|
raise ValidationError(
|
|
f'The total days ({record.total_days})'
|
|
f' exceed the period between the pickup '
|
|
f'and dropoff dates.')
|
|
if record.total_days == 0:
|
|
raise ValidationError(
|
|
f'The total days cannot be zero.')
|
|
|
|
def action_account_tab(self):
|
|
"""View the Invoices in the Smart Tab."""
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': 'Invoices',
|
|
'res_model': 'account.move',
|
|
'target': 'current',
|
|
'domain': [('partner_id', '=', self.customer_id.id),
|
|
('vehicle_rental_id', '=', self.id)],
|
|
'view_mode': 'tree,form',
|
|
}
|
|
|
|
def _compute_vehicle_to_invoice(self):
|
|
"""
|
|
Computes the number of invoices related to the vehicle rental
|
|
contract and updates the 'vehicle_to_invoice_count' field for each record.
|
|
"""
|
|
for record in self:
|
|
record.vehicle_to_invoice_count = self.env[
|
|
'account.move'].search_count([
|
|
('vehicle_rental_id', '=', record.id)
|
|
])
|
|
|
|
def action_extra_invoice_charge(self):
|
|
"""
|
|
Creates an invoice for extra services added to the vehicle
|
|
rental contract. If there are no extra services, raises a
|
|
ValidationError.
|
|
"""
|
|
if self.extra_service_ids:
|
|
invoice_line_vals = [
|
|
{
|
|
'product_id': line.product_id.id,
|
|
'name': line.description or line.product_id.name,
|
|
'quantity': line.quantity,
|
|
'price_unit': line.amount,
|
|
}
|
|
for line in self.extra_service_ids
|
|
]
|
|
invoice_vals = {
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.customer_id.id,
|
|
'invoice_line_ids': [(0, 0, line) for line in
|
|
invoice_line_vals],
|
|
'vehicle_rental_id': self.id,
|
|
}
|
|
invoice = self.env['account.move'].create(invoice_vals)
|
|
invoice.action_post()
|
|
self.is_extra_invoice_created = True
|
|
else:
|
|
raise ValidationError(
|
|
_('Add Extra Services Products.'))
|
|
|
|
def action_damage_invoice(self):
|
|
"""
|
|
This method opens a new window to link invoices and remove invoices
|
|
for the current sale order.
|
|
"""
|
|
return {
|
|
"type": "ir.actions.act_window",
|
|
"name": "Damage Invoices",
|
|
"view_mode": "form",
|
|
"res_model": "damage.invoice",
|
|
"target": "new",
|
|
"context": {
|
|
"default_contract_id": self.id,
|
|
},
|
|
}
|
|
|
|
def action_cancel(self):
|
|
"""
|
|
Cancels the rental contract by setting its state to 'cancel'.
|
|
"""
|
|
self.write({'state': 'cancel'})
|
|
|
|
def action_cancel_charges(self):
|
|
"""
|
|
Creates an invoice for the cancellation charges based on the contract's
|
|
cancellation policy. The invoice is created using a predefined product
|
|
for cancellation charges. If the cancellation policy is not set,
|
|
it raises a ValidationError.
|
|
"""
|
|
if self.cancellation_policy_id:
|
|
product_id = self.env.ref(
|
|
'advanced_fleet_rental.product_product_vehicle_cancel_charge')
|
|
invoice_vals = {
|
|
'partner_id': self.customer_id.id,
|
|
'move_type': 'out_invoice',
|
|
'vehicle_rental_id': self.id,
|
|
'invoice_date': date.today(),
|
|
'invoice_line_ids': [(0, 0, {
|
|
'product_id': product_id.id,
|
|
'name': self.cancellation_terms,
|
|
'quantity': 1,
|
|
'price_unit': self.cancellation_charge,
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(invoice_vals)
|
|
self.is_cancelled_invoiced = True
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'account.move',
|
|
'view_mode': 'form',
|
|
'res_id': invoice.id,
|
|
'target': 'current',
|
|
}
|
|
else:
|
|
raise ValidationError(
|
|
_("No cancellation policy set on the contract."))
|
|
|