From 651511b1d8dd02909f64758abad698a8468b5545 Mon Sep 17 00:00:00 2001 From: AjmalCybro Date: Thu, 19 Oct 2023 10:37:21 +0530 Subject: [PATCH] Oct 19 : [FIX] Bug Fixed 'fleet_rental' --- fleet_rental/__manifest__.py | 2 +- fleet_rental/doc/RELEASE_NOTES.md | 7 +- fleet_rental/models/__init__.py | 6 + fleet_rental/models/account_move.py | 47 ++ fleet_rental/models/account_move_line.py | 37 ++ fleet_rental/models/car_rental.py | 307 +++++----- fleet_rental/models/car_rental_checklist.py | 40 ++ fleet_rental/models/car_tools.py | 30 + fleet_rental/models/fleet.py | 22 - fleet_rental/models/fleet_rental_line.py | 55 ++ fleet_rental/models/fleet_vehicle.py | 45 ++ fleet_rental/models/res_config_settings.py | 12 +- fleet_rental/reports/rental_report.py | 24 +- fleet_rental/reports/rental_report.xml | 3 - .../description/images/screenshot-12.png | Bin 37500 -> 0 bytes fleet_rental/static/description/index.html | 565 +++++++----------- 16 files changed, 690 insertions(+), 512 deletions(-) create mode 100644 fleet_rental/models/account_move.py create mode 100644 fleet_rental/models/account_move_line.py create mode 100644 fleet_rental/models/car_rental_checklist.py create mode 100644 fleet_rental/models/car_tools.py create mode 100644 fleet_rental/models/fleet_rental_line.py create mode 100644 fleet_rental/models/fleet_vehicle.py delete mode 100644 fleet_rental/static/description/images/screenshot-12.png diff --git a/fleet_rental/__manifest__.py b/fleet_rental/__manifest__.py index b477c0480..38b3ec36f 100755 --- a/fleet_rental/__manifest__.py +++ b/fleet_rental/__manifest__.py @@ -22,7 +22,7 @@ { 'name': 'Fleet Rental Management', - 'version': '15.0.2.0.0', + 'version': '15.0.2.1.0', 'category': "Industries", 'summary': """This module will helps you to give the vehicles for Rent.""", 'description': "Module Helps You To Manage Rental Contracts, Odoo13, Odoo 13, Fleet, Rental, Rent, Vehicle management", diff --git a/fleet_rental/doc/RELEASE_NOTES.md b/fleet_rental/doc/RELEASE_NOTES.md index 9c2057c6d..1cec69e15 100755 --- a/fleet_rental/doc/RELEASE_NOTES.md +++ b/fleet_rental/doc/RELEASE_NOTES.md @@ -3,12 +3,17 @@ #### 03.12.2020 #### Version 15.0.1.0.0 #### ADD + - Initial Commit for Fleet Rental Management #### 26.09.2023 #### Version 15.0.2.0.0 -#### ADD +#### UPDT - Rental fleet Invoice product selection from Configuration Settings. +#### 17.10.2023 +#### Version 15.0.2.1.0 +#### UPDT +- Reformatted code for invoice cancellation. diff --git a/fleet_rental/models/__init__.py b/fleet_rental/models/__init__.py index 3ad4f5630..1dc16a937 100755 --- a/fleet_rental/models/__init__.py +++ b/fleet_rental/models/__init__.py @@ -19,6 +19,12 @@ # If not, see . # ############################################################################# +from . import account_move +from . import account_move_line from . import car_rental +from . import car_rental_checklist +from . import car_tools from . import fleet +from . import fleet_rental_line +from . import fleet_vehicle from . import res_config_settings diff --git a/fleet_rental/models/account_move.py b/fleet_rental/models/account_move.py new file mode 100644 index 000000000..67794b03d --- /dev/null +++ b/fleet_rental/models/account_move.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class AccountMove(models.Model): + """Inherit account.move to add extra field """ + _inherit = 'account.move' + + fleet_rent_id = fields.Many2one('car.rental.contract', + string='Rental', + help='Invoice related to which ' + 'rental record') + + def button_cancel(self): + """ + Override the base method button_cancel to handle additional logic + for 'car.rental.contract' model based on 'fleet_rent_id'. + """ + res = super().button_cancel() + fleet_model = self.env['car.rental.contract'].search( + [('id', '=', self.fleet_rent_id.id)]) + if fleet_model.state == 'running': + fleet_model.state = 'running' + fleet_model.first_invoice_created = False + else: + fleet_model.state = 'checking' + return res diff --git a/fleet_rental/models/account_move_line.py b/fleet_rental/models/account_move_line.py new file mode 100644 index 000000000..ef70fa560 --- /dev/null +++ b/fleet_rental/models/account_move_line.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, models + + +class AccountMoveLine(models.Model): + """Inherit account.move.line""" + _inherit = 'account.move.line' + + @api.onchange('price_unit') + def _onchange_price_unit(self): + """ + Update the 'first_payment' field of the associated + 'car.rental.contract' model when the 'price_unit' field changes. + """ + fleet_model = self.move_id.fleet_rent_id + if fleet_model: + fleet_model.first_payment = self.price_unit diff --git a/fleet_rental/models/car_rental.py b/fleet_rental/models/car_rental.py index 790231164..97d2ae8d8 100755 --- a/fleet_rental/models/car_rental.py +++ b/fleet_rental/models/car_rental.py @@ -19,10 +19,9 @@ # If not, see . # ############################################################################# - from datetime import datetime, date, timedelta -from odoo import models, fields, api, _ -from odoo.exceptions import UserError +from odoo import api, fields, models, _ +from odoo.exceptions import UserError, Warning class CarRentalContract(models.Model): @@ -32,12 +31,17 @@ class CarRentalContract(models.Model): @api.onchange('rent_start_date', 'rent_end_date') def check_availability(self): + """ + Check the availability of rental vehicles based on the provided + 'rent_start_date' and 'rent_end_date'. + """ self.vehicle_id = '' fleet_obj = self.env['fleet.vehicle'].search([]) for i in fleet_obj: for each in i.rental_reserved_time: - if str(each.date_from) <= str(self.rent_start_date) <= str(each.date_to): + if str(each.date_from) <= str(self.rent_start_date) <= str( + each.date_to): i.write({'rental_check_availability': False}) elif str(self.rent_start_date) < str(each.date_from): if str(each.date_from) <= str(self.rent_end_date) <= str( @@ -50,23 +54,25 @@ class CarRentalContract(models.Model): else: i.write({'rental_check_availability': True}) - image = fields.Binary(related='vehicle_id.image_128', string="Image of Vehicle") - reserved_fleet_id = fields.Many2one('rental.fleet.reserved', invisible=True, - copy=False) - name = fields.Char(string="Name", default="Draft Contract", readonly=True, copy=False) - customer_id = fields.Many2one('res.partner', required=True, string='Customer', - help="Customer") - vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", required=True, - help="Vehicle", + image = fields.Binary(related='vehicle_id.image_128', + string="Image of Vehicle") + reserved_fleet_id = fields.Many2one('rental.fleet.reserved', + invisible=True, copy=False) + name = fields.Char(string="Name", default="Draft Contract", readonly=True, + copy=False) + customer_id = fields.Many2one('res.partner', required=True, + string='Customer', help="Customer") + vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", + required=True, help="Vehicle", readonly=True, states={'draft': [('readonly', False)]} ) - car_brand = fields.Many2one('fleet.vehicle.model.brand', string="Fleet Brand", - size=50, - related='vehicle_id.model_id.brand_id', store=True, - readonly=True) - car_color = fields.Char(string="Fleet Color", size=50, related='vehicle_id.color', - store=True, copy=False, + car_brand = fields.Many2one('fleet.vehicle.model.brand', + string="Fleet Brand", size=50, + related='vehicle_id.model_id.brand_id', + store=True, readonly=True) + car_color = fields.Char(string="Fleet Color", size=50, + related='vehicle_id.color', store=True, copy=False, default='#FFFFFF', readonly=True) cost = fields.Float(string="Rent Cost", help="This fields is to determine the cost of rent", @@ -88,54 +94,73 @@ class CarRentalContract(models.Model): cost_generated = fields.Float(string='Recurring Cost', help="Costs paid at regular intervals, depending on the cost frequency") cost_frequency = fields.Selection( - [('no', 'No'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), + [('no', 'No'), ('daily', 'Daily'), ('weekly', 'Weekly'), + ('monthly', 'Monthly'), ('yearly', 'Yearly')], string="Recurring Cost Frequency", - help='Frequency of the recurring cost', required=True, default='no') + help='Frequency of the recurring cost', required=True) journal_type = fields.Many2one('account.journal', 'Journal', default=lambda self: self.env[ - 'account.journal'].search([('id', '=', 1)])) + 'account.journal'].search( + [('id', '=', 1)])) account_type = fields.Many2one('account.account', 'Account', default=lambda self: self.env[ - 'account.account'].search([('id', '=', 17)])) - recurring_line = fields.One2many('fleet.rental.line', 'rental_number', readonly=True, - help="Recurring Invoices", + 'account.account'].search( + [('id', '=', 17)])) + recurring_line = fields.One2many('fleet.rental.line', 'rental_number', + readonly=True, help="Recurring Invoices", copy=False) first_payment = fields.Float(string='First Payment', - help="Transaction/Office/Contract charge amount, must paid by customer side other " + help="Transaction/Office/Contract charge " + "amount, must paid by customer side " + "other " "than recurrent payments", track_visibility='onchange', required=True) first_payment_inv = fields.Many2one('account.move', copy=False) - first_invoice_created = fields.Boolean(string="First Invoice Created", invisible=True, - copy=False) + first_invoice_created = fields.Boolean(string="First Invoice Created", + invisible=True, copy=False) attachment_ids = fields.Many2many('ir.attachment', 'car_rent_checklist_ir_attachments_rel', - 'rental_id', 'attachment_id', string="Attachments", - help="Images of the vehicle before contract/any attachments") - checklist_line = fields.One2many('car.rental.checklist', 'checklist_number', - string="Checklist", - help="Facilities/Accessories, That should verify when closing the contract.", + 'rental_id', 'attachment_id', + string="Attachments", + help="Images of the vehicle before " + "contract/any attachments") + checklist_line = fields.One2many('car.rental.checklist', + 'checklist_number', string="Checklist", + help="Facilities/Accessories, That should" + " verify when closing the contract.", states={'invoice': [('readonly', True)], 'done': [('readonly', True)], 'cancel': [('readonly', True)]}) - total = fields.Float(string="Total (Accessories/Tools)", readonly=True, copy=False) - tools_missing_cost = fields.Float(string="Missing Cost", readonly=True, copy=False, - help='This is the total amount of missing tools/accessories') + total = fields.Float(string="Total (Accessories/Tools)", readonly=True, + copy=False) + tools_missing_cost = fields.Float(string="Missing Cost", readonly=True, + copy=False, + help='This is the total amount of ' + 'missing tools/accessories') damage_cost = fields.Float(string="Damage Cost", copy=False) - damage_cost_sub = fields.Float(string="Damage Cost", readonly=True, copy=False) - total_cost = fields.Float(string="Total", readonly=True, copy=False) - invoice_count = fields.Integer(compute='_invoice_count', string='# Invoice', + damage_cost_sub = fields.Float(string="Damage Cost", readonly=True, copy=False) + total_cost = fields.Float(string="Total", readonly=True, copy=False) + invoice_count = fields.Integer(compute='_invoice_count', + string='# Invoice', copy=False) check_verify = fields.Boolean(compute='check_action_verify', copy=False) sales_person = fields.Many2one('res.users', string='Sales Person', default=lambda self: self.env.uid, track_visibility='always') def action_run(self): + """ + Set the state of the object to 'running'. + """ self.state = 'running' @api.depends('checklist_line.checklist_active') def check_action_verify(self): + """ + Update the 'check_verify' field based on the value of + 'checklist_active' in 'checklist_line'. + """ flag = 0 for each in self: for i in each.checklist_line: @@ -150,29 +175,43 @@ class CarRentalContract(models.Model): @api.constrains('rent_start_date', 'rent_end_date') def validate_dates(self): + """ + Check the validity of the 'rent_start_date' and 'rent_end_date' + fields. + Raise a warning if 'rent_end_date' is earlier than + 'rent_start_date'. + """ if self.rent_end_date < self.rent_start_date: - raise UserError("Please select the valid end date.") + raise Warning("Please select the valid end date.") def set_to_done(self): + """ + Set the state of the object to 'done' based on certain conditions related to invoices. + Raise a UserError if certain invoices are pending or the total cost is zero. + """ invoice_ids = self.env['account.move'].search( - [('invoice_origin', '=', self.name)]) - f = 0 - for each in invoice_ids: - if each.payment_state != 'paid': - f = 1 - break - if f == 0: + [('fleet_rent_id', '=', self.id), + ('amount_total_signed', '=', self.total_cost),('invoice_line_ids.name','=','Damage/Tools missing cost')]) + if any(each.payment_state == 'paid' for each in invoice_ids) or self.total_cost == 0: + print('kkkk') self.state = 'done' else: raise UserError("Some Invoices are pending") def _invoice_count(self): - invoice_ids = self.env['account.move'].search( - [('invoice_origin', '=', self.name)]) - self.invoice_count = len(invoice_ids) + """ + Calculate the count of invoices related to the current object. + Update the 'invoice_count' field accordingly. + """ + self.invoice_count = self.env['account.move'].search_count( + [('fleet_rent_id', '=', self.id)]) @api.constrains('state') def state_changer(self): + """ + Handle state transitions and update the 'state_id' of the + associated vehicle based on the value of the 'state' field. + """ if self.state == "running": state_id = self.env.ref('fleet_rental.vehicle_state_rent').id self.vehicle_id.write({'state_id': state_id}) @@ -186,6 +225,10 @@ class CarRentalContract(models.Model): @api.constrains('checklist_line', 'damage_cost') def total_updater(self): + """ + Update various fields related to totals based on the values in + 'checklist_line', 'damage_cost', and other relevant fields. + """ total = 0.0 tools_missing_cost = 0.0 for records in self.checklist_line: @@ -198,11 +241,13 @@ class CarRentalContract(models.Model): self.total_cost = tools_missing_cost + self.damage_cost def fleet_scheduler1(self, rent_date): + """ + Perform actions related to fleet scheduling, including creating + invoices, managing recurring data, and sending email notifications. + """ inv_obj = self.env['account.move'] inv_line_obj = self.env['account.move.line'] recurring_obj = self.env['fleet.rental.line'] - start_date = datetime.strptime(str(self.rent_start_date), '%Y-%m-%d').date() - end_date = datetime.strptime(str(self.rent_end_date), '%Y-%m-%d').date() supplier = self.customer_id inv_data = { 'ref': supplier.name, @@ -266,6 +311,10 @@ class CarRentalContract(models.Model): @api.model def fleet_scheduler(self): + """ + Perform fleet scheduling operations, including creating invoices, + managing recurring data, and sending email notifications. + """ inv_obj = self.env['account.move'] inv_line_obj = self.env['account.move.line'] recurring_obj = self.env['fleet.rental.line'] @@ -273,7 +322,8 @@ class CarRentalContract(models.Model): for records in self.search([]): start_date = datetime.strptime(str(records.rent_start_date), '%Y-%m-%d').date() - end_date = datetime.strptime(str(records.rent_end_date), '%Y-%m-%d').date() + end_date = datetime.strptime(str(records.rent_end_date), + '%Y-%m-%d').date() if end_date >= date.today(): temp = 0 if records.cost_frequency == 'daily': @@ -292,9 +342,7 @@ class CarRentalContract(models.Model): if temp == 1 and records.cost_frequency != "no" and records.state == "running": supplier = records.customer_id inv_data = { - # 'name': supplier.name, 'ref': supplier.name, - # 'account_id': supplier.property_account_payable_id.id, 'partner_id': supplier.id, 'currency_id': records.account_type.company_id.currency_id.id, 'journal_id': records.journal_type.id, @@ -345,7 +393,8 @@ class CarRentalContract(models.Model): 'Amount %s' 'Due Date %s' 'Responsible Person %s, %s') % \ - (self.customer_id.name, self.name, inv_id.amount_total, + (self.customer_id.name, self.name, + inv_id.amount_total, inv_id.invoice_date_due, inv_id.user_id.name, inv_id.user_id.mobile) @@ -364,43 +413,38 @@ class CarRentalContract(models.Model): self.state = "invoice" self.reserved_fleet_id.unlink() self.rent_end_date = fields.Date.today() + product_id = self.env['product.product'].search( + [("name", "=", "Fleet Rental Service")]) + if product_id.property_account_income_id.id: + income_account = product_id.property_account_income_id + elif product_id.categ_id.property_account_income_categ_id.id: + income_account = product_id.categ_id.property_account_income_categ_id + else: + raise UserError( + _('Please define income account for this product: "%s" (id:%d).') % ( + product_id.name, + product_id.id)) if self.total_cost != 0: - inv_obj = self.env['account.move'] - inv_line_obj = self.env['account.move.line'] supplier = self.customer_id inv_data = { 'ref': supplier.name, + 'move_type': 'out_invoice', 'partner_id': supplier.id, 'currency_id': self.account_type.company_id.currency_id.id, 'journal_id': self.journal_type.id, 'invoice_origin': self.name, + 'fleet_rent_id': self.id, 'company_id': self.account_type.company_id.id, 'invoice_date_due': self.rent_end_date, + 'invoice_line_ids': [(0, 0, { + 'name': "Damage/Tools missing cost", + 'account_id': income_account.id, + 'price_unit': self.total_cost, + 'quantity': 1, + 'product_id': product_id.id, + })] } - inv_id = inv_obj.create(inv_data) - product_id = self.env['product.product'].search( - [("name", "=", "Fleet Rental Service")]) - if product_id.property_account_income_id.id: - income_account = product_id.property_account_income_id - elif product_id.categ_id.property_account_income_categ_id.id: - income_account = product_id.categ_id.property_account_income_categ_id - else: - raise UserError( - _('Please define income account for this product: "%s" (id:%d).') % ( - product_id.name, - product_id.id)) - inv_line_data = { - 'name': "Damage/Tools missing cost", - 'account_id': income_account.id, - 'price_unit': self.total_cost, - 'quantity': 1, - 'product_id': product_id.id, - 'move_id': inv_id.id, - } - inv_line_obj.create(inv_line_data) - imd = self.env['ir.model.data'] - action = self.env.ref('account.view_move_tree') - # action = imd.xmlid_to_object('account.view_move_tree') + inv_id = self.env['account.move'].create(inv_data) list_view_id = self.env.ref('account.view_move_form', False) form_view_id = self.env.ref('account.view_move_tree', False) result = { @@ -408,7 +452,8 @@ class CarRentalContract(models.Model): 'view_mode': 'form', 'res_model': 'account.move', 'type': 'ir.actions.act_window', - 'views': [(list_view_id.id, 'tree'), (form_view_id.id, 'form')], + 'views': [(list_view_id.id, 'tree'), + (form_view_id.id, 'form')], } if len(inv_id) > 1: result['domain'] = "[('id','in',%s)]" % inv_id.ids @@ -417,6 +462,11 @@ class CarRentalContract(models.Model): return result def action_confirm(self): + """ + Confirm the rental contract, check vehicle availability, update + state to "reserved," generate a sequence code, and send a + confirmation email. + """ check_availability = 0 for each in self.vehicle_id.rental_reserved_time: if each.date_from <= self.rent_start_date <= each.date_to: @@ -439,13 +489,15 @@ class CarRentalContract(models.Model): }) self.write({'reserved_fleet_id': reserved_id.id}) else: - raise UserError('Sorry This vehicle is already booked by another customer') + raise UserError( + 'Sorry This vehicle is already booked by another customer') self.state = "reserved" sequence_code = 'car.rental.sequence' order_date = self.create_date order_date = str(order_date)[0:10] self.name = self.env['ir.sequence'] \ - .with_context(ir_sequence_date=order_date).next_by_code(sequence_code) + .with_context(ir_sequence_date=order_date).next_by_code( + sequence_code) mail_content = _( '

Order Confirmed!


Hi %s,
This is to notify that your rental contract has ' 'been confirmed.

' @@ -467,15 +519,39 @@ class CarRentalContract(models.Model): self.env['mail.mail'].create(main_content).send() def action_cancel(self): + """ + Cancel the rental contract. + Update the state to "cancel" and delete the associated reserved + fleet ID if it exists. + """ self.state = "cancel" if self.reserved_fleet_id: self.reserved_fleet_id.unlink() def force_checking(self): - self.state = "checking" + """ + Force the checking of payment status for associated invoices. + If all invoices are marked as paid, update the state to "checking." + Otherwise, raise a UserError indicating that some invoices are + pending. + """ + invoice_ids = self.env['account.move'].search( + [('fleet_rent_id', '=', self.id), + ('amount_total_signed', '=', self.first_payment)]) + if any(each.payment_state == 'paid' for each in invoice_ids): + print('kkkk') + self.state = "checking" + else: + raise UserError("Some Invoices are pending") def action_view_invoice(self): - inv_obj = self.env['account.move'].search([('invoice_origin', '=', self.name)]) + """ + Display the associated invoices for the current record. + Construct the appropriate view configurations based on the number + of invoices found. + """ + inv_obj = self.env['account.move'].search( + [('fleet_rent_id', '=', self.id)]) inv_ids = [] for each in inv_obj: inv_ids.append(each.id) @@ -493,7 +569,7 @@ class CarRentalContract(models.Model): } else: value = { - 'domain': str([('id', 'in', inv_ids)]), + 'domain': [('fleet_rent_id', '=', self.id)], 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'account.move', @@ -501,10 +577,16 @@ class CarRentalContract(models.Model): 'type': 'ir.actions.act_window', 'name': _('Invoice'), } - return value def action_invoice_create(self): + """ + Create an invoice for the rental contract. + Calculate the rental duration and iterate over each day to create + invoices. + Create the first payment invoice, add relevant invoice line data, + and send an email notification for the received payment. + """ for each in self: rent_date = self.rent_start_date if each.cost_frequency != 'no' and rent_date < date.today(): @@ -524,10 +606,8 @@ class CarRentalContract(models.Model): rent_date = rent_date + timedelta(days=7) if each.cost_frequency == 'monthly': rent_date = rent_date + timedelta(days=30) - self.first_invoice_created = True inv_obj = self.env['account.move'] - inv_line_obj = self.env['account.move.line'] supplier = self.customer_id inv_data = { 'ref': supplier.name, @@ -536,16 +616,14 @@ class CarRentalContract(models.Model): 'currency_id': self.account_type.company_id.currency_id.id, 'journal_id': self.journal_type.id, 'invoice_origin': self.name, + 'fleet_rent_id': self.id, 'company_id': self.account_type.company_id.id, 'invoice_date_due': self.rent_end_date, } inv_id = inv_obj.create(inv_data) self.first_payment_inv = inv_id.id - fleet_rental_product_id = int( - self.env['ir.config_parameter'].sudo().get_param( - 'fleet_service_product_id')) - product_id = self.env['product.template'].sudo().browse( - fleet_rental_product_id).product_variant_id + product_id = self.env['product.product'].search( + [("name", "=", "Fleet Rental Service")]) if product_id.property_account_income_id.id: income_account = product_id.property_account_income_id.id elif product_id.categ_id.property_account_income_categ_id.id: @@ -581,9 +659,7 @@ class CarRentalContract(models.Model): 'email_to': self.customer_id.email, } self.env['mail.mail'].create(main_content).send() - imd = self.env['ir.model.data'] action = self.env.ref('account.action_move_out_invoice_type') - result = { 'name': action.name, 'type': 'ir.actions.act_window', @@ -595,44 +671,7 @@ class CarRentalContract(models.Model): return result -class FleetRentalLine(models.Model): - _name = 'fleet.rental.line' - - name = fields.Char('Description') - date_today = fields.Date('Date') - account_info = fields.Char('Account') - recurring_amount = fields.Float('Amount') - rental_number = fields.Many2one('car.rental.contract', string='Rental Number') - payment_info = fields.Char(compute='paid_info', string='Payment Stage', - default='draft') - invoice_number = fields.Integer(string='Invoice ID') - invoice_ref = fields.Many2one('account.move', string='Invoice Ref') - date_due = fields.Date(string='Due Date', related='invoice_ref.invoice_date_due') - - def paid_info(self): - for each in self: - if self.env['account.move'].browse(each.invoice_number): - each.payment_info = self.env['account.move'].browse( - each.invoice_number).state - else: - each.payment_info = 'Record Deleted' - - -class CarRentalChecklist(models.Model): - _name = 'car.rental.checklist' - - name = fields.Many2one('car.tools', string="Name") - checklist_active = fields.Boolean(string="Available", default=True) - checklist_number = fields.Many2one('car.rental.contract', string="Checklist Number") - price = fields.Float(string="Price") - @api.onchange('name') - def onchange_name(self): - self.price = self.name.price -class CarTools(models.Model): - _name = 'car.tools' - name = fields.Char(string="Name") - price = fields.Float(string="Price") diff --git a/fleet_rental/models/car_rental_checklist.py b/fleet_rental/models/car_rental_checklist.py new file mode 100644 index 000000000..c64159ae6 --- /dev/null +++ b/fleet_rental/models/car_rental_checklist.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models + + +class CarRentalChecklist(models.Model): + """Model to add the checklist of rental""" + _name = 'car.rental.checklist' + + name = fields.Many2one('car.tools', string="Name") + checklist_active = fields.Boolean(string="Available", default=True) + checklist_number = fields.Many2one('car.rental.contract', + string="Checklist Number") + price = fields.Float(string="Price") + + @api.onchange('name') + def onchange_name(self): + """ + Update the price based on the selected name. + """ + self.price = self.name.price diff --git a/fleet_rental/models/car_tools.py b/fleet_rental/models/car_tools.py new file mode 100644 index 000000000..57c042cfc --- /dev/null +++ b/fleet_rental/models/car_tools.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class CarTools(models.Model): + """Model to add the tools for the car""" + _name = 'car.tools' + + name = fields.Char(string="Name") + price = fields.Float(string="Price") diff --git a/fleet_rental/models/fleet.py b/fleet_rental/models/fleet.py index 92f13575a..e3b484897 100755 --- a/fleet_rental/models/fleet.py +++ b/fleet_rental/models/fleet.py @@ -19,7 +19,6 @@ # If not, see . # ############################################################################# - from odoo import models, fields @@ -31,24 +30,3 @@ class FleetReservedTime(models.Model): date_from = fields.Date(string='Reserved Date From') date_to = fields.Date(string='Reserved Date To') reserved_obj = fields.Many2one('fleet.vehicle') - - -class EmployeeFleet(models.Model): - _inherit = 'fleet.vehicle' - - rental_check_availability = fields.Boolean(default=True, copy=False) - color = fields.Char(string='Color', default='#FFFFFF') - rental_reserved_time = fields.One2many('rental.fleet.reserved', 'reserved_obj', - string='Reserved Time', readonly=1, - ondelete='cascade') - fuel_type = fields.Selection([('gasoline', 'Gasoline'), - ('diesel', 'Diesel'), - ('electric', 'Electric'), - ('hybrid', 'Hybrid'), - ('petrol', 'Petrol')], - 'Fuel Type', help='Fuel Used by the vehicle') - - _sql_constraints = [ - ('vin_sn_unique', 'unique (vin_sn)', "Chassis Number already exists !"), - ('license_plate_unique', 'unique (license_plate)', - "License plate already exists !")] diff --git a/fleet_rental/models/fleet_rental_line.py b/fleet_rental/models/fleet_rental_line.py new file mode 100644 index 000000000..e57f1790f --- /dev/null +++ b/fleet_rental/models/fleet_rental_line.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class FleetRentalLine(models.Model): + _name = 'fleet.rental.line' + + name = fields.Char('Description') + date_today = fields.Date('Date') + account_info = fields.Char('Account') + recurring_amount = fields.Float('Amount') + rental_number = fields.Many2one('car.rental.contract', + string='Rental Number') + payment_info = fields.Char(compute='paid_info', string='Payment Stage', + default='draft') + invoice_number = fields.Integer(string='Invoice ID') + invoice_ref = fields.Many2one('account.move', string='Invoice Ref') + date_due = fields.Date(string='Due Date', + related='invoice_ref.invoice_date_due') + + def paid_info(self): + """ + Retrieve payment information for the current record. + Check the state of the associated invoice based on the provided + invoice number. + If the record exists, set the payment_info field to the state of + the invoice. + Otherwise, set the payment_info field to 'Record Deleted'. + """ + for each in self: + if self.env['account.move'].browse(each.invoice_number): + each.payment_info = self.env['account.move'].browse( + each.invoice_number).state + else: + each.payment_info = 'Record Deleted' diff --git a/fleet_rental/models/fleet_vehicle.py b/fleet_rental/models/fleet_vehicle.py new file mode 100644 index 000000000..3a56730e1 --- /dev/null +++ b/fleet_rental/models/fleet_vehicle.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class EmployeeFleet(models.Model): + """Inherit fleet.vehicle""" + _inherit = 'fleet.vehicle' + + rental_check_availability = fields.Boolean(default=True, copy=False) + color = fields.Char(string='Color', default='#FFFFFF') + rental_reserved_time = fields.One2many('rental.fleet.reserved', + 'reserved_obj', + string='Reserved Time', readonly=1, + ondelete='cascade') + fuel_type = fields.Selection([('gasoline', 'Gasoline'), + ('diesel', 'Diesel'), + ('electric', 'Electric'), + ('hybrid', 'Hybrid'), + ('petrol', 'Petrol')], + 'Fuel Type', help='Fuel Used by the vehicle') + + _sql_constraints = [('vin_sn_unique', 'unique (vin_sn)', + "Chassis Number already exists !"), + ('license_plate_unique', 'unique (license_plate)', + "License plate already exists !")] diff --git a/fleet_rental/models/res_config_settings.py b/fleet_rental/models/res_config_settings.py index f39606cf7..4fab4ae3b 100755 --- a/fleet_rental/models/res_config_settings.py +++ b/fleet_rental/models/res_config_settings.py @@ -27,9 +27,13 @@ class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' def _get_default_product(self): + """ + Retrieve the default product ID for fleet services. + """ return self.env.ref('fleet_rental.fleet_service_product').id - fleet_service_product_id = fields.Many2one('product.template', - string="Product", - config_parameter='fleet_service_product_id', - default=_get_default_product) + fleet_service_product_id = fields.Many2one( + 'product.template', + string="Product", + config_parameter='fleet_service_product_id', + default=_get_default_product) diff --git a/fleet_rental/reports/rental_report.py b/fleet_rental/reports/rental_report.py index 3f66ae17f..3f22db1d9 100755 --- a/fleet_rental/reports/rental_report.py +++ b/fleet_rental/reports/rental_report.py @@ -19,7 +19,6 @@ # If not, see . # ############################################################################# - from odoo import models, fields, tools @@ -36,10 +35,13 @@ class FleetRentalReport(models.Model): cost = fields.Float(string="Rent Cost") rent_start_date = fields.Date(string="Rent Start Date") rent_end_date = fields.Date(string="Rent End Date") - state = fields.Selection([('draft', 'Draft'), ('running', 'Running'), ('cancel', 'Cancel'), - ('checking', 'Checking'), ('done', 'Done')], string="State") - cost_frequency = fields.Selection([('no', 'No'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), - ('yearly', 'Yearly')], string="Recurring Cost Frequency") + state = fields.Selection( + [('draft', 'Draft'), ('running', 'Running'), ('cancel', 'Cancel'), + ('checking', 'Checking'), ('done', 'Done')], string="State") + cost_frequency = fields.Selection( + [('no', 'No'), ('daily', 'Daily'), ('weekly', 'Weekly'), + ('monthly', 'Monthly'), + ('yearly', 'Yearly')], string="Recurring Cost Frequency") total = fields.Float(string="Total(Tools)") tools_missing_cost = fields.Float(string="Tools missing cost") damage_cost = fields.Float(string="Damage cost") @@ -49,6 +51,9 @@ class FleetRentalReport(models.Model): _order = 'name desc' def _select(self): + """ + Construct a SQL select query string with specific fields. + """ select_str = """ SELECT (select 1 ) AS nbr, @@ -72,6 +77,9 @@ class FleetRentalReport(models.Model): return select_str def _group_by(self): + """ + Construct a SQL GROUP BY query string with specific fields. + """ group_by_str = """ GROUP BY t.id, @@ -94,6 +102,12 @@ class FleetRentalReport(models.Model): return group_by_str def init(self): + """ + Initialize the module and create a database view for reporting + fleet rentals. + Drop the existing 'report_fleet_rental' view if it already exists. + Create a new view with the SQL select and group by queries. + """ tools.sql.drop_view_if_exists(self._cr, 'report_fleet_rental') self._cr.execute(""" CREATE view report_fleet_rental as diff --git a/fleet_rental/reports/rental_report.xml b/fleet_rental/reports/rental_report.xml index 7ffef207e..17523a0f0 100755 --- a/fleet_rental/reports/rental_report.xml +++ b/fleet_rental/reports/rental_report.xml @@ -1,6 +1,5 @@ - report.fleet.rental.pivot @@ -12,7 +11,6 @@ - Fleet Rental Analysis report.fleet.rental @@ -22,7 +20,6 @@ Fleet Rental. - diff --git a/fleet_rental/static/description/images/screenshot-12.png b/fleet_rental/static/description/images/screenshot-12.png deleted file mode 100644 index 47a151bcf58fe0f044e8104683821f437ee7f3e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37500 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 YgYJjCq7OFn7mNL+f(D@cx%tQc55!+}4gdfE diff --git a/fleet_rental/static/description/index.html b/fleet_rental/static/description/index.html index 7db08599d..781ef9b51 100644 --- a/fleet_rental/static/description/index.html +++ b/fleet_rental/static/description/index.html @@ -1,19 +1,17 @@ -

+
+ style="border-bottom: 1px solid #d5d5d5;">
- +
+ style="background-color: #7C7BAD !important; color: #fff !important; font-weight: 600 !important; padding: 5px 15px 8px !important; margin: 0 5px !important;"> Community
+ style="background-color: #875A7B !important; color: #fff !important; font-weight: 600 !important; padding: 5px 15px 8px !important; margin: 0 5px !important;"> Enterprise
@@ -27,15 +25,12 @@
-

Fleet Rental Management -

+ style="text-align: center; padding: 1rem !important;"> +

Fleet Rental Management

- With this module you can give vehicles like car, van, bike, jeep etc. - for rent. + style="color: #212529 !important; font-size: 1.5rem !important; letter-spacing: 1px !important;"> + With this module you can give vehicles like car, van, bike, jeep etc. for rent.

@@ -45,20 +40,15 @@
+ style="text-align: center; padding: 2.5rem 1rem !important;">

Overview


-

- This module is an application for Vehicle Rental System which helps in - managing the rental of - vehicles like car,van,bike, jeep etc. It manages fleet/vehicle - property by extending the basic - fleet module of Odoo. Currently fleet module does not have any - connection with accounting - module. But in this module, we integrate the module with accounting - also. + style="border: 3px solid #AC1015 !important; background-color: #AC1015 !important; width: 80px !important; margin-bottom: 2rem !important;" /> +

+ This module is an application for Vehicle Rental System which helps in managing the rental of + vehicles like car,van,bike, jeep etc. It manages fleet/vehicle property by extending the basic + fleet module of Odoo. Currently fleet module does not have any connection with accounting + module. But in this module, we integrate the module with accounting also.

@@ -66,134 +56,118 @@
-
-

Key - Features

+
+

Key Features


+ style="border: 3px solid #AC1015 !important; background-color: #AC1015 !important; width: 80px !important; margin-bottom: 2rem !important;" />
- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

- Multiple Plans for Rental - Contract(Days/Weeks/Months/Years). + Multiple Plans for Rental Contract(Days/Weeks/Months/Years).

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Integrated with Accounting Module.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Automatically Create Recurring Invoices.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

- Sending email for confirmation, first payment and - recurrent invoices. + Sending email for confirmation, first payment and recurrent invoices.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Check List Facility.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Separate Tree view for Checklist.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Damage Checking Facility.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Billing Facility for Damages/Check Lists.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Contract Payment Validations.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Detailed Fleet Rental Analysis Report.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Access Rights From Multiple Level.

- + style="height: 60px !important; padding: 1rem 1.5rem !important; border-radius: 0 !important; margin: 1.5rem 0"> +

Flexible for further customization.

@@ -209,19 +183,16 @@
-
-

- Screenshots

+
+

Screenshots


+ style="border: 3px solid #AC1015 !important; background-color: #AC1015 !important; width: 80px !important; margin-bottom: 2rem !important;" /> -
+

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> 01

@@ -229,27 +200,23 @@ Fleet Rental -> Rental Management

- When you install the module, an extra menu named Rental - Management is created under the - Fleet Menu. Also "Fleet" menu is replaced as "Fleet Rental". - Here you can see different - color codes according to each state. This helps you in finding - out contracts easily. + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + When you install the module, an extra menu named Rental Management is created under the + Fleet Menu. Also "Fleet" menu is replaced as "Fleet Rental". Here you can see different + color codes according to each state. This helps you in finding out contracts easily.

- + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> 02

@@ -257,28 +224,25 @@ Fleet Rental -> Rental Management -> Rental Contract

- This is the Rental Contract form. You can see the Recurring - lines created according to + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + This is the Rental Contract form. You can see the Recurring lines created according to the - Recurring cost.
And also you can see all the invoices - related to this contract from + Recurring cost.
And also you can see all the invoices related to this contract from the smart button "Invoices".

- + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> 03

@@ -286,30 +250,24 @@ Checklist Tab in Rental Contract Form

- Here you can add the list of tools given with the vehicle. - When the vehicle is returned - back, the checklist can be validated and helps you to identify - the tools that are not - returned. The price of unreturned tools will be added to the - missing tool cost. The - renter have to pay that amount and you can also add damage - cost if any. Check the - damages by using the images of vehicle uploaded before the - contract. + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + Here you can add the list of tools given with the vehicle. When the vehicle is returned + back, the checklist can be validated and helps you to identify the tools that are not + returned. The price of unreturned tools will be added to the missing tool cost. The + renter have to pay that amount and you can also add damage cost if any. Check the + damages by using the images of vehicle uploaded before the contract.

- + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> 04

@@ -317,138 +275,104 @@ Checklist Easy Access

- - You can also create invoice against the checklist from here. - The checklists are those - which are in the checking state, that means the ones ready for - checking the operation. - If there is any damage or any missing tool, then you can - charge all that from customer. + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + + You can also create invoice against the checklist from here. The checklists are those + which are in the checking state, that means the ones ready for checking the operation. + If there is any damage or any missing tool, then you can charge all that from customer.

+ style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> - + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> 05

-
-

- Fleet Rental Service Product -

-

- - You can also select the Service product in Configuration Settings. -

-
-
- - - - -
-
-

- 06

-

Email Notifications

- The system will send email notification to notify the - confirmation of contract. -
- Note: You should configure outgoing and - incoming e-mail settings from + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + The system will send email notification to notify the confirmation of contract. +
+ Note: You should configure outgoing and incoming e-mail settings from your odoo for email service.

+ style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;">

The system will notify the first payment through email.

+ style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;">

The system will remind all recurrent invoices through email.

- + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

- 07

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> + 06

Contract Payment Validations

- Here you can see you have 1 invoice and this contract is in - 'Invoice' state. So you can - set this contract to done only if all the invoices are in - 'paid' state. Otherwise it + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + Here you can see you have 1 invoice and this contract is in 'Invoice' state. So you can + set this contract to done only if all the invoices are in 'paid' state. Otherwise it will raise a warning as follow.

+ style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> - + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> + -
+

- 08

+ style="font-size: 2rem !important; font-weight: 800 !important; color: #ffffff !important; background-color: #AC1015 !important; padding: 1rem !important; width: 70px !important; height: 75px !important;"> + 07

Fleet Rental Analysis Report

- You can also analyse all your fleet rentals from Fleet Rental - -> Reports -> Fleet Rental + style="color: #212529 !important; font-size: 1.3rem !important; font-weight: 300 !important;"> + You can also analyse all your fleet rentals from Fleet Rental -> Reports -> Fleet Rental Analysis.

- + style="margin-top: -15px !important; margin-top: 1rem !important; margin-bottom: 4rem !important;"> +
@@ -458,80 +382,72 @@
+ style="text-align: center; padding: 2.5rem 1rem !important;">

Suggested Products


+ style="border: 3px solid #AC1015 !important; background-color: #AC1015 !important; width: 80px !important; margin-bottom: 2rem !important;" />