From 16507985cc75a3af77fabc474acc3d2ae444152f Mon Sep 17 00:00:00 2001 From: AjmalCybro Date: Sat, 30 Dec 2023 11:49:54 +0530 Subject: [PATCH] Dec 30 : [UPDT] Bug Fixed 'fleet_rental' --- fleet_rental/__manifest__.py | 8 + fleet_rental/doc/RELEASE_NOTES.md | 6 +- fleet_rental/models/__init__.py | 10 +- fleet_rental/models/account_move.py | 47 ++ fleet_rental/models/account_move_line.py | 37 ++ fleet_rental/models/car_rental.py | 610 +++++++++++------- fleet_rental/models/car_rental_checklist.py | 40 ++ fleet_rental/models/car_tools.py | 30 + fleet_rental/models/fleet.py | 21 +- fleet_rental/models/fleet_rental_line.py | 55 ++ fleet_rental/models/fleet_vehicle.py | 45 ++ fleet_rental/models/res_config_settings.py | 39 ++ fleet_rental/reports/__init__.py | 2 +- fleet_rental/reports/rental_report.py | 25 +- fleet_rental/reports/rental_report.xml | 22 +- fleet_rental/static/src/js/time_widget.js | 32 + fleet_rental/static/src/scss/timepicker.scss | 3 + fleet_rental/static/src/xml/timepicker.xml | 9 + fleet_rental/views/car_rental_view.xml | 133 ++-- fleet_rental/views/checklist_view.xml | 17 +- .../views/res_config_settings_views.xml | 27 + 21 files changed, 894 insertions(+), 324 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 create mode 100644 fleet_rental/models/res_config_settings.py create mode 100644 fleet_rental/static/src/js/time_widget.js create mode 100644 fleet_rental/static/src/scss/timepicker.scss create mode 100644 fleet_rental/static/src/xml/timepicker.xml create mode 100644 fleet_rental/views/res_config_settings_views.xml diff --git a/fleet_rental/__manifest__.py b/fleet_rental/__manifest__.py index fa017d0fa..b272459b0 100755 --- a/fleet_rental/__manifest__.py +++ b/fleet_rental/__manifest__.py @@ -38,8 +38,16 @@ 'views/car_rental_view.xml', 'views/checklist_view.xml', 'views/car_tools_view.xml', + 'views/res_config_settings_views.xml', 'reports/rental_report.xml' ], + "assets": { + 'web.assets_backend': [ + 'fleet_rental/static/src/js/time_widget.js', + 'fleet_rental/static/src/xml/timepicker.xml', + 'fleet_rental/static/src/scss/timepicker.scss' + ], + }, 'demo': [ ], 'images': ['static/description/banner.png'], diff --git a/fleet_rental/doc/RELEASE_NOTES.md b/fleet_rental/doc/RELEASE_NOTES.md index 6be6bf475..7ebfbafe7 100755 --- a/fleet_rental/doc/RELEASE_NOTES.md +++ b/fleet_rental/doc/RELEASE_NOTES.md @@ -5,6 +5,8 @@ #### ADD Initial Commit for Fleet Rental Management +#### 22.12.2023 +#### Version 16.0.2.1.1 +#### UPDT - - +- Reformatted code for hourly payment, invoice cancellation, extend rent. diff --git a/fleet_rental/models/__init__.py b/fleet_rental/models/__init__.py index affcea1b5..1dc16a937 100755 --- a/fleet_rental/models/__init__.py +++ b/fleet_rental/models/__init__.py @@ -3,7 +3,7 @@ # # Cybrosys Technologies Pvt. Ltd. # -# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Copyright (C) 2023-TODAY Cybrosys Technologies(). # Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) # # You can modify it under the terms of the GNU AFFERO @@ -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 f17dd1414..74c3fb008 100755 --- a/fleet_rental/models/car_rental.py +++ b/fleet_rental/models/car_rental.py @@ -3,7 +3,7 @@ # # Cybrosys Technologies Pvt. Ltd. # -# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Copyright (C) 2023-TODAY Cybrosys Technologies(). # Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) # # You can modify it under the terms of the GNU AFFERO @@ -19,9 +19,8 @@ # If not, see . # ############################################################################# - from datetime import datetime, date, timedelta -from odoo import models, fields, api, _ +from odoo import api, fields, models, _ from odoo.exceptions import UserError, Warning, ValidationError @@ -30,89 +29,124 @@ class CarRentalContract(models.Model): _description = 'Fleet Rental Management' _inherit = 'mail.thread' - @api.onchange('rent_start_date', 'rent_end_date') - def check_availability(self): - 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): - 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(each.date_to): - i.write({'rental_check_availability': False}) - elif str(self.rent_end_date) > str(each.date_to): - i.write({'rental_check_availability': False}) - else: - i.write({'rental_check_availability': True}) - 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", required=True) - rent_start_date = fields.Date(string="Rent Start Date", required=True, default=str(date.today()), - help="Start date of contract", track_visibility='onchange') - rent_end_date = fields.Date(string="Rent End Date", required=True, help="End date of contract", + cost = fields.Float(string="Rent Cost", + help="This fields is to determine the cost of rent", + required=True) + rent_start_date = fields.Date(string="Rent Start Date", required=True, + default=str(date.today()), + help="Start date of contract", + track_visibility='onchange') + start_time = fields.Char(string="Start By", + help="Enter the contract starting hour") + end_time = fields.Char(string="End By", + help="Enter the contract Ending hour") + rent_by_hour = fields.Boolean(string="Rent By Hour", + help="Enable to start contract on " + "hour basis") + rent_end_date = fields.Date(string="Rent End Date", required=True, + help="End date of contract", track_visibility='onchange') state = fields.Selection( - [('draft', 'Draft'), ('reserved', 'Reserved'), ('running', 'Running'), ('cancel', 'Cancel'), - ('checking', 'Checking'), ('invoice', 'Invoice'), ('done', 'Done')], string="State", + [('draft', 'Draft'), ('reserved', 'Reserved'), ('running', 'Running'), + ('cancel', 'Cancel'), + ('checking', 'Checking'), ('invoice', 'Invoice'), ('done', 'Done')], + string="State", default="draft", copy=False, track_visibility='onchange') notes = fields.Text(string="Details & Notes") 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'), - ('yearly', 'Yearly')], string="Recurring Cost Frequency", - help='Frequency of the recurring cost', required=True) + cost_frequency = fields.Selection( + [('no', 'No'), ('daily', 'Daily'), ('weekly', 'Weekly'), + ('monthly', 'Monthly'), + ('yearly', 'Yearly')], string="Recurring Cost Frequency", + 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)])) + default=lambda self: self.env[ + '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", + default=lambda self: self.env[ + '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) - 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.", + 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.", 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') - damage_cost = fields.Float(string="Damage Cost", copy=False) - damage_cost_sub = fields.Float(string="Damage Cost", readonly=True, copy=False) + 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 / Balance Amount", + copy=False) + damage_cost_sub = fields.Float(string="Damage Cost / Balance Amount", + 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) + 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, + sales_person = fields.Many2one('res.users', string='Sales Person', + default=lambda self: self.env.uid, track_visibility='always') + read_only = fields.Boolean(string="Read Only", help="To make field read " + "only") 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: @@ -127,27 +161,44 @@ 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 ValidationError("Please select the valid end date.") + raise UserError("Please select the valid end date.") def set_to_done(self): - 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: + """ + 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( + [('fleet_rent_id', '=', self.id), + ('amount_untaxed_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: 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}) @@ -161,6 +212,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: @@ -173,11 +228,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, @@ -186,15 +243,17 @@ class CarRentalContract(models.Model): 'invoice_date_due': self.rent_end_date, } inv_id = inv_obj.create(inv_data) - product_id = self.env['product.product'].search([("name", "=", "Fleet Rental Service")]) + 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)) + _('Please define income account for this product: "%s" (id:%d).') % ( + product_id.name, + product_id.id)) recurring_data = { 'name': self.vehicle_id.name, 'date_today': rent_date, @@ -225,7 +284,8 @@ class CarRentalContract(models.Model): 'Amount %s' 'Due Date %s' 'Responsible Person %s, %s') % \ - (self.customer_id.name, self.name, inv_id.amount_total, inv_id.invoice_date_due, + (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) main_content = { @@ -238,13 +298,19 @@ 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'] today = date.today() 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() + 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() if end_date >= date.today(): temp = 0 if records.cost_frequency == 'daily': @@ -268,19 +334,22 @@ class CarRentalContract(models.Model): 'currency_id': records.account_type.company_id.currency_id.id, 'journal_id': records.journal_type.id, 'invoice_origin': records.name, + 'fleet_rent_id': records.id, 'company_id': records.account_type.company_id.id, 'invoice_date_due': self.rent_end_date, } inv_id = inv_obj.create(inv_data) - product_id = self.env['product.product'].search([("name", "=", "Fleet Rental Service")]) + 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)) + _('Please define income account for this product: "%s" (id:%d).') % ( + product_id.name, + product_id.id)) recurring_data = { 'name': records.vehicle_id.name, 'date_today': today, @@ -312,7 +381,9 @@ class CarRentalContract(models.Model): '' '' '
Amount %s
Due Date %s
Responsible Person %s, %s
') % \ - (self.customer_id.name, self.name, inv_id.amount_total, inv_id.invoice_date_due, + (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) main_content = { @@ -330,40 +401,39 @@ class CarRentalContract(models.Model): self.state = "invoice" self.reserved_fleet_id.unlink() self.rent_end_date = fields.Date.today() + self.vehicle_id.rental_check_availability = True + 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') + 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 = { @@ -371,9 +441,9 @@ 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 else: @@ -381,8 +451,12 @@ class CarRentalContract(models.Model): return result def action_confirm(self): - if self.rent_end_date < self.rent_start_date: - raise ValidationError("Please select the valid end date.") + """ + Confirm the rental contract, check vehicle availability, update + state to "reserved," generate a sequence code, and send a + confirmation email. + """ + self.vehicle_id.rental_check_availability = False check_availability = 0 for each in self.vehicle_id.rental_reserved_time: if each.date_from <= self.rent_start_date <= each.date_to: @@ -397,28 +471,34 @@ class CarRentalContract(models.Model): else: check_availability = 0 if check_availability == 0: - reserved_id = self.vehicle_id.rental_reserved_time.create({'customer_id': self.customer_id.id, - 'date_from': self.rent_start_date, - 'date_to': self.rent_end_date, - 'reserved_obj': self.vehicle_id.id - }) + reserved_id = self.vehicle_id.rental_reserved_time.create( + {'customer_id': self.customer_id.id, + 'date_from': self.rent_start_date, + 'date_to': self.rent_end_date, + 'reserved_obj': self.vehicle_id.id + }) self.write({'reserved_fleet_id': reserved_id.id}) else: - raise Warning('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) - mail_content = _('

Order Confirmed!


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

' - 'Please find the details below:

' - '
' - '' - '
Reference Number %s
Time Range %s to %s
Vehicle %s
Point Of Contact %s , %s
') % \ - (self.customer_id.name, self.name, self.rent_start_date, self.rent_end_date, - self.vehicle_id.name, self.sales_person.name, self.sales_person.mobile) + .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.

' + 'Please find the details below:

' + '
' + '' + '
Reference Number %s
Time Range %s to %s
Vehicle %s
Point Of Contact %s , %s
') % \ + (self.customer_id.name, self.name, self.rent_start_date, + self.rent_end_date, + self.vehicle_id.name, self.sales_person.name, + self.sales_person.mobile) main_content = { 'subject': _('Confirmed: %s - %s') % (self.name, self.vehicle_id.name), @@ -429,15 +509,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" + self.vehicle_id.rental_check_availability = True 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_untaxed_signed', '=', self.first_payment)]) + if any(each.payment_state == 'paid' for each in invoice_ids): + 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) @@ -455,7 +559,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', @@ -463,10 +567,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(): @@ -476,7 +586,8 @@ class CarRentalContract(models.Model): if each.cost_frequency == 'monthly': rental_days = int(rental_days / 30) for each1 in range(0, rental_days + 1): - if rent_date > datetime.strptime(str(each.rent_end_date), "%Y-%m-%d").date(): + if rent_date > datetime.strptime(str(each.rent_end_date), + "%Y-%m-%d").date(): break each.fleet_scheduler1(rent_date) if each.cost_frequency == 'daily': @@ -485,110 +596,147 @@ class CarRentalContract(models.Model): rent_date = rent_date + timedelta(days=7) if each.cost_frequency == 'monthly': rent_date = rent_date + timedelta(days=30) - - if self.first_payment != 0: - 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, - '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, - '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 - 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: - income_account = product_id.categ_id.property_account_income_categ_id.id - else: - raise UserError( - _('Please define income account for this product: "%s" (id:%d).') % (product_id.name, - product_id.id)) - - if inv_id: - list_value = [(0, 0, { - 'name': self.vehicle_id.name, - 'price_unit': self.first_payment, - 'quantity': 1.0, - 'account_id': income_account, - 'product_id': product_id.id, - 'move_id': inv_id.id, - })] - inv_id.write({'invoice_line_ids': list_value}) - mail_content = _( - '

