diff --git a/vehicle_subscription/README.rst b/vehicle_subscription/README.rst new file mode 100644 index 000000000..fd5e186fc --- /dev/null +++ b/vehicle_subscription/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/AGPL-3.0-standalone.html + :alt: License: AGPL-3 + +Vehicle Subscription +==================== +This module helps you to Subscribe,Cancel and Change subscription through website as well as backend. + +Configuration +============= +* No additional configurations needed + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +* Developers: (v17) Jumana Jabin MP, Contact: odoo@cybrosys.com + +License +------- +GNU AFFERO GENERAL PUBLIC LICENSE v3.0 (AGPL-3) +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/vehicle_subscription/__init__.py b/vehicle_subscription/__init__.py new file mode 100644 index 000000000..24612511c --- /dev/null +++ b/vehicle_subscription/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP() +# +# 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 . import controllers +from . import models +from . import wizard diff --git a/vehicle_subscription/__manifest__.py b/vehicle_subscription/__manifest__.py new file mode 100644 index 000000000..7dee146dd --- /dev/null +++ b/vehicle_subscription/__manifest__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author:Jumana Jabin MP () +# +# 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 . +# +############################################################################# +{ + 'name': 'Vehicle Subscription', + 'version': "17.0.1.0.0", + 'category': 'Fleet', + 'summary': """Subscribe,Cancel and Switch subscription""", + 'description': """This module helps you to Subscribe,Cancel and Change + subscription through website as well as backend.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['mail', 'contacts', 'fleet', 'account', 'sale', 'website', + 'portal', ], + 'data': [ + 'security/vehicle_subscription_groups.xml', + 'security/ir.model.access.csv', + 'data/ir_cron_data.xml', + 'data/product_template_data.xml', + 'data/website_menu_data.xml', + 'data/mail_data.xml', + 'views/website_portal_subscription_template.xml', + 'views/fleet_vehicle_model_views.xml', + 'views/fleet_subscription_views.xml', + 'views/vehicle_insurance_views.xml', + 'views/subscription_request_views.xml', + 'views/insurance_type_views.xml', + 'views/online_subscription_template.xml', + 'views/online_vehicle_template.xml', + 'views/account_move_views.xml', + 'views/subscription_form_success_template.xml', + 'views/online_vehicle_cancellation_template.xml', + 'views/cancellation_request_views.xml', + 'views/change_vehicle_subscription_template.xml', + 'wizard/change_subscription_views.xml', + ], + 'assets': { + 'web.assets_frontend': [ + 'vehicle_subscription/static/src/js/vehicle_subscription_success.js', + 'vehicle_subscription/static/src/js/website_subscription.js', + 'vehicle_subscription/static/src/js/vehicle_booking.js', + 'vehicle_subscription/static/src/js/change_subscription_form.js', + 'vehicle_subscription/static/src/js/change_subscription_request.js', + ], + }, + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/vehicle_subscription/controllers/__init__.py b/vehicle_subscription/controllers/__init__.py new file mode 100644 index 000000000..4c1f6c1b8 --- /dev/null +++ b/vehicle_subscription/controllers/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 . import vehicle_subscription +from . import website_portal diff --git a/vehicle_subscription/controllers/vehicle_subscription.py b/vehicle_subscription/controllers/vehicle_subscription.py new file mode 100644 index 000000000..e85665694 --- /dev/null +++ b/vehicle_subscription/controllers/vehicle_subscription.py @@ -0,0 +1,440 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP() +# +# 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 datetime import datetime, timedelta +from odoo import http +from odoo.http import request + + +class OnlineSubscription(http.Controller): + """Online Vehicle subscription through website""" + + @http.route(['/online/subscription/city'], type='json', auth="public", + website=True) + def get_city(self, **kwargs): + """Calling this function using ajax rpc in order to get city + based on state """ + states = request.env['res.country.state'].sudo().search_read([], + ['name']) + return states + + @http.route('/online/subscription', auth='public', website=True) + def subscription_form(self): + """This function will return vehicle with which state is not null""" + vehicle_id = request.env['fleet.vehicle'].sudo().search( + [('states_id', '!=', False)]) + insurance_type = request.env['insurance.type'].sudo().search([]) + vals = { + 'states': vehicle_id.states_id, + 'all_states': request.env['res.country.state'].sudo().search_read( + [], ['name']), + 'cities': [rec.location for rec in vehicle_id], + 'insurance_type': insurance_type, + } + return http.request.render('vehicle_subscription.subscription_form', + vals) + + @http.route('/online/subscription/next', auth='public', website=True) + def vehicle_form(self, **kw): + """Redirect to corresponding templates according to the + data provided by user in form page """ + if kw.get('start_date'): + start_date = kw.get('start_date') + end_date = kw.get('end_date') + insurance = kw.get('insurance_type') + start = datetime.strptime(start_date, '%Y-%m-%d').date() + end = datetime.strptime(end_date, '%Y-%m-%d').date() + insurance_amount = request.env['vehicle.insurance'].sudo().browse( + int(insurance)).insurance_amount + # print("insurance_amount",insurance_amount) + insurance_type = request.env['vehicle.insurance'].sudo().search( + [('insurance_type_id.id', '=', insurance), + ('start_date', '<=', start), ('end_date', '>=', end)]) + vehicle_ids = insurance_type.vehicle_id + subscribed_vehicle_id = (request.env['fleet.subscription'].sudo(). + search( + [('state', '=', 'subscribed')]).vehicle_id) + vehicle = request.env['fleet.vehicle'].sudo().search( + [('id', 'in', vehicle_ids.ids)]) + vehicle_id = vehicle.filtered( + lambda v: v.id not in subscribed_vehicle_id.ids) + if vehicle_id: + for rec in vehicle_id: + rec.write({ + 'insurance': insurance, + 'start': start, + 'end': end, + }) + data = { + 'vehicles': vehicle_id, + 'amount': insurance_amount, + 'customers': request.env.user.partner_id.name, + } + return http.request.render('vehicle_subscription.vehicle_form', + data) + else: + return http.request.render( + 'vehicle_subscription.subscription_vehicle_missing') + else: + return http.request.render('vehicle_subscription.vehicle_form') + + @http.route(['/online/subscription/book'], type='json', auth="public", + website=True) + def get_vehicle(self, **kwargs): + """Ajax RPC handler for booking vehicle subscription and + creating corresponding invoices in the backend.""" + extra_km = kwargs.get('extra_km') + product_template_id = (request.env.ref( + 'vehicle_subscription.product_template_vehicle_subscription_form'). + id) + product_id = request.env['product.product'].sudo().search( + [('product_tmpl_id', '=', product_template_id)]) + vehicle = int(kwargs.get('vehicle')) + customer = kwargs.get('customer') + checked = int(kwargs.get('checked')) + invoice_type = int(kwargs.get('invoice')) + vehicle_id = request.env['fleet.vehicle'].sudo().browse(int(vehicle)) + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)]) + if extra_km == '': + km = 0 + else: + km = extra_km + # print(vehicle_id.insurance, "bbbbbbbbbb") + # insurance_amount = vehicle_id.insurance.insurance_amount + # insurance_amount = request.env['vehicle.insurance'].sudo().browse( + # int(vehicle_id.insurance)).insurance_amount + # price = vehicle_id.duration * vehicle_id.subscription_price + # print("aaaaaaaaaaaaaaaa",insurance_amount + price) + subscribe = request.env['fleet.subscription'].sudo().create({ + 'vehicle_id': vehicle_id.id, + 'customer_id': customer_id.id, + 'insurance_type_id': vehicle_id.insurance, + 'start_date': vehicle_id.start, + 'end_date': vehicle_id.end, + 'extra_km': km, + 'fuel': 'without_fuel' if checked == False else 'with_fuel', + }) + subscribe.action_invoice() + # subscribe.sale_id.order_line.price_unit = price + subscribe.sale_id.action_confirm() + if invoice_type: + subscribe.sale_id._create_invoices().action_post() + subscribe.invoice_ids.is_subscription = True + subscribe.sale_id.invoice_ids.is_subscription = True + else: + subscribe.sale_id.invoice_status = 'invoiced' + total_price = subscribe.sale_id.order_line.price_unit + duration = vehicle_id.duration + per_day = total_price / duration + start_date = vehicle_id.start + end_date = vehicle_id.end + next_invoice_day = start_date + while next_invoice_day <= end_date: + next_invoice_day = next_invoice_day + timedelta(days=30) + if next_invoice_day <= end_date: + durations = (next_invoice_day - start_date).days + generate_invoice = request.env[ + 'account.move'].sudo().create({ + 'move_type': 'out_invoice', + 'partner_id': customer_id.id, + 'invoice_date': next_invoice_day, + 'invoice_origin': subscribe.sale_id.name, + 'invoice_line_ids': [(0, 0, { + 'product_id': product_id.id, + 'name': vehicle_id.name, + 'price_unit': per_day * durations, + })] + }) + generate_invoice.is_subscription = True + generate_invoice.action_post() + subscribe.sale_id.invoice_ids = [(4, generate_invoice.id)] + subscribe.invoice_ids = [(4, generate_invoice.id)] + else: + next_invoice_day = end_date + durations = (next_invoice_day - start_date).days + generate_invoice = request.env[ + 'account.move'].sudo().create({ + 'move_type': 'out_invoice', + 'partner_id': customer_id.id, + 'invoice_date': next_invoice_day, + 'invoice_line_ids': [(0, 0, { + 'product_id': product_id.id, + 'name': vehicle_id.name, + 'price_unit': per_day * durations, + })] + }) + generate_invoice.is_subscription = True + generate_invoice.action_post() + subscribe.sale_id.invoice_ids = [(4, generate_invoice.id)] + subscribe.invoice_ids = [(4, generate_invoice.id)] + break + start_date = start_date + timedelta(days=30) + values = { + 'subscription_id': subscribe.id + } + return values + + @http.route(['/next/vehicle', '/next/vehicle/'], + auth='public', website=True, type='http') + def subscription_create(self): + """Return template for successful subscription""" + current_vehicle = request.env['fleet.subscription'].sudo().search([ + ('customer_id', '=', request.env.user.partner_id.id), + ('state', '=', 'subscribed'), + ], order='write_date desc', limit=1) + context = { + 'vehicle_name': current_vehicle.vehicle_id.name, + 'customer_name': request.env.user.partner_id.name, + } + return request.render('vehicle_subscription.subscription_form_success', + context) + + @http.route(['/online/subscription/with/fuel'], type='json', auth="public", + website=True) + def get_with_fuel(self, **kwargs): + """Calculate price for vehicle according to fuel type """ + vehicle = int(kwargs.get('vehicle')) + km = kwargs.get('extra_km') + vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle) + vehicle.write({ + 'extra_km': km, + }) + insurance_amount = vehicle.insurance + amount = request.env['vehicle.insurance'].sudo() \ + .browse(int(insurance_amount)).insurance_amount + if float(km) > vehicle.free_km: + new_price = (((vehicle.extra_km / vehicle.mileage) * + vehicle.fuel_rate) + + (vehicle.duration * vehicle.subscription_price) + + amount) + else: + if float(km) <= vehicle.free_km: + new_price = ((vehicle.duration * vehicle.subscription_price) + + amount) + return str(new_price) + + @http.route(['/online/subscription/without/fuel'], type='json', + auth="public", + website=True) + def get_without_fuel(self, **kwargs): + """Calculate price for vehicle according to fuel type """ + vehicle = int(kwargs.get('vehicle')) + km = kwargs.get('extra_km') + vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle) + insurance_amount = vehicle.insurance + amount = request.env['vehicle.insurance'].sudo() \ + .browse(int(insurance_amount)).insurance_amount + vehicle.write({ + 'extra_km': km, + }) + if float(km) > vehicle.free_km: + new_price = (((vehicle.duration * vehicle.subscription_price) + + amount) + (vehicle.charge_km * vehicle.extra_km)) + else: + new_price = ( + vehicle.duration * vehicle.subscription_price) + amount + return str(new_price) + + @http.route('/online/subscription/cancel', auth='public', website=True) + def cancellation_form(self): + """Cancel subscription form through website""" + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', request.env.user.partner_id.name)]) + vehicle_id = request.env['fleet.subscription'].sudo().search( + [('customer_id', '=', customer_id.id), + ('state', '=', 'subscribed')]) + vals = { + 'customers': customer_id.name, + 'vehicles': vehicle_id, + } + return http.request.render( + 'vehicle_subscription.subscription_cancellation_form', vals) + + @http.route('/online/choose/vehicle', type='json', auth="public", + website=True) + def choose_vehicle(self, **kwargs): + """Only display vehicle of selected customer in website""" + customer = kwargs.get('customer_id') + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)]) + vehicle_id = request.env['fleet.subscription'].sudo().search( + [('state', '=', 'subscribed'), + ('customer_id', '=', customer_id.id)]).mapped('vehicle_id') + if vehicle_id: + vehicle = [(rec.id, rec.name) for rec in vehicle_id] + return [*set(vehicle)] + + @http.route('/online/cancellation/click', auth='public', type='http', + website=True) + def cancellation_click_form(self, **kwargs): + """Proceed with cancellation button click""" + customer = kwargs.get('customer') + vehicle = int(kwargs.get('vehicle')) + reason = kwargs.get('reason') + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)]) + subscription = request.env['fleet.subscription'].sudo().browse(vehicle) + vehicle_id = subscription.vehicle_id + existing_request = request.env['cancellation.request'].sudo().search([ + ('customer_id', '=', customer_id.id), + ('vehicle_id', '=', vehicle_id.id), + ('state', '!=', 'cancel') + ], limit=1) + if existing_request: + values = { + 'customer': customer, + 'vehicle': vehicle_id.name, + 'existing_request': existing_request, + } + return request.render( + 'vehicle_subscription.existing_cancellation_popup', values) + cancel_request = request.env['cancellation.request'].sudo().create({ + 'customer_id': customer_id.id, + 'vehicle_id': vehicle_id.id, + 'reason': reason, + }) + values = { + 'customer': customer, + 'vehicle': vehicle_id.name, + } + cancel_request.state = 'to_approve' + return request.render('vehicle_subscription.booking_cancellation', + values) + + @http.route('/online/subscription/change', auth='public', website=True) + def subscription_change_form(self): + """Rendered response for the 'vehicle_subscription. + subscription_change_form' template, + containing the available vehicles and the current customer's name.""" + customer = request.env.user.partner_id.name + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)]) + invoice = request.env['account.move'].sudo().search([ + ('invoice_line_ids.product_id', 'like', 'Vehicle Subscription'), + ('partner_id', '=', customer_id.id) + ]) + vehicle_names = [line.name for line in invoice.invoice_line_ids if + line.product_id.name == 'Vehicle Subscription'] + vehicles = request.env['fleet.vehicle'].sudo().search( + [('name', 'in', vehicle_names)]) + filtered_vehicles = vehicles + for vehicle in vehicles: + existing_request = request.env['subscription.request'].sudo().search([ + ('customer_id', '=', customer_id.id), + ('current_vehicle_id', '=', vehicle.id), + ('state', '!=', 'cancel'),('is_subscription' ,'!=',True) + ]) + already_request = request.env['subscription.request'].sudo().search([ + ('customer_id', '=', customer_id.id), + ('current_vehicle_id', '=', vehicle.id), + ('state', '!=', 'cancel'),('is_subscription' ,'=',True) + ]) + already_vehicle = already_request.current_vehicle_id + if existing_request: + values = { + 'customer': customer, + 'current_vehicle': existing_request.current_vehicle_id.name, + 'existing_request': existing_request, + } + return request.render( + 'vehicle_subscription.existing_subscription_popup', values) + else: + vals = { + 'vehicles': filtered_vehicles.filtered(lambda v: v.id != already_vehicle.id), + 'customers': customer_id.name, + } + return http.request.render( + 'vehicle_subscription.subscription_change_form', vals) + + @http.route('/online/subscription/change/vehicle', auth='public', + type='http', website=True) + def change_click_form(self, **kwargs): + """ Rendered response based on the conditions: + - If the 'customer' parameter exists, render the + 'vehicle_subscription.subscription_change_button' template + with the provided data. + - If the 'customer' parameter does not exist, render the + 'vehicle_subscription.subscription_change_boolean_false' + template.""" + if kwargs.get('customer'): + customer = kwargs.get('customer') + vehicle = int(kwargs.get('vehicle')) + reason = kwargs.get('reason') + checkbox = kwargs.get('checkbox_model') + customer_id = request.env['res.partner'].sudo(). \ + search([('name', '=', customer)]) + vehicle_id = request.env['fleet.vehicle'].sudo().browse(vehicle) + new_vehicle_id = request.env['fleet.vehicle'].sudo() \ + .search([('model_id', '!=', vehicle_id.model_id.id)]) + if checkbox == 'on': + values = { + 'customer_name': customer_id.name, + 'vehicle_name': vehicle_id.name, + 'vehicles': [rec for rec in new_vehicle_id], + 'reason': reason, + } + return request.render( + 'vehicle_subscription.subscription_change_button', values) + else: + return request.render( + 'vehicle_subscription.subscription_change_boolean_false') + else: + return request.render( + 'vehicle_subscription.subscription_change_button') + + @http.route('/online/subscription/change/button', auth='public', + type='http', website=True) + def click_form(self, **kwargs): + """Rendered response for the + 'vehicle_subscription.change_subscription' template. """ + customer = kwargs.get('customer') + reason = kwargs.get('reason') + current_vehicle = kwargs.get('vehicle') + vehicle_id = int(kwargs.get('new_vehicle')) + current_vehicle_id = request.env['fleet.vehicle'].sudo() \ + .search([('name', '=', current_vehicle)]) + customer_id = request.env['res.partner'].sudo() \ + .search([('name', '=', customer)]) + vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle_id) + change_subscription = request.env['subscription.request'] \ + .sudo().create({ + 'current_vehicle_id': current_vehicle_id.id, + 'new_vehicle_id': vehicle.id, + 'reason_to_change': reason, + 'customer_id': customer_id.id, + 'is_subscription': True, + }) + change_subscription.is_subscription = True + change_subscription.state = 'to_approve' + return request.render('vehicle_subscription.change_subscription') + + @http.route('/online/proceed/cancellation', auth='public', type='http', + website=True) + def proceed_cancellation(self): + """Proceed with cancellation in change subscription """ + return request.redirect('/online/subscription/cancel') + + @http.route(['/web/signup/user'], type='http', auth="user", + website=True) + def redirect_login(self): + """Used to redirect on clicking signup page""" + return request.redirect('/online/subscription') diff --git a/vehicle_subscription/controllers/website_portal.py b/vehicle_subscription/controllers/website_portal.py new file mode 100644 index 000000000..3e11d8a8b --- /dev/null +++ b/vehicle_subscription/controllers/website_portal.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 http +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal, \ + pager as portal_pager + + +class PortalAccount(CustomerPortal): + """PortalAccount for subscription""" + + @http.route(['/my/subscription/invoice'], type='http', auth="user", + website=True) + def portal_my_subscription_order(self, page=1, date_begin=None, + date_end=None, sortby=None, filterby=None): + """Rendered response for the ' + vehicle_subscription.portal_my_invoices_subscription' template, + containing the subscription invoices.""" + partner = request.env.user.partner_id + values = self._prepare_my_invoices_values(page, date_begin, date_end, + sortby, filterby) + pager = portal_pager(**values['pager']) + domain = [ + ('invoice_line_ids.product_id', 'like', 'Vehicle Subscription'), + ('partner_id', '=', partner.id) + ] + values.update({ + 'invoices': request.env['account.move'].sudo().search(domain), + 'pager': pager, + }) + return request.render( + "vehicle_subscription.portal_my_invoices_subscription", values) + + def _prepare_home_portal_values(self, counters): + """Prepare the values for the home portal page.""" + values = super()._prepare_home_portal_values(counters) + partner = request.env.user.partner_id + if 'subscription_count' in counters: + values['subscription_count'] = request.env['account.move'].sudo() \ + .search_count( + [( + 'invoice_line_ids.product_id', 'like', 'Vehicle Subscription'), + ('partner_id', '=', partner.id)]) + return values diff --git a/vehicle_subscription/data/ir_cron_data.xml b/vehicle_subscription/data/ir_cron_data.xml new file mode 100644 index 000000000..e2c657f73 --- /dev/null +++ b/vehicle_subscription/data/ir_cron_data.xml @@ -0,0 +1,16 @@ + + + + + + Subscription Expired + + code + model._onchange_end_date() + 1 + days + -1 + + + + diff --git a/vehicle_subscription/data/mail_data.xml b/vehicle_subscription/data/mail_data.xml new file mode 100644 index 000000000..d94762108 --- /dev/null +++ b/vehicle_subscription/data/mail_data.xml @@ -0,0 +1,79 @@ + + + + + + Subscription Expired + + code + model._onchange_end_date() + 1 + days + -1 + + + + + Vehicle Cancellation + + Subscription Cancellation + +
+

