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 47a151bcf..000000000 Binary files a/fleet_rental/static/description/images/screenshot-12.png and /dev/null differ 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;" />