First Payment Received!


Hi %s,
This is to notify that your first payment has ' - 'been received.

' - 'Please find the details below:

' - '
' - '
Invoice Number %s
Date %s
Amount %s
') % ( - self.customer_id.name, inv_id.payment_reference, inv_id.invoice_date, inv_id.amount_total) - main_content = { - 'subject': _('Payment Received: %s') % inv_id.payment_reference, - 'author_id': self.env.user.partner_id.id, - 'body_html': mail_content, - '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', - 'views': [[False, 'form']], - 'target': 'current', - 'res_id': inv_id.id, - 'res_model': 'account.move', - } - return result - + self.first_invoice_created = True + inv_obj = self.env['account.move'] + 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, + } + inv_id = inv_obj.create(inv_data) + self.first_payment_inv = inv_id.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: + income_account = product_id.categ_id.property_account_income_categ_id.id else: - raise ValidationError("Please enter advance amount to make first payment") - - -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") + raise UserError( + _('Please define income account for this product: "%s" (id:%d).') % ( + product_id.name, + product_id.id)) + + if inv_id: + list_value = [(0, 0, { + 'name': self.vehicle_id.name, + 'price_unit': self.first_payment, + 'quantity': 1.0, + 'account_id': income_account, + 'product_id': product_id.id, + 'move_id': inv_id.id, + })] + inv_id.write({'invoice_line_ids': list_value}) + mail_content = _( + '