+ Dear +
+ Sorry....you need to pay amount till date inorder to + cancel + subscription.Your invoice is attached + below +
+

+ Regards, +
+ +
+
+
+ + + Vehicle Cancellation + + Subscription Cancellation + +
+

+ Dear +
+ The amount should be refunded immediately. +
+

+ Regards, +
+ +
+
+
+ + + Vehicle Cancellation + + Subscription Cancellation + +
+

+ Dear +
+ Your request for subscription cancellation is approved +
+

+ Regards, +
+ +
+
+
+
+
diff --git a/vehicle_subscription/data/product_template_data.xml b/vehicle_subscription/data/product_template_data.xml new file mode 100644 index 000000000..1702ccfcb --- /dev/null +++ b/vehicle_subscription/data/product_template_data.xml @@ -0,0 +1,13 @@ + + + + + + Vehicle Subscription + service + order + 55 + + + diff --git a/vehicle_subscription/data/website_menu_data.xml b/vehicle_subscription/data/website_menu_data.xml new file mode 100644 index 000000000..967c23e9c --- /dev/null +++ b/vehicle_subscription/data/website_menu_data.xml @@ -0,0 +1,12 @@ + + + + + + Subscription Form + /online/subscription + + 55 + + + diff --git a/vehicle_subscription/doc/RELEASE_NOTES.md b/vehicle_subscription/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..c6e761807 --- /dev/null +++ b/vehicle_subscription/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 04.01.2025 +#### Version 17.0.1.0.0 +#### ADD + - Initial Commit Vehicle Subscription diff --git a/vehicle_subscription/models/__init__.py b/vehicle_subscription/models/__init__.py new file mode 100644 index 000000000..3b2283672 --- /dev/null +++ b/vehicle_subscription/models/__init__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 . import account_move +from . import cancellation_request +from . import fleet_subscription +from . import fleet_vehicle +from . import fleet_vehicle_model +from . import insurance_type +from . import subscription_request +from . import vehicle_insurance diff --git a/vehicle_subscription/models/account_move.py b/vehicle_subscription/models/account_move.py new file mode 100644 index 000000000..b2266c398 --- /dev/null +++ b/vehicle_subscription/models/account_move.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author:Jumana Jabin MP () +# +# 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): + """Inherited account move to add a field """ + _inherit = 'account.move' + + is_subscription = fields.Boolean(string="Subscribe", + help="This field will be set true for " + "invoice corresponding to vehicle " + "subscription") diff --git a/vehicle_subscription/models/cancellation_request.py b/vehicle_subscription/models/cancellation_request.py new file mode 100644 index 000000000..ccd184716 --- /dev/null +++ b/vehicle_subscription/models/cancellation_request.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana jabin MP () +# +# 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 . +# +############################################################################# +import base64 +from datetime import date +from odoo import fields, models + + +class CancellationRequest(models.Model): + """Created new model to add new fields and function""" + _name = "cancellation.request" + _description = "Cancellation Request" + _inherit = "mail.thread" + _rec_name = 'vehicle_id' + + vehicle_id = fields.Many2one('fleet.vehicle', + string="Vehicle", help="Choose vehicle " + "inorder to cancel " + "subscription", + required=True) + date = fields.Date(string="Cancellation Date", default=fields.Date.today(), + help="Date for cancellation of vehicle") + customer_id = fields.Many2one('res.partner', string="Customer", + help="Choose Customer for the cancellation " + "of vehicle", required=True) + reason = fields.Char(string="Cancellation Reason", help="Describe the " + "reason for " + "cancellation") + state = fields.Selection(selection=[('draft', 'Draft'), + ('to_approve', 'To Approve'), + ('approved', 'Approved')], + string='State', default='draft', + help="States of cancellation subscription") + + def action_request(self): + """Change state to to_approve""" + self.write({'state': 'to_approve'}) + + def action_approve(self): + """Handle cancellation approval by manager. + This method handles the approval process for a cancellation + request in a vehicle subscription system. + It updates the cancellation state, generates invoices or refunds + based on the payment status, and sends notifications to the customer.""" + self.write({'state': 'approved'}) + subscription = self.env['fleet.subscription'].search( + [('vehicle_id', '=', self.vehicle_id.id), + ('customer_id', '=', self.customer_id.id)]) + invoice = subscription.sale_id.invoice_ids + multy_invoice = subscription.invoice_ids + product_template_id = self.env.ref( + 'vehicle_subscription.product_template_vehicle_subscription_form').id + product_id = self.env['product.product'].search( + [('product_tmpl_id', '=', product_template_id)], limit=1) + if subscription.duration != 0: + per_day_price = subscription.sale_id.amount_untaxed / subscription.duration + else: + per_day_price = 0 + if isinstance(self.date, date) and isinstance(subscription.start_date, + date): + invoice_duration = (self.date - subscription.start_date).days + else: + invoice_duration = 0 + email_template = self.env.ref( + 'vehicle_subscription.cancellation_request_mail') + template_approved = self.env.ref( + 'vehicle_subscription.cancellation_approved') + refund_approved = self.env.ref( + 'vehicle_subscription.cancellation_request_refund_mail') + uptodate_price = round(per_day_price * invoice_duration, 2) + paid_amount = self.env['account.move'].search( + [('id', 'in', subscription.invoice_ids.ids), + ('payment_state', 'in', ['paid', 'partial'])]).mapped( + 'amount_untaxed_signed') + if paid_amount: + if sum(paid_amount) == uptodate_price: + subscription.state = 'cancel' + email_values = { + 'email_to': self.customer_id.email, + 'email_from': self.env.user.email, + } + template_approved.send_mail(self.id, email_values=email_values, + force_send=True) + elif sum(paid_amount) < uptodate_price: + if len(invoice) == 1 or len(multy_invoice) == 1: + invoice.button_cancel() + generate_invoice = self.env['account.move'].sudo().create({ + 'move_type': 'out_invoice', + 'partner_id': self.customer_id.id, + 'invoice_date': self.date, + 'invoice_line_ids': fields.Command.create([{ + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': (per_day_price * invoice_duration) - sum( + paid_amount), + }]) + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env['ir.actions.report'].sudo()._render_qweb_pdf( + "account.account_invoices", generate_invoice.ids)[ + 0]) + ir_values = { + 'name': 'Invoice', + 'type': 'binary', + 'datas': data_record, + 'store_fname': 'invoice.pdf', + 'mimetype': 'application/pdf', + 'res_model': 'account.move', + 'res_id': generate_invoice.id, + } + invoice_report_attachment_id = self.env[ + 'ir.attachment'].sudo().create(ir_values) + email_values = { + 'email_to': self.customer_id.email, + 'email_from': self.env.user.email, + 'attachment_ids': [invoice_report_attachment_id.id] + } + email_template.send_mail(self.id, + email_values=email_values, + force_send=True) + email_template.attachment_ids = [] + subscription.invoice_ids = fields.Command.link( + generate_invoice.id) + subscription.sale_id.write({ + 'invoice_ids': fields.Command.link(generate_invoice.id) + }) + else: + for invoice_id in multy_invoice: + invoice = self.env['account.move'].browse( + invoice_id.id) + invoice.button_cancel() + generate_invoice = self.env['account.move'].sudo().create({ + 'move_type': 'out_invoice', + 'partner_id': self.customer_id.id, + 'invoice_date': self.date, + 'invoice_line_ids': fields.Command.create([{ + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': (per_day_price * invoice_duration) - sum( + paid_amount), + }]) + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env['ir.actions.report'].sudo()._render_qweb_pdf( + "account.account_invoices", generate_invoice.ids)[ + 0]) + ir_values = { + 'name': 'Invoice', + 'type': 'binary', + 'datas': data_record, + 'store_fname': 'invoice.pdf', + 'mimetype': 'application/pdf', + 'res_model': 'account.move', + 'res_id': generate_invoice.id, + } + invoice_report_attachment_id = self.env[ + 'ir.attachment'].sudo().create(ir_values) + email_values = { + 'email_to': self.customer_id.email, + 'email_from': self.env.user.email, + 'attachment_ids': [invoice_report_attachment_id.id] + } + email_template.send_mail(self.id, + email_values=email_values, + force_send=True) + email_template.attachment_ids = [] + subscription.invoice_ids = fields.Command.link( + generate_invoice.id) + subscription.sale_id.write({ + 'invoice_ids': fields.Command.link(generate_invoice.id) + }) + else: + generate_refund = self.env['account.move'].sudo().create({ + 'move_type': 'out_refund', + 'invoice_date': fields.Date.today(), + 'partner_id': self.customer_id.id, + 'invoice_line_ids': fields.Command.create([{ + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': (sum(paid_amount) - uptodate_price) + }]) + }) + generate_refund.action_post() + subscription.refund_id = generate_refund + data_record = base64.b64encode( + self.env['ir.actions.report'].sudo()._render_qweb_pdf( + "account.account_invoices", generate_refund.ids)[0]) + ir_values = { + 'name': 'Invoice', + 'type': 'binary', + 'datas': data_record, + 'store_fname': 'invoice.pdf', + 'mimetype': 'application/pdf', + 'res_model': 'account.move', + 'res_id': generate_refund.id, + } + invoice_report_attachment_id = self.env[ + 'ir.attachment'].sudo().create(ir_values) + email_values = { + 'email_to': self.customer_id.email, + 'email_from': self.env.user.email, + 'attachment_ids': [invoice_report_attachment_id.id] + } + refund_approved.send_mail(self.id, email_values=email_values, + force_send=True) + refund_approved.attachment_ids = fields.Command.clear() + else: + invoice.button_cancel() + generate_invoice = self.env['account.move'].sudo().create({ + 'move_type': 'out_invoice', + 'partner_id': self.customer_id.id, + 'invoice_date': self.date, + 'invoice_line_ids': [fields.Command.create({ + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': per_day_price * invoice_duration, + })] + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env['ir.actions.report'].sudo()._render_qweb_pdf( + "account.account_invoices", generate_invoice.ids)[0]) + ir_values = { + 'name': 'Invoice', + 'type': 'binary', + 'datas': data_record, + 'store_fname': 'invoice.pdf', + 'mimetype': 'application/pdf', + 'res_model': 'account.move', + 'res_id': generate_invoice.id, + } + invoice_report_attachment_id = self.env[ + 'ir.attachment'].sudo().create(ir_values) + email_values = { + 'email_to': self.customer_id.email, + 'email_from': self.env.user.email, + 'attachment_ids': [invoice_report_attachment_id.id] + } + email_template.send_mail(self.id, email_values=email_values, + force_send=True) + email_template.attachment_ids = [] + subscription.invoice_ids = fields.Command.link(generate_invoice.id) + subscription.sale_id.write({ + 'invoice_ids': fields.Command.link(generate_invoice.id) + }) diff --git a/vehicle_subscription/models/fleet_subscription.py b/vehicle_subscription/models/fleet_subscription.py new file mode 100644 index 000000000..d116beece --- /dev/null +++ b/vehicle_subscription/models/fleet_subscription.py @@ -0,0 +1,385 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 datetime import datetime +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class VehicleSubscription(models.Model): + """Created new model to add new fields and function""" + _name = "fleet.subscription" + _description = "Fleet Subscription" + _inherit = "mail.thread" + _rec_name = 'vehicle_id' + + vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", + domain="[('id', 'in',vehicle_ids)]", + help="This field help you to choose vehicle", + required=True) + vehicle_ids = fields.Many2many('fleet.vehicle', string="Vehicle", + compute='_compute_vehicle_ids', + help="Returns vehicle by satisfying " + "the domain") + model_id = fields.Many2one(related="vehicle_id.model_id", string='Model', + help="This field help you to choose model " + "of vehicle") + price = fields.Float(compute="_compute_price", string='Price', + help="Compute field which results the price of vehicle") + uptodate_price = fields.Float(compute="_compute_uptodate_price", + string='Price', + help="Compute field which results the price" + "of vehicle until the date ") + extra_price = fields.Float(string="Extra Price", + compute="_compute_extra_price", + help="Compute field which results the extra " + "price of vehicle") + start_date = fields.Date(string="Start Date", required=True, + help="Start date of subscription") + end_date = fields.Date(string="End Date", required=True, + help="End date of subscription") + cancellation_date = fields.Date(string="Cancellation Date", + default=fields.Date.today(), + help="Subscription cancellation date") + duration = fields.Integer(string="Duration", compute='_compute_duration', + help="Compute subscription duration") + cancel_duration = fields.Integer(string="Duration", + compute='_compute_cancel_duration', + help="compute cancel duration") + state = fields.Selection( + selection=[('draft', 'Draft'), ('subscribed', 'Subscribed'), + ('cancel', 'Cancelled'), ('expired', 'Expired') + ], string='State', default='draft', + help="States of subscription") + street = fields.Char(string="Street", help="Choose the street") + state_id = fields.Many2one("res.country.state", string='State', + ondelete='restrict', + domain="[('country_id', '=?', country_id)]", + help="Choose the state") + city = fields.Char(string="City", help="Choose the city") + country_id = fields.Many2one('res.country', string='Country', + ondelete='restrict', + help="Choose the country") + fuel = fields.Selection(selection=[('with_fuel', 'With Fuel'), + ('without_fuel', 'Without Fuel')], + string="Fuel Choice", default='without_fuel', + help="Help you to choose the type of fuel") + fuel_type = fields.Selection(string="Fuel Type", + related= + "vehicle_id.model_id.default_fuel_type", + help="Fuel type will be given which is related" + " to the model") + fuel_rate = fields.Integer(String="Rate", default=300, help="Rate of fuel") + charge_km = fields.Integer(string="Charge in km", default=12, + help="Rate per kilometer") + default_km = fields.Float(string="Default KMS", + related='vehicle_id.free_km', + help="Default km is set based on free km of " + "vehicle which is given by authorised " + "person") + extra_km = fields.Float(string="Extra KMS", default_km=1, + help="As per customer he/she can choose extra km") + mileage = fields.Float(string='Mileage', + related='vehicle_id.model_id.mileage', + help="Helps to set mileage of vehicle") + sale = fields.Integer(string="sale", compute='_compute_sale', + help="Helps you to store count of sale") + invoice = fields.Integer(string="Invoice", compute='_compute_invoice', + help="Helps you to store count of invoice") + invoice_ids = fields.Many2many('account.move', string='Invoices', + help="Used to store ids of invoices") + customer_id = fields.Many2one('res.partner', string="Customer", + help="Helps you to choose customer") + sale_id = fields.Many2one('sale.order', string='sale', readonly=True, + help="Stores id of sale order") + refund_id = fields.Many2one('account.move', string='Refund', readonly=True, + help="Stores id of invoice which belongs " + "to refund") + insurance_type_id = fields.Many2one('vehicle.insurance', + domain= + "[('vehicle_id', '=',vehicle_id)]") + refund = fields.Integer(compute='_compute_refund', + help="Helps you to store count of refund") + seating_capacity = fields.Integer(string='Seating Capacity', + help="Seating capacity of vehicle can " + "be set") + invisible_sub = fields.Boolean(string="Approve Subscription", + help="As subscription request get approved " + "this field will be enabled") + + def _get_vehicle_domain(self): + """This method retrieves the vehicles that meet the following + criteria""" + insurance_ids = self.env['vehicle.insurance'].search([]).mapped( + 'vehicle_id') + domain = [] + for record in insurance_ids: + state = record.log_services.mapped('state') + if 'done' in state and 'running' not in state and 'new' \ + not in state and 'cancelled' not in state: + if not self.search( + [('vehicle_id', '=', record.id), + ('state', '!=', 'subscribe')]): + domain.append(record.id) + return domain + + @api.onchange('vehicle_id') + def _onchange_vehicle_id(self): + """Function used to fill the seating capacity""" + if self.vehicle_id: + self.seating_capacity = self.vehicle_id.model_id.seats + + @api.onchange('seating_capacity') + def _onchange_seating_capacity(self): + """As the seating capacity changes vehicles are shown """ + if self.seating_capacity != self.vehicle_id.model_id.seats: + self.vehicle_id = False + + @api.onchange('default_km') + def _onchange_default_km(self): + """Charge per km is set as onchange of default_km""" + if self.default_km <= self.vehicle_id.free_km: + self.charge_km = 0 + + @api.depends('vehicle_id', 'seating_capacity') + def _compute_vehicle_ids(self): + """Compute the vehicle_IDS based on the vehicle and seating + capacity.""" + for rec in self: + if not rec.vehicle_ids: + domain = rec._get_vehicle_domain() + if rec.seating_capacity: + model_id = self.env['fleet.vehicle'].search( + [('state_id', '=', 'registered'), + ('model_id.seats', '=', rec.seating_capacity), + ('id', 'in', domain)]) + else: + model_id = self.env['fleet.vehicle'].search( + [('id', 'in', domain)]) + for record in model_id: + rec.vehicle_ids = [(4, record.id)] + + @api.depends('start_date', 'end_date') + def _compute_duration(self): + """Compute duration based on start and end date""" + for record in self: + if record.end_date: + if record.end_date < record.start_date: + raise ValidationError(_( + "End date should be greater than start date.")) + if record.start_date and record.end_date: + start = record.start_date.strftime("%Y-%m-%d") + end = record.end_date.strftime("%Y-%m-%d") + start_datetime = datetime.strptime(start, "%Y-%m-%d") + end_datetime = datetime.strptime(end, "%Y-%m-%d") + delta = end_datetime - start_datetime + record.duration = delta.days + else: + record.duration = 0 + + @api.depends('start_date', 'cancellation_date') + def _compute_cancel_duration(self): + """Compute duration based on cancellation date""" + for record in self: + if record.start_date and record.cancellation_date: + start = record.start_date.strftime("%Y-%m-%d") + end = record.cancellation_date.strftime("%Y-%m-%d") + start_datetime = datetime.strptime(start, "%Y-%m-%d") + end_datetime = datetime.strptime(end, "%Y-%m-%d") + delta = end_datetime - start_datetime + record.cancel_duration = delta.days + else: + record.cancel_duration = 0 + + @api.depends('extra_km', 'charge_km', 'fuel_rate', 'fuel') + def _compute_extra_price(self): + """Compute extra charges based on criteria""" + for rec in self: + if rec.fuel == 'without_fuel': + rec.extra_price = (rec.extra_km * rec.charge_km) + elif rec.mileage == 0: + raise ValidationError(_("Mileage cannot be zero.")) + else: + rec.extra_price = ( + (rec.extra_km / rec.mileage) * rec.fuel_rate) + + @api.depends('duration') + def _compute_price(self): + """Function used to compute price of vehicle""" + for rec in self: + rec.price = (rec.duration * rec.vehicle_id.subscription_price) \ + + rec.insurance_type_id.insurance_amount + + @api.depends('cancel_duration') + def _compute_uptodate_price(self): + """Compute price as per the cancellation date""" + for rec in self: + rec.uptodate_price = ( + (rec.sale_id.order_line.price_unit / rec.duration) * ( + (rec.cancellation_date - rec.start_date).days)) + + def action_get_car_insurance(self): + """Get the action to view the car + insurance associated with the subscription.""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Insurance', + 'view_mode': 'form', + 'res_model': 'vehicle.insurance', + 'res_id': self.insurance_type_id.id, + 'context': [('create', '=', False)] + } + def action_get_sale(self): + """Get the action to view the sale + associated with the subscription.""" + return { + 'type': 'ir.actions.act_window', + 'name': 'Sale Order', + 'view_mode': 'form', + 'res_model': 'sale.order', + 'res_id': self.sale_id.id, + 'context': [('create', '=', False)] + } + + def _compute_sale(self): + """Used to calculate the sale count""" + for record in self: + record.sale = self.env['sale.order'].search_count( + [('id', '=', self.sale_id.id)]) + + def action_get_refund(self): + """Get the action to view the refund + associated with the subscription.""" + return { + 'type': 'ir.actions.act_window', + 'name': 'Refund', + 'view_mode': 'form', + 'res_id': self.refund_id.id, + 'res_model': 'account.move', + 'context': [('create', '=', False)] + } + + def _compute_refund(self): + """Used to calculate count of refund""" + for record in self: + record.refund = self.env['account.move'].search_count( + [('id', '=', self.refund_id.id)]) + + def action_get_invoice(self): + """Get the action to view the invoice + associated with the subscription.""" + invoice_ids = self.invoice_ids + self.sale_id.invoice_ids + return { + 'type': 'ir.actions.act_window', + 'name': 'Sale Order', + 'view_mode': 'tree,form', + 'res_model': 'account.move', + 'domain': [('id', 'in', invoice_ids.ids)], + 'context': [('create', '=', False)] + } + + def _compute_invoice(self): + """Used to calculate invoice count""" + for record in self: + invoice_ids = record.invoice_ids + record.sale_id.invoice_ids + record.invoice = self.env['account.move'].search_count( + [('id', 'in', invoice_ids.ids)]) + + def action_invoice(self): + """Used to generate invoice on clicking the button""" + self.write({'state': 'subscribed'}) + product_template_id = self.env.ref( + 'vehicle_subscription.product_template_vehicle_subscription_form').id + product_id = self.env['product.product'].search( + [('product_tmpl_id', '=', product_template_id)]) + sale_order_id = self.env['sale.order'].create({ + 'partner_id': self.customer_id.id, + 'order_line': [(0, 0, { + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': self.price + self.extra_price, + })] + }) + self.sale_id = sale_order_id + def action_request(self): + """Request for change subscription is generated """ + return { + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'change.subscription', + 'target': 'new', + } + + def action_cancel(self): + """Proceed with cancellation of subscription""" + product_template_id = self.env.ref( + 'vehicle_subscription.product_template_vehicle_subscription_form').id + product_id = self.env['product.product'].search( + [('product_tmpl_id', '=', product_template_id)], limit=1) + invoice = self.env['account.move'].search( + [('id', 'in', self.invoice_ids.ids), + ('payment_state', 'in', ['paid', 'partial'])]).mapped( + 'amount_untaxed_signed') + invoiced_amount = sum(invoice) + total_price = self.uptodate_price + if invoiced_amount == total_price: + self.write({'state': 'cancel'}) + self.sale_id.action_done() + elif invoiced_amount > total_price: + self.write({'state': 'cancel'}) + self.refund_id = self.env['account.move'].create({ + 'move_type': 'out_refund', + 'invoice_date': fields.Date.today(), + 'partner_id': self.customer_id.id, + 'invoice_line_ids': [fields.Command.create({ + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': self.uptodate_price + self.extra_price, + })] + }) + else: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Warning'), + 'message': _( + 'You need to pay the amount till date in order to ' + 'cancel the subscription'), + 'sticky': True, + } + } + + @api.onchange('end_date') + def _onchange_end_date(self): + """Check expiry for subscription""" + if self.end_date: + if self.end_date < fields.Date.today(): + self.write({'state': 'expired'}) + + @api.constrains('start_date', 'end_date') + def _check_dates(self): + """Ensure that the start date is not greater than the end date.""" + for rec in self: + if rec.start_date > rec.end_date: + raise ValidationError( + "Start Date cannot be greater than End Date") diff --git a/vehicle_subscription/models/fleet_vehicle.py b/vehicle_subscription/models/fleet_vehicle.py new file mode 100644 index 000000000..fccb75ba3 --- /dev/null +++ b/vehicle_subscription/models/fleet_vehicle.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 datetime import datetime +from odoo import api, fields, models + + +class FleetVehicle(models.Model): + """Inherited model to add fields and functions""" + _inherit = 'fleet.vehicle' + + free_km = fields.Float(string="Free KM", required=True, default=1500, + help="Set free km for each vehicle") + subscription_price = fields.Float(string="Subscription price per day", + help='Price of vehicle per day', + required=True, default=12) + states_id = fields.Many2one("res.country.state", string='State', + help="Help you choose the state") + countries_id = fields.Many2one('res.country', string='Country', + help="help you to choose country") + insurance = fields.Char(string="Insurance", + help="Helps you to set Insurance") + start = fields.Date(string="Start Date", + help="Helps you to choose start date") + end = fields.Date(string="End Date", help="Helps you to choose end date") + duration = fields.Integer(string="Duration", compute='_compute_duration') + fuel = fields.Selection(selection=[('with_fuel', 'With Fuel'), + ('without_fuel', 'Without Fuel')], + string="Fuel Choice", default='without_fuel', + help="Help you to choose the type of fuel") + fuel_rate = fields.Integer(String="Rate", default=300, help="Rate of fuel") + charge_km = fields.Integer(string="Charge in km", default=12, + help="Rate per kilometer") + extra_km = fields.Float(string="Extra KMS", default=1500, + help="As per customer he/she can choose extra km") + mileage = fields.Float(related='model_id.mileage', string='Mileage', + help="Helps to set mileage of vehicle") + + @api.depends('start', 'end') + def _compute_duration(self): + """Compute duration of days based on start and end date""" + for record in self: + if record.start and record.end: + start = record.start.strftime("%Y-%m-%d") + end = record.end.strftime("%Y-%m-%d") + start_datetime = datetime.strptime(start, "%Y-%m-%d") + end_datetime = datetime.strptime(end, "%Y-%m-%d") + delta = end_datetime - start_datetime + record.duration = delta.days + else: + record.duration = 0 diff --git a/vehicle_subscription/models/fleet_vehicle_model.py b/vehicle_subscription/models/fleet_vehicle_model.py new file mode 100644 index 000000000..c0e25f9c2 --- /dev/null +++ b/vehicle_subscription/models/fleet_vehicle_model.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 FleetVehicleModel(models.Model): + """Inherited fleet.vehicle.model to add fields""" + _inherit = 'fleet.vehicle.model' + + mileage = fields.Float(string="Mileage", required=True, + default=12, help="Helps you to set mileage for " + "vehicle") + seats = fields.Integer(string='Seats Number', required=True, + default=4, helps="Helps you to choose seating " + "capacity") diff --git a/vehicle_subscription/models/insurance_type.py b/vehicle_subscription/models/insurance_type.py new file mode 100644 index 000000000..9656e2790 --- /dev/null +++ b/vehicle_subscription/models/insurance_type.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 InsuranceType(models.Model): + """New model insurance type to add fields""" + _name = "insurance.type" + _description = "Insurance type" + _inherit = "mail.thread" + + name = fields.Char(string="Insurance Name", required=True, + help="This field is used to set name for insurance") + coverage_ids = fields.One2many('insurance.coverage', 'coverage_id', + string="Coverage", + help="Helps you to give details of coverage") + + +class InsuranceCoverageType(models.Model): + """One2many field for insurance type""" + _name = 'insurance.coverage' + _description = "Insurance Coverage" + + description = fields.Char(string="Description", help="Detail of coverage", + required=True) + coverage_price = fields.Float(string="Price", help="Rate of insurance", + required=True) + coverage_id = fields.Many2one('insurance.type', + help="Can choose insurance type") diff --git a/vehicle_subscription/models/subscription_request.py b/vehicle_subscription/models/subscription_request.py new file mode 100644 index 000000000..81fb1438b --- /dev/null +++ b/vehicle_subscription/models/subscription_request.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 SubscriptionRequest(models.Model): + """New model subscription.request""" + _name = "subscription.request" + _description = "Subscription Request" + _inherit = "mail.thread" + _rec_name = "new_vehicle_id" + + customer_id = fields.Many2one('res.partner', string="Customer", + help="Choose the customer for subscription " + "request") + sale_id = fields.Many2one('sale.order', string='sale', readonly=True, + help="Helps you to store sale order") + refund_id = fields.Many2one('account.move', string='Refund', readonly=True) + current_vehicle_id = fields.Many2one('fleet.vehicle', + string="Current Vehicle", + help="Currently using vehicle of " + "customer will be set", + required=True) + new_vehicle_id = fields.Many2one('fleet.vehicle', string="New Vehicle" + , domain="[('id', 'in', vehicle_ids)]", + help="Can choose different vehicle " + "with same model", required=True) + vehicle_ids = fields.Many2many('fleet.vehicle', + compute='_compute_vehicle_ids', + help="Compute and can choose vehicle with " + "satisfying domain ") + reason_to_change = fields.Char(string="Reason", required=True, + help="Reason for changing vehicle") + state = fields.Selection( + selection=[('to_approve', 'To Approve'), + ('approved', 'Approved'), + ], string='State', default='to_approve', + help="States of subscription") + is_subscription = fields.Boolean(string="Subscription", + help="Online Subscriptions") + + @api.depends('current_vehicle_id') + def _compute_vehicle_ids(self): + """This method searches for vehicles with the same model and brand + as the current vehicle, excluding the current vehicle itself. + The vehicle IDs are updated accordingly, and the state + is set to 'to_approve'.""" + self.vehicle_ids = False + model_id = self.env['fleet.vehicle'].search( + [('model_id', '=', self.current_vehicle_id.model_id.id), + ('model_id.brand_id', '=', + self.current_vehicle_id.model_id.brand_id.id), + ('id', '!=', self.current_vehicle_id.id)]) + for record in model_id: + self.vehicle_ids = [(4, record.id)] + self.write({'state': 'to_approve'}) + + def action_approve(self): + """ Process the approval of the subscription request.""" + subscription = self.env['fleet.subscription'].search( + [('vehicle_id', '=', self.current_vehicle_id.id), + ('state', '=', 'subscribed')]) + subscription.update({ + 'vehicle_id': self.new_vehicle_id, + 'invisible_sub': True, + }) + self.write({'state': 'approved'}) + sale_order = subscription.sale_id + if sale_order.order_line: + sale_order.order_line[0].name = self.new_vehicle_id.name + invoice_ids = subscription.invoice_ids + for rec in invoice_ids: + if rec.invoice_line_ids: + rec.invoice_line_ids[0].name = self.new_vehicle_id.name + subscription.write({'sale_id': sale_order.id}) + subscription.write({'invoice_ids': invoice_ids.ids}) diff --git a/vehicle_subscription/models/vehicle_insurance.py b/vehicle_subscription/models/vehicle_insurance.py new file mode 100644 index 000000000..5d9f32d7e --- /dev/null +++ b/vehicle_subscription/models/vehicle_insurance.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 +from odoo.exceptions import ValidationError + + +class VehicleInsurance(models.Model): + """New model vehicle .insurance""" + _name = "vehicle.insurance" + _description = "Vehicle Insurance" + _inherit = "mail.thread" + _rec_name = "vehicle_id" + + vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", + help="Help you to choose vehicle", + required=True) + start_date = fields.Date(string="Start Date", help="Insurance start date", + required=True) + end_date = fields.Date(string="End Date", help="Insurance end date", + required=True) + insurance_type_id = fields.Many2one('insurance.type', + string='Insurance Type', + help="Choose insurance type") + insurance_amount = fields.Float(string="Amount", + compute="_compute_insurance_amount", + help="Calculate insurance amount") + + def _compute_insurance_amount(self): + """Function used to compute insurance amount""" + for rec in self: + rec.insurance_amount = sum(rec.insurance_type_id.coverage_ids. + mapped('coverage_price')) + + @api.constrains('start_date', 'end_date') + def _check_dates(self): + """Ensure that the start date is not greater than the end date.""" + for rec in self: + if rec.start_date > rec.end_date: + raise ValidationError( + "Start Date cannot be greater than End Date") diff --git a/vehicle_subscription/security/ir.model.access.csv b/vehicle_subscription/security/ir.model.access.csv new file mode 100644 index 000000000..d13232776 --- /dev/null +++ b/vehicle_subscription/security/ir.model.access.csv @@ -0,0 +1,8 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_fleet_subscription,access.fleet.subscription,model_fleet_subscription,base.group_user,1,1,1,1 +access_vehicle_insurance,access.vehicle.insurance,model_vehicle_insurance,base.group_user,1,1,1,1 +access_subscription_request,access.subscription.request,model_subscription_request,base.group_user,1,1,1,1 +access_change_subscription,access.change.subscription,model_change_subscription,base.group_user,1,1,1,1 +access_insurance_type,access.insurance.type,model_insurance_type,base.group_user,1,1,1,1 +access_insurance_coverage,access.insurance.coverage,model_insurance_coverage,base.group_user,1,1,1,1 +access_cancellation_request,access.cancellation.request,model_cancellation_request,base.group_user,1,1,1,1 diff --git a/vehicle_subscription/security/vehicle_subscription_groups.xml b/vehicle_subscription/security/vehicle_subscription_groups.xml new file mode 100644 index 000000000..425b5bb20 --- /dev/null +++ b/vehicle_subscription/security/vehicle_subscription_groups.xml @@ -0,0 +1,17 @@ + + + + + Vehicle Subscription + User access levels for Fleet module + 10 + + + User + + + + Manager + + + diff --git a/vehicle_subscription/static/description/assets/icons/capture (1).png b/vehicle_subscription/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/capture (1).png differ diff --git a/vehicle_subscription/static/description/assets/icons/check.png b/vehicle_subscription/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/check.png differ diff --git a/vehicle_subscription/static/description/assets/icons/chevron.png b/vehicle_subscription/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/chevron.png differ diff --git a/vehicle_subscription/static/description/assets/icons/cogs.png b/vehicle_subscription/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/cogs.png differ diff --git a/vehicle_subscription/static/description/assets/icons/consultation.png b/vehicle_subscription/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/consultation.png differ diff --git a/vehicle_subscription/static/description/assets/icons/ecom-black.png b/vehicle_subscription/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/ecom-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/education-black.png b/vehicle_subscription/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/education-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/hotel-black.png b/vehicle_subscription/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/hotel-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/img.png b/vehicle_subscription/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/img.png differ diff --git a/vehicle_subscription/static/description/assets/icons/license.png b/vehicle_subscription/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/license.png differ diff --git a/vehicle_subscription/static/description/assets/icons/lifebuoy.png b/vehicle_subscription/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/lifebuoy.png differ diff --git a/vehicle_subscription/static/description/assets/icons/manufacturing-black.png b/vehicle_subscription/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/manufacturing-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/photo-capture.png b/vehicle_subscription/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/photo-capture.png differ diff --git a/vehicle_subscription/static/description/assets/icons/pos-black.png b/vehicle_subscription/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/pos-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/puzzle.png b/vehicle_subscription/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/puzzle.png differ diff --git a/vehicle_subscription/static/description/assets/icons/restaurant-black.png b/vehicle_subscription/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/restaurant-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/service-black.png b/vehicle_subscription/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/service-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/trading-black.png b/vehicle_subscription/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/trading-black.png differ diff --git a/vehicle_subscription/static/description/assets/icons/training.png b/vehicle_subscription/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/training.png differ diff --git a/vehicle_subscription/static/description/assets/icons/update.png b/vehicle_subscription/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/update.png differ diff --git a/vehicle_subscription/static/description/assets/icons/user.png b/vehicle_subscription/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/user.png differ diff --git a/vehicle_subscription/static/description/assets/icons/wrench.png b/vehicle_subscription/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/vehicle_subscription/static/description/assets/icons/wrench.png differ diff --git a/vehicle_subscription/static/description/assets/misc/Cybrosys R.png b/vehicle_subscription/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/Cybrosys R.png differ diff --git a/vehicle_subscription/static/description/assets/misc/categories.png b/vehicle_subscription/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/categories.png differ diff --git a/vehicle_subscription/static/description/assets/misc/check-box.png b/vehicle_subscription/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/check-box.png differ diff --git a/vehicle_subscription/static/description/assets/misc/compass.png b/vehicle_subscription/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/compass.png differ diff --git a/vehicle_subscription/static/description/assets/misc/corporate.png b/vehicle_subscription/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/corporate.png differ diff --git a/vehicle_subscription/static/description/assets/misc/customer-support.png b/vehicle_subscription/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/customer-support.png differ diff --git a/vehicle_subscription/static/description/assets/misc/cybrosys-logo.png b/vehicle_subscription/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/cybrosys-logo.png differ diff --git a/vehicle_subscription/static/description/assets/misc/email.svg b/vehicle_subscription/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/features.png b/vehicle_subscription/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/features.png differ diff --git a/vehicle_subscription/static/description/assets/misc/logo.png b/vehicle_subscription/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/logo.png differ diff --git a/vehicle_subscription/static/description/assets/misc/phone.svg b/vehicle_subscription/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/vehicle_subscription/static/description/assets/misc/pictures.png b/vehicle_subscription/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/pictures.png differ diff --git a/vehicle_subscription/static/description/assets/misc/pie-chart.png b/vehicle_subscription/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/pie-chart.png differ diff --git a/vehicle_subscription/static/description/assets/misc/right-arrow.png b/vehicle_subscription/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/right-arrow.png differ diff --git a/vehicle_subscription/static/description/assets/misc/star (1) 2.svg b/vehicle_subscription/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/star.png b/vehicle_subscription/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/star.png differ diff --git a/vehicle_subscription/static/description/assets/misc/support (1) 1.svg b/vehicle_subscription/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/support-email.svg b/vehicle_subscription/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/support.png b/vehicle_subscription/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/support.png differ diff --git a/vehicle_subscription/static/description/assets/misc/tick-mark.svg b/vehicle_subscription/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/whatsapp 1.svg b/vehicle_subscription/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/misc/whatsapp.png b/vehicle_subscription/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/whatsapp.png differ diff --git a/vehicle_subscription/static/description/assets/misc/whatsapp.svg b/vehicle_subscription/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/vehicle_subscription/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vehicle_subscription/static/description/assets/modules/1.jpg b/vehicle_subscription/static/description/assets/modules/1.jpg new file mode 100644 index 000000000..5141a7802 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/1.jpg differ diff --git a/vehicle_subscription/static/description/assets/modules/2.jpg b/vehicle_subscription/static/description/assets/modules/2.jpg new file mode 100644 index 000000000..39006b41a Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/2.jpg differ diff --git a/vehicle_subscription/static/description/assets/modules/3.png b/vehicle_subscription/static/description/assets/modules/3.png new file mode 100644 index 000000000..ae33208db Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/3.png differ diff --git a/vehicle_subscription/static/description/assets/modules/4.jpg b/vehicle_subscription/static/description/assets/modules/4.jpg new file mode 100644 index 000000000..12a613059 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/4.jpg differ diff --git a/vehicle_subscription/static/description/assets/modules/5.jpg b/vehicle_subscription/static/description/assets/modules/5.jpg new file mode 100644 index 000000000..53b053f74 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/5.jpg differ diff --git a/vehicle_subscription/static/description/assets/modules/6.jpg b/vehicle_subscription/static/description/assets/modules/6.jpg new file mode 100644 index 000000000..73781cf50 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/6.jpg differ diff --git a/vehicle_subscription/static/description/assets/screenshots/0.png b/vehicle_subscription/static/description/assets/screenshots/0.png new file mode 100644 index 000000000..6b58dc17d Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/0.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/1.png b/vehicle_subscription/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..49d3c02d9 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/1.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/10.png b/vehicle_subscription/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..d5a618c62 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/10.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/11.png b/vehicle_subscription/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..42da07069 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/11.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/12.png b/vehicle_subscription/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..06f9d9d92 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/12.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/13.png b/vehicle_subscription/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..e68b26259 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/13.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/14.png b/vehicle_subscription/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..ce8dfba73 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/14.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/15.png b/vehicle_subscription/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..e2b6bb440 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/15.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/16.png b/vehicle_subscription/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..9e8630e24 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/16.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/17.png b/vehicle_subscription/static/description/assets/screenshots/17.png new file mode 100644 index 000000000..e2e18ad2f Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/17.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/18.png b/vehicle_subscription/static/description/assets/screenshots/18.png new file mode 100644 index 000000000..7d0715bfa Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/18.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/19.png b/vehicle_subscription/static/description/assets/screenshots/19.png new file mode 100644 index 000000000..b508b59ce Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/19.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/2.png b/vehicle_subscription/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..1cc2943ae Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/2.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/3.png b/vehicle_subscription/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..3886cb2ab Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/3.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/4.png b/vehicle_subscription/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..9f432094c Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/4.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/5.png b/vehicle_subscription/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..ff5237f9c Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/5.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/6.png b/vehicle_subscription/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..2813180d2 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/6.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/7.png b/vehicle_subscription/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..0dc002452 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/7.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/8.png b/vehicle_subscription/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..1ddbbaf1a Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/8.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/9.png b/vehicle_subscription/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..a74f1be2d Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/9.png differ diff --git a/vehicle_subscription/static/description/assets/screenshots/hero.gif b/vehicle_subscription/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..b55c5bcce Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/hero.gif differ diff --git a/vehicle_subscription/static/description/banner.jpg b/vehicle_subscription/static/description/banner.jpg new file mode 100644 index 000000000..c5a77fea1 Binary files /dev/null and b/vehicle_subscription/static/description/banner.jpg differ diff --git a/vehicle_subscription/static/description/icon.png b/vehicle_subscription/static/description/icon.png new file mode 100644 index 000000000..cf5237b6b Binary files /dev/null and b/vehicle_subscription/static/description/icon.png differ diff --git a/vehicle_subscription/static/description/index.html b/vehicle_subscription/static/description/index.html new file mode 100644 index 000000000..68d7c10a7 --- /dev/null +++ b/vehicle_subscription/static/description/index.html @@ -0,0 +1,1063 @@ + + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+
+
+
+
+

