diff --git a/vehicle_subscription/README.rst b/vehicle_subscription/README.rst new file mode 100644 index 000000000..8f601792a --- /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 Management +=============================== +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: (V15) Swaraj R, 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..7ad5d6f2e --- /dev/null +++ b/vehicle_subscription/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..5a9d177bb --- /dev/null +++ b/vehicle_subscription/__manifest__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 Management', + 'version': "15.0.1.0.0", + 'category': 'Industries', + 'summary': """Vehicle Subscription Management From Website""", + 'description': """A Vehicle Subscription Management system on a website +allows users to subscribe to vehicles on a flexible, recurring basis, +similar to how they might subscribe to streaming services""", + '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', + 'report/cancellation_request.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/*.js', + ], + }, + 'images': ['static/description/banner.png'], + '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..ac37e77f8 --- /dev/null +++ b/vehicle_subscription/controllers/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..b64507de0 --- /dev/null +++ b/vehicle_subscription/controllers/vehicle_subscription.py @@ -0,0 +1,442 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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.addons.base.models.ir_ui_view import keep_query +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 """ + state = int(kwargs.get('state')) + vehicle = request.env['fleet.vehicle'].sudo().search( + [('states_id', '=', state)]) + states = [city.location for city in vehicle] + return [*set(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, + '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, + type='http', method="POST") + def vehicle_form(self, **kw): + """Redirect to corresponding templates according to the + data provided by user in form page """ + if kw.get('start_date'): + states = kw.get('state') + city = kw.get('city') + 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() + seats = kw.get('seating_capacity') + insurance_type = request.env['vehicle.insurance'].sudo().search( + [('insurance_type_id.id', '=', int(insurance)), + ('start_date', '<=', start), ('end_date', '>=', end)]) + insurance_amount = insurance_type[ + 0].insurance_amount if insurance_type else 0 + for rec in insurance_type: + rec.vehicle_id.insurance = rec.id + 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), ('states_id', '=', int(states)), + ('location', '=', city), + ('seats', '=', int(seats))]) + vehicle_id = vehicle.filtered( + lambda v: v.id not in subscribed_vehicle_id.ids) + if vehicle_id: + for rec in vehicle_id: + rec.write({ + '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)], limit=1) + if extra_km == '': + km = 0 + else: + km = int(extra_km) + subscribe = request.env['fleet.subscription'].sudo().create({ + 'vehicle_id': vehicle_id.id, + 'customer_id': request.env.user.partner_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 not checked else 'with_fuel' + }) + if checked: + subscribe.price = ( + ((km / vehicle_id.mileage) * vehicle_id.fuel_rate) + + (vehicle_id.duration * vehicle_id.subscription_price) + + request.env['vehicle.insurance'].browse( + int(vehicle_id.insurance)).insurance_amount + ( + vehicle_id.charge_km * km)) + else: + subscribe.price = ( + ((vehicle_id.duration * vehicle_id.subscription_price) + + request.env['vehicle.insurance'].browse( + int(vehicle_id.insurance)).insurance_amount) + ( + vehicle_id.charge_km * km)) + subscribe.sudo().action_invoice() + subscribe.sale_id.sudo().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 + subscribe.sale_id.invoice_ids.subscription_id = subscribe.id + 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, + 'subscription_id': subscribe.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, + 'subscription_id': subscribe.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 + } + for each in subscribe.invoice_ids: + each.partner_id = customer_id.id + for each in subscribe.sale_id.invoice_ids: + each.partner_id = customer_id.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') if kwargs.get('extra_km') else '0' + vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle) + vehicle.write({ + 'extra_km': float(km), + }) + insurance_amount = vehicle.insurance + amount = request.env['vehicle.insurance'].sudo() \ + .browse(int(insurance_amount)).insurance_amount + new_price = (((vehicle.extra_km / vehicle.mileage) * + vehicle.fuel_rate) + + (vehicle.duration * vehicle.subscription_price) + + amount + (vehicle.charge_km * vehicle.extra_km)) + 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') if kwargs.get('extra_km') else '0' + 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': float(km), + }) + new_price = (((vehicle.duration * vehicle.subscription_price) + + amount) + (vehicle.charge_km * vehicle.extra_km)) + 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.user.partner_id + 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""" + if bool(kwargs): + vehicle = [] + customer = kwargs.get('customer_id') + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)], limit=1) + 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""" + if bool(kwargs): + customer = kwargs.get('customer') + vehicle = int(kwargs.get('vehicle')) + reason = kwargs.get('reason') + customer_id = request.env['res.partner'].sudo().search( + [('name', '=', customer)], limit=1) + vehicle_id = request.env['fleet.vehicle'].sudo().browse(vehicle) + subscription = request.env['fleet.subscription'].search( + [('vehicle_id', '=', vehicle_id.id), ('state', '=', 'subscribed'), + ('customer_id', '=', customer_id.id)], limit=1) + if request.env['cancellation.request'].sudo().search([ + ('customer_id', '=', customer_id.id), + ('vehicle_id', '=', vehicle_id.id), + ('subscription_id', '=', subscription.id) + ]): + return request.render('vehicle_subscription.booking_cancelled', + {'customer': customer, + 'vehicle': vehicle_id.name}) + cancel_request = request.env['cancellation.request'].sudo().create({ + 'customer_id': customer_id.id, + 'vehicle_id': vehicle_id.id, + 'reason': reason, + 'subscription_id': subscription.id + }) + values = { + 'customer': customer, + 'vehicle': vehicle_id.name, + } + cancel_request.state = 'to_approve' + return request.render('vehicle_subscription.booking_cancellation', + values) + else: + return request.redirect("/online/subscription") + + @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.""" + vehicle = request.env['fleet.vehicle'].sudo().search( + [('states_id', '!=', False)]) + customer = request.env.user.partner_id.name + vals = { + 'vehicles': vehicle, + 'customers': customer, + } + 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 bool(kwargs): + 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)], limit=1) + 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') + else: + return request.redirect("/online/subscription") + + @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. """ + if bool(kwargs): + 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)], limit=1) + vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle_id) + subscription = request.env['fleet.subscription'].search( + [('vehicle_id', '=', current_vehicle_id.id), + ('state', '=', 'subscribed'), + ('customer_id', '=', customer_id.id)], limit=1) + if request.env['subscription.request'].sudo().search([ + ('current_vehicle_id', '=', current_vehicle_id.id), + ('customer_id', '=', customer_id.id), + ('subscription_id', '=', subscription.id), + ('state', '=', 'to_approve') + ]): + return request.render('vehicle_subscription' + '.subscription_change_create_alert', + {'customer': customer_id.name, + 'vehicle': current_vehicle_id.name}) + 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, + 'subscription_id': subscription.id + }) + change_subscription.state = 'to_approve' + return request.render('vehicle_subscription.change_subscription') + else: + return request.redirect("/online/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..b6af14a05 --- /dev/null +++ b/vehicle_subscription/controllers/website_portal.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 collections import OrderedDict +from odoo.osv import expression +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""" + + def _get_account_searchbar_sortings(self): + """Website accounts search bar sorting options""" + return { + 'date': {'label': _('Date'), 'order': 'invoice_date desc'}, + 'duedate': {'label': _('Due Date'), + 'order': 'invoice_date_due desc'}, + 'name': {'label': _('Reference'), 'order': 'name desc'}, + 'state': {'label': _('Status'), 'order': 'state'}, + } + + def _get_account_searchbar_filters(self): + """Function get the search bar filters""" + return { + 'all': {'label': _('All'), 'domain': []}, + 'invoices': {'label': _('Invoices'), 'domain': [ + ('move_type', 'in', ('out_invoice', 'out_refund'))]}, + 'bills': {'label': _('Bills'), 'domain': [ + ('move_type', 'in', ('in_invoice', 'in_refund'))]}, + } + + def _prepare_my_invoices_values(self, page, date_begin, date_end, sortby, + filterby, domain=None, url="/my/invoices"): + """Function prepare the invoice value""" + values = self._prepare_portal_layout_values() + AccountInvoice = request.env['account.move'] + domain = expression.AND([ + domain or [], + self._get_invoices_domain(), + ]) + searchbar_sortings = self._get_account_searchbar_sortings() + if not sortby: + sortby = 'date' + order = searchbar_sortings[sortby]['order'] + searchbar_filters = self._get_account_searchbar_filters() + if not filterby: + filterby = 'all' + domain += searchbar_filters[filterby]['domain'] + if date_begin and date_end: + domain += [('create_date', '>', date_begin), + ('create_date', '<=', date_end)] + values.update({ + 'date': date_begin, + 'invoices': lambda pager_offset: AccountInvoice.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager_offset), + 'page_name': 'invoice', + 'pager': { + "url": url, + "url_args": {'date_begin': date_begin, 'date_end': date_end, + 'sortby': sortby}, + "total": AccountInvoice.search_count(domain), + "page": page, + "step": self._items_per_page, + }, + 'default_url': url, + 'searchbar_sortings': searchbar_sortings, + 'sortby': sortby, + 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), + 'filterby': filterby, + }) + return values + + @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), ('subscription_id', '!=', False) + ] + values.update({ + 'invoices': request.env['account.move'].sudo().search(domain), + 'pager': pager, + 'page_name': 'subscription_home' + }) + 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), + ('subscription_id', 'in', + request.env['fleet.subscription'].search([]).ids)]) + 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..634f22c64 --- /dev/null +++ b/vehicle_subscription/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 13.09.2024 +#### Version 15.0.1.0.0 +#### ADD +Initial Commit Vehicle Subscription Management diff --git a/vehicle_subscription/models/__init__.py b/vehicle_subscription/models/__init__.py new file mode 100644 index 000000000..d393da725 --- /dev/null +++ b/vehicle_subscription/models/__init__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..57b469968 --- /dev/null +++ b/vehicle_subscription/models/account_move.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 field's """ + _inherit = 'account.move' + + is_subscription = fields.Boolean(string="Subscribe", + help="This field will be set true for " + "invoice corresponding to vehicle " + "subscription") + subscription_id = fields.Many2one('fleet.subscription', + help='Subscription Id of the invoice if ' + 'any', string='Subscription Id') diff --git a/vehicle_subscription/models/cancellation_request.py b/vehicle_subscription/models/cancellation_request.py new file mode 100644 index 000000000..1a087278c --- /dev/null +++ b/vehicle_subscription/models/cancellation_request.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 odoo import fields, models, _ +from odoo.exceptions import UserError + + +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', required=True, + string="Vehicle", help="Choose vehicle " + "inorder to cancel " + "subscription") + 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", required=True, + 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") + subscription_id = fields.Many2one('fleet.subscription', + string='Subscription Id', + help='Subscription related to this ' + 'cancellation request') + + 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.""" + subscription = self.env['fleet.subscription'].search( + [('vehicle_id', '=', self.vehicle_id.id), + ('customer_id', '=', self.customer_id.id), + ('state', '=', 'subscribed')], limit=1) + if subscription: + invoice = subscription.sale_id.invoice_ids + multy_invoice = subscription.invoice_ids.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)]) + per_day_price = self.vehicle_id.subscription_price + insurance_amount = self.subscription_id.insurance_type_id.insurance_amount + invoice_duration =1 if self.date - subscription.start_date == 0 else (self.date - subscription.start_date).days + template_approved = self.env.ref( + 'vehicle_subscription.cancellation_approved') + if invoice_duration < 0: + 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) + self.write({'state': 'approved'}) + invoice.button_cancel() + return + email_template = self.env.ref( + 'vehicle_subscription.cancellation_request_mail') + refund_approved = self.env.ref( + 'vehicle_subscription.cancellation_request_refund_mail') + uptodate_price = round(per_day_price * invoice_duration, 2) + insurance_amount + 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) + self.write({'state': 'approved'}) + elif sum(paid_amount) < uptodate_price: + self.state = 'to_approve' + 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': [(0, 0, { + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': + round(per_day_price * invoice_duration, 2) + insurance_amount - sum(paid_amount), + })] + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env.ref('vehicle_subscription' + '.cancellation_request_action_report')._render_qweb_pdf(res_ids=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': [ + (4, invoice_report_attachment_id.id, None)] + } + email_template.send_mail(self.id, + email_values=email_values, + force_send=True) + email_template.attachment_ids = [(5, 0, 0)] + subscription.invoice_ids = [(4, generate_invoice.id)] + subscription.sale_id.write({ + 'invoice_ids': [(4, generate_invoice.id)] + }) + else: + self.state = 'to_approve' + for invoice_id in multy_invoice: + invoice = self.env['account.move'].browse( + invoice_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': [(0, 0, { + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': + round(per_day_price * invoice_duration, 2) + insurance_amount - sum( + paid_amount), + })] + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env.ref('vehicle_subscription' + '.cancellation_request_action_report')._render_qweb_pdf( + res_ids=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': [ + (4, invoice_report_attachment_id.id, None)] + } + email_template.send_mail(self.id, + email_values=email_values, + force_send=True) + email_template.attachment_ids = [(5, 0, 0)] + subscription.invoice_ids = [(4, generate_invoice.id)] + subscription.sale_id.write({ + 'invoice_ids': [(4, generate_invoice.id)] + }) + else: + self.state = 'to_approve' + 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': [(0, 0, { + '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.ref('vehicle_subscription' + '.cancellation_request_action_report')._render_qweb_pdf( + res_ids=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': [ + (4, invoice_report_attachment_id.id, None)] + } + refund_approved.send_mail(self.id, + email_values=email_values, + force_send=True) + refund_approved.attachment_ids = [(5, 0, 0)] + else: + for invoice in subscription.invoice_ids: + 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_origin': subscription.sale_id.name, + 'invoice_line_ids': [(0, 0, { + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': round(per_day_price * invoice_duration, 2) + insurance_amount, + })] + }) + generate_invoice.action_post() + data_record = base64.b64encode( + self.env.ref('vehicle_subscription' + '.cancellation_request_action_report')._render_qweb_pdf( + res_ids=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': [ + (4, invoice_report_attachment_id.id, None)] + } + email_template.send_mail(self.id, email_values=email_values, + force_send=True) + email_template.attachment_ids = [(5, 0, 0)] + subscription.invoice_ids = [(4, generate_invoice.id)] + subscription.sale_id.write({ + 'invoice_ids': [(4, generate_invoice.id)] + }) + else: + raise UserError(_(f"{self.customer_id.name} currently has no " + f"active" + f" Subscription for {self.vehicle_id.name}")) diff --git a/vehicle_subscription/models/fleet_subscription.py b/vehicle_subscription/models/fleet_subscription.py new file mode 100644 index 000000000..bc9fabcd2 --- /dev/null +++ b/vehicle_subscription/models/fleet_subscription.py @@ -0,0 +1,361 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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)]", + required=True, + help="This field help you to choose vehicle") + 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(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", + 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=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", + required=True, + 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") + is_invisible_sub = fields.Boolean(string="Approve Subscription", + help="As subscription request get " + "approved this 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)]) + for record in model_id: + self.vehicle_ids = [(4, record.id)] + else: + model_id = self.env['fleet.vehicle'].search( + [('id', 'in', domain)]) + for record in model_id: + self.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('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, + }) + line = self.env["sale.order.line"].create({ + 'product_id': product_id.id, + 'product_uom_qty': 1, + 'price_unit': self.price, + 'order_id': sale_order_id.id + }) + 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)]) + 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': [(0, 0, { + 'product_id': product_id.id, + 'name': self.vehicle_id.name, + 'price_unit': self.uptodate_price, + })] + }) + else: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Warning'), + 'message': 'you need to pay amount till date inorder to ' + 'cancel 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'}) + self.sale_id.action_done() diff --git a/vehicle_subscription/models/fleet_vehicle.py b/vehicle_subscription/models/fleet_vehicle.py new file mode 100644 index 000000000..7b2db1976 --- /dev/null +++ b/vehicle_subscription/models/fleet_vehicle.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..15e593e63 --- /dev/null +++ b/vehicle_subscription/models/fleet_vehicle_model.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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, help="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..acf7eec03 --- /dev/null +++ b/vehicle_subscription/models/insurance_type.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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") + coverage_price = fields.Float(string="Price", help="Rate of insurance") + 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..28dd88096 --- /dev/null +++ b/vehicle_subscription/models/subscription_request.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 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", required=True, readonly=True) + 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", + required=True, readonly=True, + help="Currently using vehicle of " + "customer will be set") + new_vehicle_id = fields.Many2one('fleet.vehicle', + string="New Vehicle", + readonly=True, + required=True, + help="Can choose different vehicle " + "with same model") + reason_to_change = fields.Char(string="Reason", required=True, + readonly=True, + help="Reason for changing vehicle") + subscription_id = fields.Many2one('fleet.subscription', + string='Subscription Id', + help='Subscription related to this ' + 'cancellation request') + state = fields.Selection( + selection=[('to_approve', 'To Approve'), + ('approved', 'Approved'), + ], string='State', default='to_approve', + help="States of subscription") + + 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, + 'is_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..6fe4dcaa7 --- /dev/null +++ b/vehicle_subscription/models/vehicle_insurance.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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 VehicleInsurance(models.Model): + """New model vehicle .insurance""" + _name = "vehicle.insurance" + _description = "Vehicle Insurance" + _inherit = "mail.thread" + _rec_name = "insurance_type_id" + + vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle", + help="Help you to choose vehicle") + start_date = fields.Date(string="Start Date", help="Insurance start date") + end_date = fields.Date(string="End Date", help="Insurance end date") + 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')) diff --git a/vehicle_subscription/report/cancellation_request.xml b/vehicle_subscription/report/cancellation_request.xml new file mode 100644 index 000000000..d52efad9a --- /dev/null +++ b/vehicle_subscription/report/cancellation_request.xml @@ -0,0 +1,13 @@ + + + + + Fleet Vehicle Subscription Cancel + cancellation.request + qweb-pdf + account.report_invoice_with_payments + account.report_invoice_with_payments + + report + + \ No newline at end of file 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..f7e176b6c --- /dev/null +++ b/vehicle_subscription/security/vehicle_subscription_groups.xml @@ -0,0 +1,18 @@ + + + + + Vehicle Subscription + User access levels for Fleet module + 10 + + + User + + + + Manager + + + + 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/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/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/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/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/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.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.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/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/white-arrow.png b/vehicle_subscription/static/description/assets/misc/white-arrow.png new file mode 100644 index 000000000..68aeb7bef Binary files /dev/null and b/vehicle_subscription/static/description/assets/misc/white-arrow.png differ diff --git a/vehicle_subscription/static/description/assets/modules/budget_image.png b/vehicle_subscription/static/description/assets/modules/budget_image.png new file mode 100644 index 000000000..b50130c7d Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/budget_image.png differ diff --git a/vehicle_subscription/static/description/assets/modules/credit_image.png b/vehicle_subscription/static/description/assets/modules/credit_image.png new file mode 100644 index 000000000..3ad04ecfd Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/credit_image.png differ diff --git a/vehicle_subscription/static/description/assets/modules/employee_image.png b/vehicle_subscription/static/description/assets/modules/employee_image.png new file mode 100644 index 000000000..30ad58232 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/employee_image.png differ diff --git a/vehicle_subscription/static/description/assets/modules/export_image.png b/vehicle_subscription/static/description/assets/modules/export_image.png new file mode 100644 index 000000000..492980ad0 Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/export_image.png differ diff --git a/vehicle_subscription/static/description/assets/modules/gantt_image.png b/vehicle_subscription/static/description/assets/modules/gantt_image.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/gantt_image.png differ diff --git a/vehicle_subscription/static/description/assets/modules/quotation_image.png b/vehicle_subscription/static/description/assets/modules/quotation_image.png new file mode 100644 index 000000000..499b1a72f Binary files /dev/null and b/vehicle_subscription/static/description/assets/modules/quotation_image.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..f0ea13584 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..b520acbe9 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..0b0488103 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..5fc48724e Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/12.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..58da45564 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/14.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..f701b7501 Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/2.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..89c76fada 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..c990259f9 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..833eb623d 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..667e772b9 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..27ba25232 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..6452ee89a 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..89ec42cad Binary files /dev/null and b/vehicle_subscription/static/description/assets/screenshots/hero.gif differ diff --git a/vehicle_subscription/static/description/banner.png b/vehicle_subscription/static/description/banner.png new file mode 100644 index 000000000..43b3707b3 Binary files /dev/null and b/vehicle_subscription/static/description/banner.png differ diff --git a/vehicle_subscription/static/description/icon.png b/vehicle_subscription/static/description/icon.png new file mode 100644 index 000000000..2a2c3a897 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..8226b815f --- /dev/null +++ b/vehicle_subscription/static/description/index.html @@ -0,0 +1,637 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ Vehicle Subscription Management

+

Vehicle Subscription Management From Website

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

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ This module helps you to Subscribe,Cancel and Change subscription through website as well as backend. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ + Community & + Enterprise 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 +
+
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Vehicle Subscription +

+

Go to Website -> + Subscription Form -> + Choose Location , Insurance and duration of subscription.

+ +
+
+

Choose Vehicle +

+

Book vehicle from + listed vehicle user can change default km choose fuel type ,choose payment type.

+ +
+ +
+

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

+ +
+ +
+

Cancel Request +

+

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

+ +
+ +
+

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

+ +
+
+
+ + + +
+
+

Suggested Products

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ 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 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/vehicle_subscription/static/src/js/booking_cancellation.js b/vehicle_subscription/static/src/js/booking_cancellation.js new file mode 100644 index 000000000..9ef8daf41 --- /dev/null +++ b/vehicle_subscription/static/src/js/booking_cancellation.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Booking cancellation redirect to back page +publicWidget.registry.cancellation_page = publicWidget.Widget.extend({ + selector: '#subscription_cancellation_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) diff --git a/vehicle_subscription/static/src/js/change_subscription.js b/vehicle_subscription/static/src/js/change_subscription.js new file mode 100644 index 000000000..0ef04d87c --- /dev/null +++ b/vehicle_subscription/static/src/js/change_subscription.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Change subscription redirect to back page +publicWidget.registry.change_subscription_on = publicWidget.Widget.extend({ + selector: '#change_subscription_on', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) 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..1ef0b72a2 --- /dev/null +++ b/vehicle_subscription/static/src/js/change_subscription_form.js @@ -0,0 +1,31 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +import ajax from 'web.ajax'; +//Public Widget Class for Change subscription +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 + await ajax.jsonRpc('/online/choose/vehicle', "call", { + 'customer_id': customer_id, + }) + .then(function(result) { + const select = self.$el.find('#change_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..f4cd0da75 --- /dev/null +++ b/vehicle_subscription/static/src/js/change_subscription_request.js @@ -0,0 +1,18 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +import ajax from '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..38d2d4c38 --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_cancellation.js @@ -0,0 +1,31 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +import ajax from '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/subscription_change_boolean_false.js b/vehicle_subscription/static/src/js/subscription_change_boolean_false.js new file mode 100644 index 000000000..3fb11ddfb --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_change_boolean_false.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Redirect from subscription_change_boolean_false to Previous page +publicWidget.registry.boolean_false = publicWidget.Widget.extend({ + selector: '#boolean_false', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) diff --git a/vehicle_subscription/static/src/js/subscription_change_button.js b/vehicle_subscription/static/src/js/subscription_change_button.js new file mode 100644 index 000000000..090910bf5 --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_change_button.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Redirect from subscription_change_button to previous page +publicWidget.registry.boolean_true = publicWidget.Widget.extend({ + selector: '#boolean_true', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) diff --git a/vehicle_subscription/static/src/js/subscription_form_success.js b/vehicle_subscription/static/src/js/subscription_form_success.js new file mode 100644 index 000000000..13bd9e505 --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_form_success.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Redirect from subscription_form_success to previous page +publicWidget.registry.form_page = publicWidget.Widget.extend({ + selector: '#subscription_form_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) diff --git a/vehicle_subscription/static/src/js/subscription_vehicle_missing.js b/vehicle_subscription/static/src/js/subscription_vehicle_missing.js new file mode 100644 index 000000000..d11709e0c --- /dev/null +++ b/vehicle_subscription/static/src/js/subscription_vehicle_missing.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Redirect from subscription_vehicle_missing to previous page +publicWidget.registry.form_page = publicWidget.Widget.extend({ + selector: '#subscription_form_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) 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..da7aad34c --- /dev/null +++ b/vehicle_subscription/static/src/js/vehicle_booking.js @@ -0,0 +1,115 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +import ajax from 'web.ajax'; +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', + }, + //Click function to book subscription + _onClickBook: async function(ev){ + var checked=this.$('#checkbox_for_fuel')[0].checked + var invoice_checked=this.$('#checkbox_for_invoice_type')[0].checked + var customer_id = this.$('input[name="customer"]')[0].value + var km = this.$('#extra_km')[0].value + var vehicle_id = ev.currentTarget.firstChild.nextSibling.defaultValue + await ajax.jsonRpc('/online/subscription/book', "call", { + '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; + }); + }, + //Click function to set price + _onClickWithFuel: async function(ev){ + this.$('#with_fuel .btn').css('background-color', 'red'); + this.$('#without_fuel .btn').css('background-color', ''); + this.$('#checkbox_for_fuel')[0].checked = true + var km = this.$('#extra_km')[0].value + var table = this.$('#vehicle_booking_table')[0]; + 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 ajax.jsonRpc('/online/subscription/with/fuel', "call", { + 'vehicle': vehicle_id, + 'price':current_price, + 'extra_km': km, + }) + .then(function(result) { + row.cells[2].innerText = result + }) + } + } + }, + //Click function to set price without fuel + _onClickWithoutFuel: async function(ev){ + this.$('#without_fuel .btn').css('background-color', 'red'); + this.$('#with_fuel .btn').css('background-color', ''); + this.$('#checkbox_for_fuel')[0].checked = false + var km = this.$('#extra_km')[0].value + var table = this.$('#vehicle_booking_table')[0]; + 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 ajax.jsonRpc('/online/subscription/without/fuel', "call", { + 'vehicle': vehicle_id, + 'price':current_price, + 'extra_km':km, + }) + .then(function(result) { + row.cells[2].innerText = result + }) + } + } + }, + //Change function to set price using extra km + _onChangeExtraKm: async function(ev){ + this.$('#checkbox_for_fuel')[0].checked = true + this.$('#with_fuel .btn').css('background-color', 'red'); + this.$('#without_fuel .btn').css('background-color', ''); + var km = ev.currentTarget.value + var table = this.$('#vehicle_booking_table')[0]; + 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 ajax.jsonRpc('/online/subscription/with/fuel', "call", { + 'vehicle': vehicle_id, + 'price':current_price, + 'extra_km':km, + }) + .then(function(result){ + row.cells[2].innerText = result + }) + } + } + }, + //Click function + _onClickFullPayment:function(ev){ + this.$('#checkbox_for_invoice_type')[0].checked = true + this.$('#full_subscription .btn').css('background-color', 'red'); + this.$('#monthly_subscription .btn').css('background-color', ''); + }, + //Click function + _onClickMonthlyPayment:function(ev){ + this.$('#checkbox_for_invoice_type')[0].checked = true + this.$('#full_subscription .btn').css('background-color', ''); + this.$('#monthly_subscription .btn').css('background-color', 'red'); + }, + //Click function for previous page + _onClickBack:function(ev){ + window.history.back(); + } +}) diff --git a/vehicle_subscription/static/src/js/vehicle_missing.js b/vehicle_subscription/static/src/js/vehicle_missing.js new file mode 100644 index 000000000..451c30b77 --- /dev/null +++ b/vehicle_subscription/static/src/js/vehicle_missing.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +//Vehicle Missing redirect to back page +publicWidget.registry.subscription_missing_page = publicWidget.Widget.extend({ + selector: '#subscription_missing_page', + events: { + 'click .redirect_back_with_data':'_onClickBack', + }, + //Previous page + _onClickBack:function(ev){ + 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..7cbd87ca4 --- /dev/null +++ b/vehicle_subscription/static/src/js/website_subscription.js @@ -0,0 +1,44 @@ +/** @odoo-module **/ + +import publicWidget from 'web.public.widget'; +import Dialog from 'web.Dialog'; +import ajax from 'web.ajax'; + +publicWidget.registry.Location = publicWidget.Widget.extend({ + selector: '#whole_sub', + events: { + 'click #location_id': '_onLocationClick', + 'change #state_id':'_onStateChange', + 'click #dismiss':'_onCloseClick', + }, + _onLocationClick: function (ev) { //function that opens modal + var location = this.$('#location_temp')[0]; + location.style.display='block'; + }, + _onStateChange:function(ev){ // On the change of state ,city gets changed + var self=this; + var state_id = ev.currentTarget.value + ajax.jsonRpc('/online/subscription/city', "call", { + 'state': state_id, + }) + .then(function(result) { + const select = self.$el.find('#city_id')[0]; + const options = Array.from(select.options); + options.forEach((option) => { + option.remove(); + }); + result.forEach((item) => { + let newOption = new Option(item, item); + select.add(newOption, undefined); + }); + }); + }, + // Click function of close button state and city is appended in location field. + _onCloseClick: function(ev){ + var location = this.$('#location_temp')[0]; + var city=this.$('#city_id')[0].value + var state=this.$('#state_id').val(); + this.$('#location_id')[0].value = this.$('#state_id')[0].options[this.$('#state_id')[0].selectedIndex].text +','+ city + location.style.display='none'; + }, + }) diff --git a/vehicle_subscription/views/account_move_views.xml b/vehicle_subscription/views/account_move_views.xml new file mode 100644 index 000000000..0091bb190 --- /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..05e805a38 --- /dev/null +++ b/vehicle_subscription/views/cancellation_request_views.xml @@ -0,0 +1,46 @@ + + + + + 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..d9eac6b38 --- /dev/null +++ b/vehicle_subscription/views/change_vehicle_subscription_template.xml @@ -0,0 +1,304 @@ + + + + + + + + + diff --git a/vehicle_subscription/views/fleet_subscription_views.xml b/vehicle_subscription/views/fleet_subscription_views.xml new file mode 100644 index 000000000..390906b4d --- /dev/null +++ b/vehicle_subscription/views/fleet_subscription_views.xml @@ -0,0 +1,117 @@ + + + + + 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..f34b9b3f2 --- /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..0cfb364c2 --- /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..aca54df3c --- /dev/null +++ b/vehicle_subscription/views/online_subscription_template.xml @@ -0,0 +1,250 @@ + + + + + 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..c486eb11f --- /dev/null +++ b/vehicle_subscription/views/online_vehicle_cancellation_template.xml @@ -0,0 +1,108 @@ + + + + + diff --git a/vehicle_subscription/views/online_vehicle_template.xml b/vehicle_subscription/views/online_vehicle_template.xml new file mode 100644 index 000000000..bdd45c1ca --- /dev/null +++ b/vehicle_subscription/views/online_vehicle_template.xml @@ -0,0 +1,197 @@ + + + + + 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..c869c9578 --- /dev/null +++ b/vehicle_subscription/views/subscription_form_success_template.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + diff --git a/vehicle_subscription/views/subscription_request_views.xml b/vehicle_subscription/views/subscription_request_views.xml new file mode 100644 index 000000000..997ee505e --- /dev/null +++ b/vehicle_subscription/views/subscription_request_views.xml @@ -0,0 +1,44 @@ + + + + + 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..cc3bf9b4e --- /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..61da511cf --- /dev/null +++ b/vehicle_subscription/views/website_portal_subscription_template.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + diff --git a/vehicle_subscription/wizard/__init__.py b/vehicle_subscription/wizard/__init__.py new file mode 100644 index 000000000..647308eb8 --- /dev/null +++ b/vehicle_subscription/wizard/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..7d0eceb5a --- /dev/null +++ b/vehicle_subscription/wizard/change_subscription.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Swaraj R () +# +# 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..7e89400e1 --- /dev/null +++ b/vehicle_subscription/wizard/change_subscription_views.xml @@ -0,0 +1,29 @@ + + + + + change.subscription.view.form + change.subscription + +
+ + + +
+
+
+
+
+ + + ir.actions.act_window + change.subscription + form + + new + +