First Payment Received!


Hi %s,
This is to notify that your first payment has ' + 'been received.

' + 'Please find the details below:

' + '
' + '
Invoice Number %s
Date %s
Amount %s
') % ( + self.customer_id.name, inv_id.payment_reference, + inv_id.invoice_date, inv_id.amount_total) + main_content = { + 'subject': _('Payment Received: %s') % inv_id.payment_reference, + 'author_id': self.env.user.partner_id.id, + 'body_html': mail_content, + 'email_to': self.customer_id.email, + } + self.env['mail.mail'].create(main_content).send() + action = self.env.ref('account.action_move_out_invoice_type') + result = { + 'name': action.name, + 'type': 'ir.actions.act_window', + 'views': [[False, 'form']], + 'target': 'current', + 'res_id': inv_id.id, + 'res_model': 'account.move', + } + return result + + def action_extend_rent(self): + """ + Set the 'read_only' attribute to True, indicating that the rent + extension action is being performed and the corresponding fields + should be made read-only. + + This method is typically used in the context of extending a rental + agreement. + """ + self.read_only = True + + def action_confirm_extend_rent(self): + """ + Confirm the extension of a rental agreement and update the rental + reserved time for the associated vehicle. + + This method sets the 'date_to' field of the rental reserved time + for the vehicle to the specified 'rent_end_date', indicating the + extended rental period. After confirming the extension, the + 'read_only' attribute is set to False to allow further + modifications. + + This method is typically called when a user confirms the extension + of a rental. + """ + self.vehicle_id.rental_reserved_time.write( + { + 'date_to': self.rent_end_date, + }) + self.read_only = False + + @api.constrains('start_time', 'end_time', 'rent_start_date', + 'rent_end_date') + def validate_time(self): + """ + Validate the time constraints for a rental agreement. + + This method is used as a constraint to ensure that the specified + start and end times are valid, especially when renting by the hour. + If renting by the hour, it checks whether the end time is greater + than the start time when the rental start and end + dates are the same. + + :raises ValidationError: If the time constraints are not met, a + validation error is raised with a relevant + error message. + """ + if self.rent_by_hour: + start_time = datetime.strptime(self.start_time, "%H:%M").time() + end_time = datetime.strptime(self.end_time, "%H:%M").time() + if (self.rent_end_date == self.rent_start_date and + end_time <= start_time): + raise ValidationError("Please choose a different end time") + + @api.constrains('rent_end_date') + def validate_on_read_only(self): + old_date = self.vehicle_id.rental_reserved_time.date_to + if self.read_only: + if self.rent_end_date <= old_date: + raise ValidationError( + f"Please choose a date greater that {old_date}") + + def action_discard_extend(self): + """ + Validate the 'rent_end_date' when the rental agreement is in + read-only mode. + + This constraint checks if the rental agreement is marked as + read-only, indicating that it has been extended or modified. If in + read-only mode, it compares the 'rent_end_date' with the existing + 'date_to' value in the rental reserved time of the associated + vehicle. It ensures that the 'rent_end_date' is greater than the + existing date to maintain consistency. + + :raises ValidationError: If the 'rent_end_date' is not greater than + the existing 'date_to', a validation error + is raised with a relevant error message. + """ + self.read_only = False + self.rent_end_date = self.vehicle_id.rental_reserved_time.date_to 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 484ff43ee..e3b484897 100755 --- a/fleet_rental/models/fleet.py +++ b/fleet_rental/models/fleet.py @@ -3,7 +3,7 @@ # # Cybrosys Technologies Pvt. Ltd. # -# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Copyright (C) 2023-TODAY Cybrosys Technologies(). # Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) # # You can modify it under the terms of the GNU AFFERO @@ -19,7 +19,6 @@ # If not, see . # ############################################################################# - from odoo import models, fields @@ -31,21 +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 new file mode 100644 index 000000000..4fab4ae3b --- /dev/null +++ b/fleet_rental/models/res_config_settings.py @@ -0,0 +1,39 @@ +# -*- 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 ResConfigSettings(models.TransientModel): + """Inherit configuration settings""" + _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) diff --git a/fleet_rental/reports/__init__.py b/fleet_rental/reports/__init__.py index 7dcd4f019..4f9a39c26 100755 --- a/fleet_rental/reports/__init__.py +++ b/fleet_rental/reports/__init__.py @@ -3,7 +3,7 @@ # # Cybrosys Technologies Pvt. Ltd. # -# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Copyright (C) 2023-TODAY Cybrosys Technologies(). # Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) # # You can modify it under the terms of the GNU AFFERO diff --git a/fleet_rental/reports/rental_report.py b/fleet_rental/reports/rental_report.py index 3c28815ec..41a27dbcb 100755 --- a/fleet_rental/reports/rental_report.py +++ b/fleet_rental/reports/rental_report.py @@ -3,7 +3,7 @@ # # Cybrosys Technologies Pvt. Ltd. # -# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Copyright (C) 2023-TODAY Cybrosys Technologies(). # Author: Cybrosys Technogies @cybrosys(odoo@cybrosys.com) # # You can modify it under the terms of the GNU AFFERO @@ -36,10 +36,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 +52,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 +78,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 +103,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 5986c2ca1..17523a0f0 100755 --- a/fleet_rental/reports/rental_report.xml +++ b/fleet_rental/reports/rental_report.xml @@ -1,26 +1,28 @@ - - + report.fleet.rental.pivot report.fleet.rental - - - - + + + + - Fleet Rental Analysis report.fleet.rental pivot {'group_by_no_leaf':1,'group_by':[]} - This report allows you to analyse the performance of your Fleet Rental. + This report allows you to analyse the performance of your + Fleet Rental. + - - + \ No newline at end of file diff --git a/fleet_rental/static/src/js/time_widget.js b/fleet_rental/static/src/js/time_widget.js new file mode 100644 index 000000000..8998419e5 --- /dev/null +++ b/fleet_rental/static/src/js/time_widget.js @@ -0,0 +1,32 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { useInputField } from "@web/views/fields/input_field_hook"; +const { Component, useRef } = owl; +/** + * We define this module for the function of creating a time picker widget + * + */ +class FieldTimePicker extends Component { + setup() { + this.input = useRef('input_time'); + useInputField({ getValue: () => this.props.record.data[this.props.name] || "", refName: "input_time" }); + } + + onBlur(){ + /** + * Handle the blur event for the timepicker input field. + * + * This function is responsible for handling the blur event on the timepicker input field. + * It checks if the close button is present in the timepicker, and if so, it adds a click event + * listener to it to handle the closing of the timepicker. + * + * @returns {void} + */ + this.props.record.update({ [this.props.name] : this.input.el?.value}) + } +} +// Set the template for the FieldTimePicker component +FieldTimePicker.template = 'FieldTimePicker'; +FieldTimePicker.supportedTypes = ["char"] +// Add the timepicker to the fields category +registry.category("fields").add("timepicker_time", FieldTimePicker); diff --git a/fleet_rental/static/src/scss/timepicker.scss b/fleet_rental/static/src/scss/timepicker.scss new file mode 100644 index 000000000..8f475435f --- /dev/null +++ b/fleet_rental/static/src/scss/timepicker.scss @@ -0,0 +1,3 @@ +.timepicker-component{ + width:30%; +} diff --git a/fleet_rental/static/src/xml/timepicker.xml b/fleet_rental/static/src/xml/timepicker.xml new file mode 100644 index 000000000..18f05e740 --- /dev/null +++ b/fleet_rental/static/src/xml/timepicker.xml @@ -0,0 +1,9 @@ + + + + +
+ +
+
+
diff --git a/fleet_rental/views/car_rental_view.xml b/fleet_rental/views/car_rental_view.xml index 6c0ecc26e..417fb0b1f 100755 --- a/fleet_rental/views/car_rental_view.xml +++ b/fleet_rental/views/car_rental_view.xml @@ -14,16 +14,16 @@ - Fleet scheduler - - code - model.fleet_scheduler() - - - 1 - days - -1 - + Fleet scheduler + + code + model.fleet_scheduler() + + + 1 + days + -1 + @@ -49,25 +49,42 @@
-
@@ -77,17 +94,27 @@ - + - - - + + + + + + + + options="{'no_create': True}"/> - - + + @@ -96,25 +123,26 @@ - + @@ -131,14 +159,18 @@ - + - + - + @@ -148,8 +180,10 @@ - - + + @@ -159,11 +193,13 @@ - +
-
@@ -70,7 +75,7 @@
- + form diff --git a/fleet_rental/views/res_config_settings_views.xml b/fleet_rental/views/res_config_settings_views.xml new file mode 100644 index 000000000..7231adbb2 --- /dev/null +++ b/fleet_rental/views/res_config_settings_views.xml @@ -0,0 +1,27 @@ + + + + + res.config.settings.view.form.inherit.fleet.rental + res.config.settings + + + + +
+
+
+
+ Default Fleet Rental Service Product + +
+
+
+
+
+
+
+