+ Vehicle Subscription

+

+ A Module for Managing Vehicle + Subscription,Change and Cancellation +

+
+ +
+
+
+
+
+

+ Key Highlights +

+
+
+
+
+
+ +
+
+

+ Odoo 17 Community & Enterprise Edition + Support.

+
+
+
+
+
+
+ +
+
+

+ Subscription of vehicle

+ +
+
+
+
+
+
+ +
+
+

+ Cancellation of subscription

+
+
+
+
+
+
+ +
+
+

+ Option to switch subscription

+
+
+
+
+
+
+ +
+
+

+ Payment option Monthly and Full Payment from website

+
+
+
+
+
+
+ +
+
+

+ Down Payment

+
+
+
+
+
+
+ +
+
+

+ Option to choose vehicle based on Location

+
+
+
+
+
+
+ +
+
+

+ Can decide Insurance type

+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ Vehicle Subscription Access Right

+
+ Go to user -> + and choose the access right for respective user
+
+
+
+
+
+
+ +
+
+

+ Vehicle Subscription

+
+ Go to Website -> + Subscription Form -> + Choose Location , Insurance and duration of subscription.
+
You can configure the insurance type from fleet -> insurance . +
+
+
+
+
+
+
+ +
+
+

+ Choose Vehicle

+
+ Book vehicle from + listed vehicle user can change default km choose fuel type ,choose payment type. +
+
+
+
+
+
+
+ +
+
+
+ After Successful Vehicle Choose , You can see like this: +
+
+
+
+
+
+
+ +
+
+

