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."))
							 | 
						|
								
							 |