+ Subscription Order

+
+ Go to Fleet + -> Under Fleet Menu ->In Subscription submenu -> Subscription Order get created
+
+
+
+
+
+
+ +
+
+

+ Subscription order in + Portal view.

+
+ Go to My Account -> + Subscription Order -> User can see their invoices so that they can pay change and cancel subscription + from invoice
+
+
+
+
+
+
+ +
+
+

+ Cancel subscription

+
+
+
+
+
+
+ +
+
+

+ Cancellation form

+
+ Specify the Reason
+
+
+
+
+
+
+ +
+
+
After Successful Cancellation , you can see like this: +
+
+
+
+
+
+
+ +
+
+

+ Cancel Request

+
+ By submitting the + cancellation form cancel request will be generated in backend.
+
+
+
+
+
+
+ +
+
+

+ Approve Cancellation

+
+ Manager user can approve the cancellation
+
+
+
+
+
+
+ +
+
+
+ After approving an email will send to the user
+
+
+
+
+
+
+ +
+
+

+ Switch Subscription

+
+ From portal , user have + option to Change Subscription.
+
+
+
+
+
+
+ +
+
+

+ Change Subscription

+
+ User need to choose + whether he or she needs to change for same model vehicle or different model and need to specify the + reason.
+
+
+
+
+
+
+ +
+
+

+ Switch Subscription + to new vehicle

+
+ If user chosen same + model, then user will redirect to the different form where user can choose vehicle of same model .
+
+
+
+
+
+
+ +
+
+

+ Switch Request

+
+ Go to Fleet -> + Configuration -> Change Subscription -> Request for switching subscription will be generated
+
+
+
+
+ +
+
+
    +
  • + Subscription of vehicle +
  • +
  • + Cancellation of subscription +
  • +
  • + Option to switch subscription +
  • +
  • + Payment option Monthly and Full Payment from website +
  • +
  • + Down Payment +
  • +
  • + Option to choose vehicle based on Location +
  • +
  • + Can decide Insurance type +
  • + +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on:20th September 2024 +
+

+ Vehicle Subscription.

+
+
+
+
+
+
+
+

+ Related Products

+
+
+ +
+
+

+ Our Services

+ +
+
+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Customization

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Support

+
+
+
+
+
+
+ service-icon +
+
+

Hire + Odoo Developer

+
+
+
+
+ +
+
+ service-icon +
+
+

Odoo + Integration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Migration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Consultancy

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Licensing Consultancy

+
+
+
+
+
+
+

+ Our Industries

+ +
+
+
+
+
+
+ +

Trading

+

Easily procure and sell your products

+
+
+
+
+ +

POS

+

Easy configuration and convivial experience

+
+
+
+
+ +

+ Education

+

A platform for educational management

+
+
+
+
+ +

+ Manufacturing

+

Plan, track and schedule your operations

+
+
+
+
+ +

E-commerce + & Website

+

Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +

Service + Management

+

Keep track of services and invoice

+
+
+
+
+ +

+ Restaurant

+

Run your bar or restaurant methodically

+
+
+
+
+ +

Hotel + Management

+

An all-inclusive hotel management application

+
+
+
+
+
+
+

+ Support

+
+
+
+
+
+
+
+ +
+ Need + Help? +

Got + questions or need help? Get in touch.

+
odoo@cybrosys.com +
+
+
+
+
+
+
+
+ +
+ WhatsApp +

Say hi + to us on WhatsApp!

+
+91 + 99456767686 +
+
+
+
+
+
+
+
+ + + + + + diff --git a/vehicle_subscription/static/src/js/change_subscription_form.js b/vehicle_subscription/static/src/js/change_subscription_form.js new file mode 100644 index 000000000..34275ce91 --- /dev/null +++ b/vehicle_subscription/static/src/js/change_subscription_form.js @@ -0,0 +1,31 @@ +odoo.define('vehicle_subscription.subscription_change', function (require) { + "use strict"; +var publicWidget = require('web.public.widget'); +const ajax = require('web.ajax'); +publicWidget.registry.Change = publicWidget.Widget.extend({ + selector: '.change_sub_vehicle', + start: function() { + this._super.apply(this, arguments); + this._onChangeCustomer(); // Call the function initially + }, + //On the onchange function customer is passed to controller + _onChangeCustomer: async function(ev){ + var self=this; + var customer_id = this.$('input[name="customer"]')[0].value + awaitajax.jsonRpc('/online/choose/vehicle', "call", { + 'customer_id': customer_id, + }) + .then(function(result) { + const select = self.$el.find('#vehicle_change')[0]; + const options = Array.from(select.options); + options.forEach((option) => { + option.remove(); + }); + result.forEach((item) => { + let newOption = new Option(item[1], item[0]); + select.add(newOption, undefined); + }); + }); + }, +}) +}) diff --git a/vehicle_subscription/static/src/js/change_subscription_request.js b/vehicle_subscription/static/src/js/change_subscription_request.js new file mode 100644 index 000000000..f2e3888f9 --- /dev/null +++ b/vehicle_subscription/static/src/js/change_subscription_request.js @@ -0,0 +1,19 @@ +odoo.define('vehicle_subscription.subscription_submit_request', function (require) { + "use strict"; +var publicWidget = require('web.public.widget'); +const ajax = require('web.ajax'); +//To getitem to get vehicle. +$(function() { + if (localStorage.getItem('current_vehicle')) { + $('#current_vehicle').val(localStorage.getItem('current_vehicle')); + } +}); +publicWidget.registry.Request = publicWidget.Widget.extend({ + selector: '.submit_boolean_on', + start: function() { + var self = this;//setitem to store the element. + var current_vehicle = self.$('#current_vehicle').val(); + localStorage.setItem('current_vehicle', current_vehicle); + } +}) +}) diff --git a/vehicle_subscription/static/src/js/subscription_cancellation.js b/vehicle_subscription/static/src/js/subscription_cancellation.js new file mode 100644 index 000000000..6f7160810 --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_cancellation.js @@ -0,0 +1,31 @@ +odoo.define('vehicle_subscription.subscription_cancellation', function (require) { + "use strict"; +var publicWidget = require('web.public.widget'); +const ajax = require('web.ajax'); +publicWidget.registry.Cancellation = publicWidget.Widget.extend({ + selector: '.cancel_sub', + start: function() { + this._super.apply(this, arguments); + this._onChangeCustomer(); // Call the function initially + }, + //On the onchange function customer is passed to controller + _onChangeCustomer: async function(ev){ + var self=this; + var customer_id = this.$('input[name="customer"]')[0].value + await ajax.jsonRpc('/online/choose/vehicle', "call", { + 'customer_id': customer_id, + }) + .then(function(result) { + const select = self.$el.find('#vehicle_cancellation')[0]; + const options = Array.from(select.options); + options.forEach((option) => { + option.remove(); + }); + result.forEach((item) => { + let newOption = new Option(item[1], item[0]); + select.add(newOption, undefined); + }); + }); + } +}) +}) diff --git a/vehicle_subscription/static/src/js/vehicle_booking.js b/vehicle_subscription/static/src/js/vehicle_booking.js new file mode 100644 index 000000000..ff1d6a93a --- /dev/null +++ b/vehicle_subscription/static/src/js/vehicle_booking.js @@ -0,0 +1,101 @@ +/** @odoo-module **/ +import publicWidget from "@web/legacy/js/public/public_widget"; +import { jsonrpc } from "@web/core/network/rpc_service"; +import { useService } from "@web/core/utils/hooks"; +publicWidget.registry.book = publicWidget.Widget.extend({ + selector: '#book_my_vehicle', + events: { + 'click .redirect_back_with_data':'_onClickBack', + 'click .book_now':'_onClickBook', + 'click #with_fuel':'_onClickWithFuel', + 'click #without_fuel':'_onClickWithoutFuel', + 'change #extra_km':'_onChangeExtraKm', + 'click #full_subscription':'_onClickFullPayment', + 'click #monthly_subscription':'_onClickMonthlyPayment', + }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + setup() { + super.setup(); + this.book = useService("book"); + }, + async _onClickBook(ev){ //Click function to book subscription + var checked= this.el.querySelector('#checkbox_for_fuel').checked + var invoice_checked=this.el.querySelector('#checkbox_for_invoice_type').checked + var customer_id =this.el.querySelector('input[name="customer"]').value + var km = this.el.querySelector('#extra_km').value + var vehicle_id = ev.currentTarget.firstChild.nextSibling.defaultValue + await jsonrpc('/online/subscription/book', { + 'vehicle': vehicle_id, + 'customer':customer_id, + 'checked':checked, + 'invoice':invoice_checked, + 'extra_km':km, + }).then(function(result) { + window.location.href="/next/vehicle/" +result.subscription_id; + }); + }, + async _onClickWithFuel(ev){ //Click function to set price + this.el.querySelector('#checkbox_for_fuel').checked = true + var km = this.el.querySelector('#extra_km').value + var table = this.el.querySelector('#vehicle_booking_table'); + for (var i = 1, row; row = table.rows[i]; i++) { + for (var j = 1, col; col = row.cells[j]; j++) { + var current_price = row.cells[2].innerText + var vehicle_id = row.cells[1].getAttribute('value') + await jsonrpc('/online/subscription/with/fuel',{ + 'vehicle': vehicle_id, + 'price':current_price, + 'extra_km': km, + }) + .then(function(result) { + row.cells[2].innerText = result + }) + } + } + }, + async _onClickWithoutFuel(ev){//Click function to set price without fuel + this.el.querySelector('#checkbox_for_fuel').checked = true + var km = this.el.querySelector('#extra_km').value + var table = this.el.querySelector('#vehicle_booking_table'); + for (var i = 1, row; row = table.rows[i]; i++) { + for (var j = 1, col; col = row.cells[j]; j++) { + var current_price = row.cells[2].innerText + var vehicle_id = row.cells[1].getAttribute('value') + await jsonrpc('/online/subscription/without/fuel', { + 'vehicle': vehicle_id, + 'price':current_price, + 'extra_km':km, + }) + .then(function(result) { + row.cells[2].innerText = result + }) + } + } + }, + async _onChangeExtraKm(ev){ //Change function to set price using extra km + var km = ev.currentTarget.value + var table = this.el.querySelector('#vehicle_booking_table'); + for (var i = 1, row; row = table.rows[i]; i++) { + for (var j = 1, col; col = row.cells[j]; j++) { + await jsonrpc('/online/subscription/with/fuel', { + 'extra_km': km, + }) + .then(function(result){ + row.cells[1].innerText = result + }) + } + } + }, + _onClickFullPayment(ev){//Click function + this.el.querySelector('#checkbox_for_invoice_type').checked = true + }, + _onClickMonthlyPayment(ev){ + this.el.querySelector('#checkbox_for_invoice_type').checked = true + }, + _onClickBack(){//Click function for previous page + window.history.back(); + }, +}) diff --git a/vehicle_subscription/static/src/js/vehicle_subscription_success.js b/vehicle_subscription/static/src/js/vehicle_subscription_success.js new file mode 100644 index 000000000..e4b1efd66 --- /dev/null +++ b/vehicle_subscription/static/src/js/vehicle_subscription_success.js @@ -0,0 +1,60 @@ +/** @odoo-module **/ +import publicWidget from "@web/legacy/js/public/public_widget"; +publicWidget.registry.missing_page = publicWidget.Widget.extend({ + selector: '#subscription_missing_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + }, +}), +publicWidget.registry.form_page = publicWidget.Widget.extend({ + selector: '#subscription_form_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + }, +}), + +publicWidget.registry.cancellation_page = publicWidget.Widget.extend({ + selector: '#subscription_cancellation_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + } +}), +publicWidget.registry.boolean_false = publicWidget.Widget.extend({ + selector: '#boolean_false', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + } +}), +publicWidget.registry.boolean_true = publicWidget.Widget.extend({ + selector: '#boolean_true', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + } +}), + +publicWidget.registry.change_subscription_on = publicWidget.Widget.extend({ + selector: '#change_subscription_on', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + _onClickBack(ev){ //Previous page + window.history.back(); + } +}) + + diff --git a/vehicle_subscription/static/src/js/website_subscription.js b/vehicle_subscription/static/src/js/website_subscription.js new file mode 100644 index 000000000..d5fcc8b23 --- /dev/null +++ b/vehicle_subscription/static/src/js/website_subscription.js @@ -0,0 +1,42 @@ +/** @odoo-module **/ +import publicWidget from "@web/legacy/js/public/public_widget"; +import { Dialog } from "@web/core/dialog/dialog"; +import { jsonrpc } from "@web/core/network/rpc_service"; +import { useService } from "@web/core/utils/hooks"; +publicWidget.registry.Location = publicWidget.Widget.extend({ + selector: '#whole_sub', + events: { + 'click #location_id': '_onLocationClick', + 'click #dismiss':'_onCloseClick', + 'click #next': '_onNextClick' + }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + setup() { + super.setup(); + this.location = useService("location"); + }, + _onLocationClick(){ + var location = this.el.querySelector('#location_temp'); + location.style.display='block'; + }, + // Click function of close button state and city is appended in location field. + _onCloseClick(ev){ + var location = this.el.querySelector('#location_temp'); + var city = this.el.querySelector('#city_id').value + var state = this.el.querySelector("#state_id"); + this.el.querySelector('#location_id').value = state.selectedOptions[0].text +','+ city + location.style.display='none'; + }, + // date validation in Subscription form. + _onNextClick(e){ + var start = this.el.querySelector('#start_date').value; + var end = this.el.querySelector('#end_date').value; + if (start > end) { + e.preventDefault(); + alert('The Start Date must be earlier than the End Date!!!!!!!!!'); + } + } +}) diff --git a/vehicle_subscription/views/account_move_views.xml b/vehicle_subscription/views/account_move_views.xml new file mode 100644 index 000000000..4ff64e219 --- /dev/null +++ b/vehicle_subscription/views/account_move_views.xml @@ -0,0 +1,14 @@ + + + + + account.move.view.form.inherited.vehicle.subscription + account.move + + + + + + + + diff --git a/vehicle_subscription/views/cancellation_request_views.xml b/vehicle_subscription/views/cancellation_request_views.xml new file mode 100644 index 000000000..c62a0d700 --- /dev/null +++ b/vehicle_subscription/views/cancellation_request_views.xml @@ -0,0 +1,48 @@ + + + + + Cancellation Request + cancellation.request + tree,form + + + + cancellation.request.view.form + cancellation.request + +
+
+ +
+ + + + + + + + +
+ + +
+
+
+
+ + +
diff --git a/vehicle_subscription/views/change_vehicle_subscription_template.xml b/vehicle_subscription/views/change_vehicle_subscription_template.xml new file mode 100644 index 000000000..1ccbdd64a --- /dev/null +++ b/vehicle_subscription/views/change_vehicle_subscription_template.xml @@ -0,0 +1,309 @@ + + + + + + + + + diff --git a/vehicle_subscription/views/fleet_subscription_views.xml b/vehicle_subscription/views/fleet_subscription_views.xml new file mode 100644 index 000000000..acfb871ba --- /dev/null +++ b/vehicle_subscription/views/fleet_subscription_views.xml @@ -0,0 +1,107 @@ + + + + + Subscription + fleet.subscription + tree,form + + + + fleet.subscription.view.form + fleet.subscription + +
+
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+
+ + +
diff --git a/vehicle_subscription/views/fleet_vehicle_model_views.xml b/vehicle_subscription/views/fleet_vehicle_model_views.xml new file mode 100644 index 000000000..f7897ffa0 --- /dev/null +++ b/vehicle_subscription/views/fleet_vehicle_model_views.xml @@ -0,0 +1,42 @@ + + + + + + fleet.vehicle.model.view.form.inherited.vehicle.subscription + + fleet.vehicle.model + + + + + + + + + + + fleet.vehicle.view.form.inherited.vehicle.subscription + + fleet.vehicle + + + + + + + + + + + + + + + + + + + + diff --git a/vehicle_subscription/views/insurance_type_views.xml b/vehicle_subscription/views/insurance_type_views.xml new file mode 100644 index 000000000..6815c7d6e --- /dev/null +++ b/vehicle_subscription/views/insurance_type_views.xml @@ -0,0 +1,44 @@ + + + + + Insurance Type + insurance.type + tree,form + + + + insurance.type.view.form + insurance.type + +
+ + + + + + + + + + + + + + + +
+ + +
+
+
+
+ + +
diff --git a/vehicle_subscription/views/online_subscription_template.xml b/vehicle_subscription/views/online_subscription_template.xml new file mode 100644 index 000000000..7a0b07f32 --- /dev/null +++ b/vehicle_subscription/views/online_subscription_template.xml @@ -0,0 +1,247 @@ + + + + + diff --git a/vehicle_subscription/views/online_vehicle_cancellation_template.xml b/vehicle_subscription/views/online_vehicle_cancellation_template.xml new file mode 100644 index 000000000..dc11d8e6a --- /dev/null +++ b/vehicle_subscription/views/online_vehicle_cancellation_template.xml @@ -0,0 +1,110 @@ + + + + + diff --git a/vehicle_subscription/views/online_vehicle_template.xml b/vehicle_subscription/views/online_vehicle_template.xml new file mode 100644 index 000000000..d34bf85dd --- /dev/null +++ b/vehicle_subscription/views/online_vehicle_template.xml @@ -0,0 +1,218 @@ + + + + + diff --git a/vehicle_subscription/views/subscription_form_success_template.xml b/vehicle_subscription/views/subscription_form_success_template.xml new file mode 100644 index 000000000..4125c070c --- /dev/null +++ b/vehicle_subscription/views/subscription_form_success_template.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + diff --git a/vehicle_subscription/views/subscription_request_views.xml b/vehicle_subscription/views/subscription_request_views.xml new file mode 100644 index 000000000..01af4a30d --- /dev/null +++ b/vehicle_subscription/views/subscription_request_views.xml @@ -0,0 +1,47 @@ + + + + + Subscription Request + subscription.request + tree,form + + + + subscription.request.view.form + subscription.request + +
+
+ +
+ + + + + + + + + +
+ + +
+
+
+
+ + +
diff --git a/vehicle_subscription/views/vehicle_insurance_views.xml b/vehicle_subscription/views/vehicle_insurance_views.xml new file mode 100644 index 000000000..29855cd47 --- /dev/null +++ b/vehicle_subscription/views/vehicle_insurance_views.xml @@ -0,0 +1,37 @@ + + + + + Insurance + vehicle.insurance + tree,form + + + + vehicle.insurance.view.form + vehicle.insurance + +
+ + + + + + + + + +
+ + +
+
+
+
+ + +
diff --git a/vehicle_subscription/views/website_portal_subscription_template.xml b/vehicle_subscription/views/website_portal_subscription_template.xml new file mode 100644 index 000000000..d9ca35e96 --- /dev/null +++ b/vehicle_subscription/views/website_portal_subscription_template.xml @@ -0,0 +1,126 @@ + + + + + + + + + diff --git a/vehicle_subscription/wizard/__init__.py b/vehicle_subscription/wizard/__init__.py new file mode 100644 index 000000000..4c177339b --- /dev/null +++ b/vehicle_subscription/wizard/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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 . import change_subscription diff --git a/vehicle_subscription/wizard/change_subscription.py b/vehicle_subscription/wizard/change_subscription.py new file mode 100644 index 000000000..13a7e185a --- /dev/null +++ b/vehicle_subscription/wizard/change_subscription.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Jumana Jabin MP () +# +# 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, _ +from odoo.exceptions import ValidationError + + +class VehicleChange(models.TransientModel): + """Transient Model for switching subscription""" + _name = "change.subscription" + _description = "Change Subscription" + _rec_name = "vehicle" + + vehicle = fields.Selection( + [('same', 'Same Vehicle'), ('different', 'Different Vehicle')], + default='same', string='Vehicle', + help="Helps you to choose type of vehicle") + + def action_change_subscription(self): + """Proceed with changing subscription""" + if self.vehicle == 'different': + raise ValidationError(_( + "Inorder to change subscription" + " to different vehicle you need to" + " cancel the current subscription plan")) + else: + active_id = self._context.get('active_id') + subscription = self.env['fleet.subscription'].browse(active_id) + return { + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'subscription.request', + 'target': 'new', + 'context': { + 'default_current_vehicle_id': subscription.vehicle_id.id} + } diff --git a/vehicle_subscription/wizard/change_subscription_views.xml b/vehicle_subscription/wizard/change_subscription_views.xml new file mode 100644 index 000000000..7f5aee2b7 --- /dev/null +++ b/vehicle_subscription/wizard/change_subscription_views.xml @@ -0,0 +1,30 @@ + + + + + change.subscription.view.form + change.subscription + +
+ + + +
+
+
+
+
+ + + ir.actions.act_window + change.subscription + form + + new + +