Browse Source

Jan 01 [ADD] : Initial Commit 'vehicle_subscription'

pull/254/merge
AjmalCybro 4 months ago
parent
commit
624dd02de3
  1. 46
      vehicle_subscription/README.rst
  2. 24
      vehicle_subscription/__init__.py
  3. 69
      vehicle_subscription/__manifest__.py
  4. 23
      vehicle_subscription/controllers/__init__.py
  5. 442
      vehicle_subscription/controllers/vehicle_subscription.py
  6. 133
      vehicle_subscription/controllers/website_portal.py
  7. 16
      vehicle_subscription/data/ir_cron_data.xml
  8. 79
      vehicle_subscription/data/mail_data.xml
  9. 13
      vehicle_subscription/data/product_template_data.xml
  10. 12
      vehicle_subscription/data/website_menu_data.xml
  11. 6
      vehicle_subscription/doc/RELEASE_NOTES.md
  12. 29
      vehicle_subscription/models/__init__.py
  13. 35
      vehicle_subscription/models/account_move.py
  14. 296
      vehicle_subscription/models/cancellation_request.py
  15. 361
      vehicle_subscription/models/fleet_subscription.py
  16. 69
      vehicle_subscription/models/fleet_vehicle.py
  17. 34
      vehicle_subscription/models/fleet_vehicle_model.py
  18. 46
      vehicle_subscription/models/insurance_type.py
  19. 80
      vehicle_subscription/models/subscription_request.py
  20. 47
      vehicle_subscription/models/vehicle_insurance.py
  21. 13
      vehicle_subscription/report/cancellation_request.xml
  22. 8
      vehicle_subscription/security/ir.model.access.csv
  23. 18
      vehicle_subscription/security/vehicle_subscription_groups.xml
  24. BIN
      vehicle_subscription/static/description/assets/icons/check.png
  25. BIN
      vehicle_subscription/static/description/assets/icons/chevron.png
  26. BIN
      vehicle_subscription/static/description/assets/icons/cogs.png
  27. BIN
      vehicle_subscription/static/description/assets/icons/consultation.png
  28. BIN
      vehicle_subscription/static/description/assets/icons/ecom-black.png
  29. BIN
      vehicle_subscription/static/description/assets/icons/education-black.png
  30. BIN
      vehicle_subscription/static/description/assets/icons/hotel-black.png
  31. BIN
      vehicle_subscription/static/description/assets/icons/license.png
  32. BIN
      vehicle_subscription/static/description/assets/icons/lifebuoy.png
  33. BIN
      vehicle_subscription/static/description/assets/icons/manufacturing-black.png
  34. BIN
      vehicle_subscription/static/description/assets/icons/pos-black.png
  35. BIN
      vehicle_subscription/static/description/assets/icons/puzzle.png
  36. BIN
      vehicle_subscription/static/description/assets/icons/restaurant-black.png
  37. BIN
      vehicle_subscription/static/description/assets/icons/service-black.png
  38. BIN
      vehicle_subscription/static/description/assets/icons/trading-black.png
  39. BIN
      vehicle_subscription/static/description/assets/icons/training.png
  40. BIN
      vehicle_subscription/static/description/assets/icons/update.png
  41. BIN
      vehicle_subscription/static/description/assets/icons/user.png
  42. BIN
      vehicle_subscription/static/description/assets/icons/wrench.png
  43. BIN
      vehicle_subscription/static/description/assets/misc/categories.png
  44. BIN
      vehicle_subscription/static/description/assets/misc/check-box.png
  45. BIN
      vehicle_subscription/static/description/assets/misc/compass.png
  46. BIN
      vehicle_subscription/static/description/assets/misc/corporate.png
  47. BIN
      vehicle_subscription/static/description/assets/misc/customer-support.png
  48. BIN
      vehicle_subscription/static/description/assets/misc/cybrosys-logo.png
  49. BIN
      vehicle_subscription/static/description/assets/misc/features.png
  50. BIN
      vehicle_subscription/static/description/assets/misc/logo.png
  51. BIN
      vehicle_subscription/static/description/assets/misc/pictures.png
  52. BIN
      vehicle_subscription/static/description/assets/misc/pie-chart.png
  53. BIN
      vehicle_subscription/static/description/assets/misc/right-arrow.png
  54. BIN
      vehicle_subscription/static/description/assets/misc/star.png
  55. BIN
      vehicle_subscription/static/description/assets/misc/support.png
  56. BIN
      vehicle_subscription/static/description/assets/misc/whatsapp.png
  57. BIN
      vehicle_subscription/static/description/assets/misc/white-arrow.png
  58. BIN
      vehicle_subscription/static/description/assets/modules/budget_image.png
  59. BIN
      vehicle_subscription/static/description/assets/modules/credit_image.png
  60. BIN
      vehicle_subscription/static/description/assets/modules/employee_image.png
  61. BIN
      vehicle_subscription/static/description/assets/modules/export_image.png
  62. BIN
      vehicle_subscription/static/description/assets/modules/gantt_image.png
  63. BIN
      vehicle_subscription/static/description/assets/modules/quotation_image.png
  64. BIN
      vehicle_subscription/static/description/assets/screenshots/1.png
  65. BIN
      vehicle_subscription/static/description/assets/screenshots/10.png
  66. BIN
      vehicle_subscription/static/description/assets/screenshots/11.png
  67. BIN
      vehicle_subscription/static/description/assets/screenshots/12.png
  68. BIN
      vehicle_subscription/static/description/assets/screenshots/14.png
  69. BIN
      vehicle_subscription/static/description/assets/screenshots/2.png
  70. BIN
      vehicle_subscription/static/description/assets/screenshots/4.png
  71. BIN
      vehicle_subscription/static/description/assets/screenshots/5.png
  72. BIN
      vehicle_subscription/static/description/assets/screenshots/6.png
  73. BIN
      vehicle_subscription/static/description/assets/screenshots/7.png
  74. BIN
      vehicle_subscription/static/description/assets/screenshots/8.png
  75. BIN
      vehicle_subscription/static/description/assets/screenshots/9.png
  76. BIN
      vehicle_subscription/static/description/assets/screenshots/hero.gif
  77. BIN
      vehicle_subscription/static/description/banner.png
  78. BIN
      vehicle_subscription/static/description/icon.png
  79. 637
      vehicle_subscription/static/description/index.html
  80. 14
      vehicle_subscription/static/src/js/booking_cancellation.js
  81. 14
      vehicle_subscription/static/src/js/change_subscription.js
  82. 31
      vehicle_subscription/static/src/js/change_subscription_form.js
  83. 18
      vehicle_subscription/static/src/js/change_subscription_request.js
  84. 31
      vehicle_subscription/static/src/js/subscription_cancellation.js
  85. 14
      vehicle_subscription/static/src/js/subscription_change_boolean_false.js
  86. 14
      vehicle_subscription/static/src/js/subscription_change_button.js
  87. 14
      vehicle_subscription/static/src/js/subscription_form_success.js
  88. 14
      vehicle_subscription/static/src/js/subscription_vehicle_missing.js
  89. 115
      vehicle_subscription/static/src/js/vehicle_booking.js
  90. 14
      vehicle_subscription/static/src/js/vehicle_missing.js
  91. 44
      vehicle_subscription/static/src/js/website_subscription.js
  92. 14
      vehicle_subscription/views/account_move_views.xml
  93. 46
      vehicle_subscription/views/cancellation_request_views.xml
  94. 304
      vehicle_subscription/views/change_vehicle_subscription_template.xml
  95. 117
      vehicle_subscription/views/fleet_subscription_views.xml
  96. 42
      vehicle_subscription/views/fleet_vehicle_model_views.xml
  97. 44
      vehicle_subscription/views/insurance_type_views.xml
  98. 250
      vehicle_subscription/views/online_subscription_template.xml
  99. 108
      vehicle_subscription/views/online_vehicle_cancellation_template.xml
  100. 197
      vehicle_subscription/views/online_vehicle_template.xml

46
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 <https://cybrosys.com/>`__
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 <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

24
vehicle_subscription/__init__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import controllers
from . import models
from . import wizard

69
vehicle_subscription/__manifest__.py

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
{
'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,
}

23
vehicle_subscription/controllers/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import vehicle_subscription
from . import website_portal

442
vehicle_subscription/controllers/vehicle_subscription.py

@ -0,0 +1,442 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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/<int:subscription_id>'],
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')

133
vehicle_subscription/controllers/website_portal.py

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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

16
vehicle_subscription/data/ir_cron_data.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">
<!--Cron action for checking expiration-->
<record id="ir_cron_expiry_date_action" model="ir.cron">
<field name="name">Subscription Expired</field>
<field name="model_id" ref="model_fleet_subscription"/>
<field name="state">code</field>
<field name="code">model._onchange_end_date()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="active" eval="True"/>
</record>
</data>
</odoo>

79
vehicle_subscription/data/mail_data.xml

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">
<!--Cron action for checking expiration-->
<record id="ir_cron_expiry_date_action" model="ir.cron">
<field name="name">Subscription Expired</field>
<field name="model_id" ref="model_fleet_subscription"/>
<field name="state">code</field>
<field name="code">model._onchange_end_date()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="active" eval="True"/>
</record>
<!--Subscription cancellation mail template-->
<record id="cancellation_request_mail" model="mail.template">
<field name="name">Vehicle Cancellation</field>
<field name="model_id"
ref="vehicle_subscription.model_cancellation_request"/>
<field name="subject">Subscription Cancellation</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p>
Dear
<br/>
Sorry....you need to pay amount till date inorder to
cancel
subscription.Your invoice is attached
below
<br/>
</p>
Regards,
<br/>
<t t-out="user.name"/>
</div>
</field>
</record>
<!-- Mail template for refund in subscription cancellation -->
<record id="cancellation_request_refund_mail" model="mail.template">
<field name="name">Vehicle Cancellation</field>
<field name="model_id"
ref="vehicle_subscription.model_cancellation_request"/>
<field name="subject">Subscription Cancellation</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p>
Dear
<br/>
The amount should be refunded immediately.
<br/>
</p>
Regards,
<br/>
<t t-out="user.name"/>
</div>
</field>
</record>
<!-- Mail template for approval of subscription cancellation -->
<record id="cancellation_approved" model="mail.template">
<field name="name">Vehicle Cancellation</field>
<field name="model_id"
ref="vehicle_subscription.model_cancellation_request"/>
<field name="subject">Subscription Cancellation</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p>
Dear
<br/>
Your request for subscription cancellation is approved
<br/>
</p>
Regards,
<br/>
<t t-out="user.name"/>
</div>
</field>
</record>
</data>
</odoo>

13
vehicle_subscription/data/product_template_data.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!--Created vehicle subscription as a service product-->
<record id="product_template_vehicle_subscription_form"
model="product.template">
<field name="name">Vehicle Subscription</field>
<field name="detailed_type">service</field>
<field name="invoice_policy">order</field>
<field name="sequence" type="int">55</field>
</record>
</data>
</odoo>

12
vehicle_subscription/data/website_menu_data.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Subscription form in website -->
<data noupdate="1">
<record id="menu_subscription_form" model="website.menu">
<field name="name">Subscription Form</field>
<field name="url">/online/subscription</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence" type="int">55</field>
</record>
</data>
</odoo>

6
vehicle_subscription/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <vehicle_subscription>
#### 13.09.2024
#### Version 15.0.1.0.0
#### ADD
Initial Commit Vehicle Subscription Management

29
vehicle_subscription/models/__init__.py

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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

35
vehicle_subscription/models/account_move.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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')

296
vehicle_subscription/models/cancellation_request.py

@ -0,0 +1,296 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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}"))

361
vehicle_subscription/models/fleet_subscription.py

@ -0,0 +1,361 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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()

69
vehicle_subscription/models/fleet_vehicle.py

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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

34
vehicle_subscription/models/fleet_vehicle_model.py

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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")

46
vehicle_subscription/models/insurance_type.py

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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")

80
vehicle_subscription/models/subscription_request.py

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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})

47
vehicle_subscription/models/vehicle_insurance.py

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swaraj R (<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
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'))

13
vehicle_subscription/report/cancellation_request.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Report Action for attachment in model cancellation request-->
<record id="cancellation_request_action_report" model="ir.actions.report">
<field name="name">Fleet Vehicle Subscription Cancel</field>
<field name="model">cancellation.request</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">account.report_invoice_with_payments</field>
<field name="report_file">account.report_invoice_with_payments</field>
<field name="binding_model_id" ref="model_cancellation_request"/>
<field name="binding_type">report</field>
</record>
</odoo>

8
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
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_fleet_subscription access.fleet.subscription model_fleet_subscription base.group_user 1 1 1 1
3 access_vehicle_insurance access.vehicle.insurance model_vehicle_insurance base.group_user 1 1 1 1
4 access_subscription_request access.subscription.request model_subscription_request base.group_user 1 1 1 1
5 access_change_subscription access.change.subscription model_change_subscription base.group_user 1 1 1 1
6 access_insurance_type access.insurance.type model_insurance_type base.group_user 1 1 1 1
7 access_insurance_coverage access.insurance.coverage model_insurance_coverage base.group_user 1 1 1 1
8 access_cancellation_request access.cancellation.request model_cancellation_request base.group_user 1 1 1 1

18
vehicle_subscription/security/vehicle_subscription_groups.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--User access levels for Fleet module-->
<record id="module_vehicle_subscription" model="ir.module.category">
<field name="name">Vehicle Subscription</field>
<field name="description">User access levels for Fleet module</field>
<field name="sequence">10</field>
</record>
<record id="vehicle_subscription_group_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="module_vehicle_subscription"/>
</record>
<record id="vehicle_subscription_group_manager" model="res.groups">
<field name="name">Manager</field>
<field name="implied_ids" eval="[(4, ref('vehicle_subscription_group_user'))]"/>
<field name="category_id" ref="module_vehicle_subscription"/>
</record>
</odoo>

BIN
vehicle_subscription/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
vehicle_subscription/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
vehicle_subscription/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
vehicle_subscription/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
vehicle_subscription/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
vehicle_subscription/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
vehicle_subscription/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
vehicle_subscription/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
vehicle_subscription/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
vehicle_subscription/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
vehicle_subscription/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
vehicle_subscription/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
vehicle_subscription/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
vehicle_subscription/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
vehicle_subscription/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
vehicle_subscription/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
vehicle_subscription/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
vehicle_subscription/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
vehicle_subscription/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
vehicle_subscription/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
vehicle_subscription/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
vehicle_subscription/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
vehicle_subscription/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
vehicle_subscription/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
vehicle_subscription/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
vehicle_subscription/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
vehicle_subscription/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
vehicle_subscription/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
vehicle_subscription/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
vehicle_subscription/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
vehicle_subscription/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
vehicle_subscription/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
vehicle_subscription/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
vehicle_subscription/static/description/assets/misc/white-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

BIN
vehicle_subscription/static/description/assets/modules/budget_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
vehicle_subscription/static/description/assets/modules/credit_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
vehicle_subscription/static/description/assets/modules/employee_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
vehicle_subscription/static/description/assets/modules/export_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
vehicle_subscription/static/description/assets/modules/gantt_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
vehicle_subscription/static/description/assets/modules/quotation_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
vehicle_subscription/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
vehicle_subscription/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

BIN
vehicle_subscription/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
vehicle_subscription/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

637
vehicle_subscription/static/description/index.html

@ -0,0 +1,637 @@
<div style="background-color: #714B67; height: 810px; width: 100%; padding: 15px; position: relative;">
<!-- TITLE BAR -->
<div class="d-flex align-items-center justify-content-between"
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;">
<img src="assets/misc/cybrosys-logo.png" width="42" height="42" style="width: 42px; height: 42px;"/>
<div>
<div
style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Community
</div>
<div
style="color: #875A7B; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Enterprise
</div>
<div
style="color: #017E84; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Odoo.sh
</div>
</div>
</div>
<!-- END OF TITLE BAR -->
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<!-- APP HERO -->
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">
Vehicle Subscription Management</h1>
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">Vehicle Subscription Management From Website</p>
<!-- END OF APP HERO -->
<img src="assets/screenshots/hero.gif" class="img-responsive"
style="width: 100%; margin-left: auto; margin-right: auto;"/>
</div>
</div>
</div>
</div>
<!-- NAVIGATION SECTION -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/compass.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Explore This
Module</h2>
</div>
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;">
<div class="col-sm-12 col-md-6 my-3">
<a href="#overview">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn
more about this
module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#features">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View
features of this
module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#screenshots">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View
screenshots for this
module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
</div>
<!-- END OF NAVIGATION SECTION -->
<!-- OVERVIEW SECTION -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pie-chart.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Overview
</h2>
</div>
<div class="row" style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 py-4">
This module helps you to Subscribe,Cancel and Change subscription through website as well as backend.
</div>
</div>
<!-- END OF OVERVIEW SECTION -->
<!-- FEATURES SECTION -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/features.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Features
</h2>
</div>
<div class="row" style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 col-md-6">
<div class="d-flex align-items-center" style="margin-top: 40px; margin-bottom: 40px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Community &amp;
Enterprise Support.</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Subscription of vehicle</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Cancellation of subscription</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Option to switch subscription</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Payment option Monthly and Full Payment from website</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Down Payment </span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Option to choose vehicle based on Location</span>
</div>
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Can decide Insurance type</span>
</div>
</div>
</div>
<!-- END OF FEATURES SECTION -->
<!-- SCREENSHOTS SECTION -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="screenshots">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pictures.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Screenshots
</h2>
</div>
<div class="row">
<div class="col-sm-12">
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Vehicle Subscription
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Go to Website ->
Subscription Form ->
Choose Location , Insurance and duration of subscription.</p>
<img src="assets/screenshots/1.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Choose Vehicle
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Book vehicle from
listed vehicle user can change default km choose fuel type ,choose payment type. </p>
<img src="assets/screenshots/2.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Subscription Order
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Go to Fleet
-> Under Fleet Menu ->In Subscription submenu -> Subscription Order get created </p>
<img src="assets/screenshots/4.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Subscription order in
Portal view.
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Go to My Account ->
Subscription Order -> User can see their invoices so that they can pay change and cancel subscription
from invoice</p>
<img src="assets/screenshots/5.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Cancel subscription
</h3>
<img src="assets/screenshots/6.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Cancellation form
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Specify the Reason </p>
<img src="assets/screenshots/7.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Cancel Request
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">By submitting the
cancellation form cancel request will be generated in backend.</p>
<img src="assets/screenshots/9.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Switch Subscription
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">From portal , user have
option to Change Subscription.</p>
<img src="assets/screenshots/10.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Change Subscription
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">User need to choose
whether he or she needs to change for same model vehicle or different model and need to specify the
reason.</p>
<img src="assets/screenshots/11.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Switch Subscription
to new vehicle
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">If user chosen same
model, then user will redirect to the different form where user can choose vehicle of same model .</p>
<img src="assets/screenshots/12.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Switch Request
</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Go to Fleet ->
Configuration -> Change Subscription -> Request for switching subscription will be generated</p>
<img src="assets/screenshots/14.png" class="img-thumbnail">
</div>
</div>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- SUGGESTED PRODUCTS -->
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center"
style="text-align: center; padding: 2.5rem 1rem !important;">
<h2 style="color: #212529 !important;">Suggested Products</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" />
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner">
<div class="carousel-item active" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/export_stockinfo_xls/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/export_image.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/custom_gantt_view/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/gantt_image.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/sales_credit_limit/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/credit_image.png">
</div>
</a>
</div>
</div>
<div class="carousel-item" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/base_account_budget/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/budget_image.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/product_to_quotation/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/quotation_image.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/employee_documents_expiry/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/employee_image.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
style="left:-25px;width: 35px;color: #000;"> <span class="carousel-control-prev-icon"><i
class="fa fa-chevron-left" style="font-size:24px"></i></span> </a> <a
class="carousel-control-next" href="#demo1" data-slide="next"
style="right:-25px;width: 35px;color: #000;">
<span class="carousel-control-next-icon"><i class="fa fa-chevron-right"
style="font-size:24px"></i></span>
</a>
</div>
</div>
</div>
<!-- END OF SUGGESTED PRODUCTS -->
<!-- OUR SERVICES -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/star.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Our Services
</h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/cogs.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Customization</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/wrench.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/lifebuoy.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Support</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/user.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Hire
Odoo
Developer</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/puzzle.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Integration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/update.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Migration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/consultation.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Consultancy</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/training.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/license.png" class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Licensing Consultancy</h6>
</div>
</div>
</div>
<!--END OF OUR SERVICES -->
<!-- OUR INDUSTRIES -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/corporate.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Our
Industries
</h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/trading-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Trading
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easily procure
and
sell your products</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/pos-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
POS
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easy
configuration
and convivial experience</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/education-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Education
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
A platform for
educational management</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/manufacturing-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Manufacturing
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Plan, track and
schedule your operations</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/ecom-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
E-commerce &amp; Website
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Mobile
friendly,
awe-inspiring product pages</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/service-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Service Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Keep track of
services and invoice</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/restaurant-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Restaurant
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Run your bar or
restaurant methodically</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/hotel-black.png" class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Hotel Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
An
all-inclusive
hotel management application</p>
</div>
</div>
</div>
</div>
<!-- END OF OUR INDUSTRIES -->
<!-- SUPPORT -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/customer-support.png"/>
</div>
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Support
</h2>
</div>
<div class="container mt-5">
<div class="row">
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/support.png" height="48" width="48" style="width: 42px; height: 42px;"/>
</div>
<div>
<h4>Need Help?</h4>
<p style="line-height: 100%;">Got questions or need help? Get in touch.</p>
<a href="mailto:odoo@cybrosys.com">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">
odoo@cybrosys.com</p>
</a>
</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/whatsapp.png" height="52" width="52" style="width: 52px; height: 52px;"/>
</div>
<div>
<h4>WhatsApp</h4>
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p>
<a href="https://api.whatsapp.com/send?phone=918606827707">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">+91 86068
27707</p>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center">
<img src="assets/misc/logo.png" width="144" height="31"
style="width:144px; height: 31px; margin-top: 40px;"/>
</div>
</div>
</div>
<!-- END OF SUPPORT -->

14
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();
}
})

14
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();
}
})

31
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);
});
});
},
})

18
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);
}
})

31
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);
});
});
}
})

14
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();
}
})

14
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();
}
})

14
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();
}
})

14
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();
}
})

115
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();
}
})

14
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();
}
})

44
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';
},
})

14
vehicle_subscription/views/account_move_views.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Used to add field in account.move-->
<record id="view_move_form" model="ir.ui.view">
<field name="name">account.move.view.form.inherited.vehicle.subscription</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='currency_id']" position="after">
<field name="is_subscription" invisible="1"/>
</xpath>
</field>
</record>
</odoo>

46
vehicle_subscription/views/cancellation_request_views.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Action of cancellation request-->
<record id="cancellation_request_action" model="ir.actions.act_window">
<field name="name">Cancellation Request</field>
<field name="res_model">cancellation.request</field>
<field name="view_mode">tree,form</field>
</record>
<!--Form view of the cancellation request-->
<record id="cancellation_request_view_form" model="ir.ui.view">
<field name="name">cancellation.request.view.form</field>
<field name="model">cancellation.request</field>
<field name="arch" type="xml">
<form string="Channel">
<header>
<field name="state" widget="statusbar"/>
<button name="action_request" class="oe_highlight"
states="draft" string="Request"
type="object"/>
<button name="action_approve" class="oe_highlight"
states="to_approve" string="Approve"
type="object"
groups="vehicle_subscription.vehicle_subscription_group_manager"/>
</header>
<sheet>
<group>
<field name="customer_id"/>
<field name="vehicle_id"/>
<field name="date"/>
<field name="reason"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!--Menu item of cancellation-->
<menuitem id="cancellation_request_menu_action"
name="Cancellation Request"
parent="fleet.fleet_configuration"
action="cancellation_request_action"
sequence="7"/>
</odoo>

304
vehicle_subscription/views/change_vehicle_subscription_template.xml

@ -0,0 +1,304 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Template for change subscription-->
<template id="subscription_change_form" name="Vehicle Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty change_sub_vehicle">
<section class="vehicles_website_form" id="change_my_vehicle">
<div class="container">
<br/>
<h1 style="text-align: center;">Change Subscription
</h1>
<br/>
<form action="/online/subscription/change/vehicle"
method="post" enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="" data-success-page=""
style="width: 50%;margin: 0 auto;padding: 40px;background: white;border-radius: 6px;box-shadow: 0 3px 10px rgb(0 0 0 /0.2);margin-bottom:60px">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor"
style="display:none">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Name
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<div class="col-sm">
<input class="form-control s_website_form_input"
name='customer'
type="text"
t-att-value="customers"/>
</div>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Vehicle
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="change_vehicle_change"
type="text"
class="form-control s_website_form_input"
name="vehicle"
required="1">
<option value="">Choose</option>
<option t-foreach="vehicles"
t-as="vehicle"
t-att-value="vehicle.id"
required="1">
<t t-esc="vehicle.name"/>
</option>
</select>
</div>
</div>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px" for="studio1">
<span class="s_website_form_label_content">
Change vehicle to same model
</span>
<span class="s_website_form_mark">*</span>
</label>
<div class="col-sm">
<input type="checkbox"
name="checkbox_model"
class="form-check-input"
id="checkbox_vehicle_change"/>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px" for="studio1">
<span class="s_website_form_label_content">
Reason for changing vehicle
</span>
<span class="s_website_form_mark">*</span>
</label>
<div class="col-sm">
<input id="reason" type="text"
class="form-control s_website_form_input"
name="reason" required="1"
placeholder="Reason for change subscription"/>
</div>
</div>
<br/>
<br/>
<div class="form-group col-12 s_website_form_submit"
data-name="change Button"
style="margin-bottom: 40px;">
<div style="width: 200px;"
class="s_website_form_label"/>
<button type="submit" id="cancel"
class="btn btn-primary"
style="float: right;">Change
</button>
</div>
</form>
</div>
</section>
</div>
</t>
</template>
<!-- < On clicking change button if boolean is enabled >-->
<template id="subscription_change_button" name="Vehicle Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty submit_boolean_on ">
<section class="vehicles_website_form" id="boolean_true">
<div class="btn btn-primary redirect_back_with_data"
style="background:none; border:none;">
<span style="font-size:18px; color:#000; background-image:url('https://cdn-icons-png.flaticon.com/512/2985/2985162.png');width: 63px;
height: 63px;display: block;background-size: 60%;margin-top: 23px;background-position: center;background-repeat: no-repeat;"/>
</div>
<div class="container">
<br/>
<h1 style="text-align: center;">Choose New vehicle</h1>
<br/>
<form action="/online/subscription/change/button"
method="post" enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="" data-success-page=""
style="width: 50%;margin: 0 auto;padding: 30px;background: white;box-shadow: 0 3px 10px rgb(0 0 0 /0.2);margin-bottom: 40px;">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="s_website_form_rows row s_col_no_bgcolor"
style="display:none">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Customer
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input class="form-control s_website_form_input"
name='customer' type="text"
t-att-value="customer_name"/>
</div>
</div>
</div>
</div>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Reason
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input class="form-control s_website_form_input"
name='reason' type="text"
t-att-value="reason"/>
</div>
</div>
</div>
</div>
<br/>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Current Vehicle
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input class="form-control"
name='vehicle'
id="current_vehicle_name"
type="text"
t-att-value="vehicle_name"/>
</div>
</div>
</div>
</div>
<br/>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
New Vehicle
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="choose_vehicle"
type="text"
class="form-control s_website_form_input"
name="new_vehicle"
required="1">
<option value="">Choose new or different of current vehicle</option>
<option t-foreach="vehicles"
t-as="vehicle"
t-att-value="vehicle.id"
required="1">
<t t-esc="vehicle.name"/>
</option>
</select>
</div>
</div>
</div>
</div>
<br/>
<br/>
<div class="form-group col-12 s_website_form_submit"
data-name="change Button"
style="margin-bottom: 40px;">
<div style="width: 200px;"
class="s_website_form_label"/>
<button type="submit" id="submit_request"
class="btn btn-primary"
style="float: right;">Submit
</button>
</div>
</form>
</div>
</section>
</div>
</t>
</template>
<!--< on clicking change button if boolean is not enabled >-->
<template id="subscription_change_boolean_false" name="Vehicle Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty ">
<section class="vehicles_website_form" id="boolean_false"
style="width: 80%;margin: 0 auto;">
<div class="btn btn-primary redirect_back_with_data"
style="background:none; border:none;">
<span style="font-size:18px; color:#000; background-image:url('https://cdn-icons-png.flaticon.com/512/2985/2985162.png');width: 63px;
height: 63px;display: block;background-size: 60%;margin-top: 23px;background-position: center;background-repeat: no-repeat;"/>
</div>
<div class="container"
style="align-items: center; display: flex;flex-direction: column;}">
<br/>
<h5>You need to cancel subscription inorder to change
to different model
</h5>
<br/>
<form action="/online/proceed/cancellation"
method="post" enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="" data-success-page="">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="form-group col-12 s_website_form_submit"
data-name="change Button">
<div class="s_website_form_label"/>
<button type="submit" id="proceed_cancel"
class="btn btn-primary">Proceed with
Cancellation
</button>
</div>
</form>
</div>
</section>
</div>
</t>
</template>
</odoo>

117
vehicle_subscription/views/fleet_subscription_views.xml

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Action of fleet.subscription-->
<record id="fleet_subscription_action" model="ir.actions.act_window">
<field name="name">Subscription</field>
<field name="res_model">fleet.subscription</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Form view of the fleet subscription-->
<record id="fleet_subscription_view_form" model="ir.ui.view">
<field name="name">fleet.subscription.view.form</field>
<field name="model">fleet.subscription</field>
<field name="arch" type="xml">
<form string="Channel">
<header>
<button name="action_invoice" class="oe_highlight"
state="draft" string="Create Order"
type="object"
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_request" class="oe_highlight"
string="Change subscription"
type="object"
attrs="{'invisible': ['|',('state', '!=', 'subscribed'),'&amp;',('is_invisible_sub','=',True),('state','=','subscribed')]}"/>
<button name="action_cancel" class="oe_highlight"
state="draft" string="Cancel subscription"
type="object"
attrs="{'invisible': [('state', '!=', 'subscribed')]}"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="object"
name="action_get_car_insurance"
icon="fa-car"
attrs="{'invisible': [('insurance_type_id', '=', False)]}"
string="Insurance">
</button>
<button class="oe_stat_button" type="object"
name="action_get_sale"
icon="fa-dollar"
attrs="{ 'invisible': [('state', '=', 'draft')] }">
<field string="Sale Order" name="sale"
widget="statinfo"/>
</button>
<button class="oe_stat_button" type="object"
name="action_get_invoice"
icon="fa-pencil-square-o"
attrs="{ 'invisible': [('state', '=', 'draft')]}">
<field string="Invoice" name="invoice"
widget="statinfo"/>
</button>
<button class="oe_stat_button" type="object"
name="action_get_refund"
attrs="{ 'invisible': [('refund', '=', 0), ('state', '!=', 'cancel')]}">
<field string="Refund" name="refund"
widget="statinfo"/>
</button>
</div>
</div>
<group col="2">
<group>
<field name="vehicle_id"/>
<field name="vehicle_ids" widget="many2many_tags"
invisible="True"/>
<field name="customer_id"/>
<field name="insurance_type_id"/>
<field name="seating_capacity"/>
<field name="price"/>
<field name="sale_id" invisible="True"/>
<field name="extra_price"/>
<field name="invoice_ids" widget="many2many_tags"
invisible="True"/>
<field name="is_invisible_sub" invisible="True"/>
</group>
<group>
<field name="start_date"/>
<field name="end_date"/>
<field name="cancellation_date"
attrs="{ 'invisible': [('state', '!=', 'cancel')] }"/>
<field name="city"/>
<field name="state_id"/>
<field name="country_id"/>
</group>
</group>
<notebook>
<page string="Features">
<group string="Add Extra Features">
<field name="fuel"/>
<field name="fuel_type"
attrs="{ 'invisible': [('fuel', '!=', 'with_fuel')] }"/>
<field name="mileage"
attrs="{ 'invisible': [('fuel', '!=', 'with_fuel')] }"/>
<field name="fuel_rate"
attrs="{ 'invisible': [('fuel', '!=', 'with_fuel')] }"/>
<field name="charge_km"/>
<field name="default_km"/>
<field name="extra_km"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!--Menu item of vehicle subscription-->
<menuitem id="vehicle_subscription_menu_action"
name="Subscription"
action="fleet_subscription_action"
parent="fleet.fleet_vehicles"
/>
</odoo>

42
vehicle_subscription/views/fleet_vehicle_model_views.xml

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Used to add field in fleet.vehicle.model-->
<record id="fleet_vehicle_model_view_form" model="ir.ui.view">
<field name="name">
fleet.vehicle.model.view.form.inherited.vehicle.subscription
</field>
<field name="model">fleet.vehicle.model</field>
<field name="inherit_id" ref="fleet.fleet_vehicle_model_view_form"/>
<field name="arch" type="xml">
<xpath expr="//group/field[@name='default_fuel_type']"
position="after">
<field name="mileage"/>
</xpath>
</field>
</record>
<!--Add fields in fleet.vehicle-->
<record id="fleet_vehicle_view_form" model="ir.ui.view">
<field name="name">
fleet.vehicle.view.form.inherited.vehicle.subscription
</field>
<field name="model">fleet.vehicle</field>
<field name="inherit_id" ref="fleet.fleet_vehicle_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='horsepower_tax']" position='after'>
<field name="free_km"/>
<field name="subscription_price"/>
<field name="states_id"/>
<field name="countries_id"/>
<field name="insurance" invisible="1"/>
<field name="end" invisible="1"/>
<field name="start" invisible="1"/>
<field name="duration" invisible="1"/>
<field name="fuel" invisible="1"/>
<field name="fuel_rate"/>
<field name="charge_km" invisible="0"/>
<field name="extra_km" invisible="1"/>
<field name="mileage"/>
</xpath>
</field>
</record>
</odoo>

44
vehicle_subscription/views/insurance_type_views.xml

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Action of insurance.type-->
<record id="insurance_type_action" model="ir.actions.act_window">
<field name="name">Insurance Type</field>
<field name="res_model">insurance.type</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Form view of the insurance type-->
<record id="insurance_type_view_form" model="ir.ui.view">
<field name="name">insurance.type.view.form</field>
<field name="model">insurance.type</field>
<field name="arch" type="xml">
<form string="Channel">
<sheet>
<group>
<field name="name"/>
</group>
<notebook>
<page string="Coverages">
<field name="coverage_ids">
<tree editable="bottom">
<field name="description"/>
<field name="coverage_price"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!--Menu item of insurance type-->
<menuitem id="insurance_type_menu_action"
name="Insurance Type"
parent="fleet.fleet_configuration"
action="insurance_type_action"
groups="vehicle_subscription.vehicle_subscription_group_manager"
sequence="8"/>
</odoo>

250
vehicle_subscription/views/online_subscription_template.xml

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Template for Subscription-->
<template id="subscription_form" name="Subscription Form">
<t t-call="website.layout">
<div class="" style="background-image: linear-gradient(to bottom, rgb(255 255 255 / 43%), rgb(0 0 0 / 50%)), url('https://cdn.pixabay.com/photo/2017/01/31/17/44/highway-2025863_960_720.jpg');
background-size: cover;background-position: center;">
<div class="online_vehicle_main_form"
style="max-width: 1320px; margin: 0px auto;width: 100%;">
<div id="wrap" class="oe_structure oe_empty"
style="display: flex; justify-content: flex-end;align-items: center;">
<section class="" data-vcss="001"
style="padding: 50px 0px;">
<div class="container"
style="padding: 30px 50px;border-radius: 6px;background-color: #fffafa75;box-shadow: 0 3px 10px rgb(255 255 255 / 17%);">
<br/>
<h1 style="text-align: center;">
</h1>
<br/>
<form action="/online/subscription/next"
method="post"
enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="" data-success-page="">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="s_website_form_rows row s_col_no_bgcolor"
id="whole_sub">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char"
data-name="Field">
<div class="form-group col-12 s_website_form_field s_website_form_required"
style="margin-left: -3%;"
data-type="char"
data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Location
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div id="location_temp"
style="display:none">
<div class="location_modal_container"
style="top: 13% !important; z-index: 100; position: fixed;left: 0;height: 100%;width: 100%;background: rgba(0,0,0,0.2);top: 0;display: flex;align-items: center;justify-content: center;">
<div class="modal-dialog">
<div class="modal-content"
style="width: 400px;">
<div class="modal-header"
style="justify-content: center;">
<h4 class="modal-title">
Choose
Location
</h4>
</div>
<div class="modal-body">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
for="studio1">
<span class="s_website_form_label_content">
State
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="state_id"
type="text"
class="form-control s_website_form_input"
name="state"
required="1">
<option value=""/>
<option t-foreach="states"
t-as="state"
t-att-value="state.id">
<t t-esc="state.name"/>
</option>
</select>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
for="studio1">
<span class="s_website_form_label_content">
City
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="city_id"
type="text"
class="form-control s_website_form_input"
name="city"
required="1">
<option t-foreach="cities"
t-as="city"
t-att-value="city">
<t t-esc="city"/>
</option>
</select>
</div>
</div>
<br/>
</div>
<div class="modal-footer">
<button type="button"
style="background: black;color: white;"
class="btn btn-default"
id="dismiss"
data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm"
id="col-sm-location">
<input id="location_id"
type="text"
class="form-control s_website_form_input"
data-toggle="modal"
autocomplete="off"
data-target="#location_temp">
</input>
</div>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Start Date
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input id="start_date"
type="Date"
class="form-control s_website_form_input"
name="start_date"
required="1"/>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
End Date
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input id="end_date"
type="Date"
class="form-control s_website_form_input"
name="end_date"
required="1"/>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Insurance Type
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="insurance_type"
type="text"
class="form-control s_website_form_input"
name="insurance_type"
required="1">
<option value=""/>
<option t-foreach="insurance_type"
t-as="insurance"
t-att-value="insurance.id">
<t t-esc="insurance.name"/>
</option>
</select>
</div>
</div>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px"
for="studio1">
<span class="s_website_form_label_content">
Seating Capacity
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<input id="seating_capacity"
type="text"
class="form-control s_website_form_input"
name="seating_capacity"
required="1"/>
</div>
</div>
<br/>
<br/>
<div class="form-group col-12 "
data-name="Next Button">
<div style="width: 200px;"
class="s_website_form_label"/>
<button type="submit" id=""
class="btn btn-primary"
style="width: 100%;">
Next
</button>
</div>
</div>
</div>
<br/>
<br/>
</form>
</div>
</section>
</div>
</div>
</div>
</t>
</template>
</odoo>

108
vehicle_subscription/views/online_vehicle_cancellation_template.xml

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Template for subscription cancellation-->
<template id="subscription_cancellation_form" name="Vehicle Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty cancel_sub">
<section class="vehicle_s_website_form" id="book_my_vehicle">
<div class="container">
<br/>
<h1 style="text-align: center;">Choose Vehicle</h1>
<br/>
<form action="/online/cancellation/click" method="post"
enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="" data-success-page=""
style="width: 50%;margin: 0 auto;padding: 30px 58px;border-radius: 6px;padding: 40px 60px;background: white;box-shadow: 0 3px 10px rgb(0 0 0 /0.2);">
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 125px"
for="studio1">
<span class="s_website_form_label_content">
Name
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<div class="col-sm">
<input class="form-control s_website_form_input"
id="customer_name"
name='customer'
type="text"
t-att-value="customers"/>
</div>
<br/>
<br/>
</div>
</div>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 125px"
for="studio1">
<span class="s_website_form_label_content">
Vehicle
</span>
<span class="s_website_form_mark">
*
</span>
</label>
<div class="col-sm">
<select id="vehicle_cancellation"
type="text"
class="form-control s_website_form_input"
name="vehicle"
required="1">
<option value=""/>
<option t-foreach="vehicles"
t-as="each"
t-att-value="each.id"
required="1">
<t t-esc="each.vehicle_id.name"/>
</option>
</select>
</div>
</div>
</div>
</div>
<br/>
<br/>
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 125px" for="studio1">
<span class="s_website_form_label_content">
Reason
</span>
<span class="s_website_form_mark">*</span>
</label>
<div class="col-sm">
<input id="reason" type="text"
class="form-control s_website_form_input"
name="reason" required="1"
placeholder="Reason for cancellation"/>
</div>
</div>
<br/>
<div class="form-group col-12 s_website_form_submit"
data-name="cancel Button"
style="margin-bottom: 30px">
<div style="width: 200px;"
class="s_website_form_label"/>
<button type="submit" id="cancel"
class="btn btn-primary"
style="float: right;">Cancel
</button>
</div>
</form>
</div>
</section>
</div>
</t>
</template>
</odoo>

197
vehicle_subscription/views/online_vehicle_template.xml

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Booking page of subscription-->
<template id="vehicle_form" name="Vehicle Form">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty">
<section class="vehicles_website_form" id="book_my_vehicle">
<div class="container">
<div class="btn btn-primary redirect_back_with_data"
style="background:none; border:none;">
<span style="font-size:18px; color:#000; background-image:url('https://cdn-icons-png.flaticon.com/512/2985/2985162.png');width: 63px;
height: 63px;display: block;background-size: 60%;margin-top: 23px;background-position: center;background-repeat: no-repeat;"/>
</div>
<br/>
<h1 style="text-align: center;">Choose Vehicle</h1>
<br/>
<input type="hidden" name="csrf_token"
t-att-value="request.csrf_token()"/>
<div class="s_website_form_rows row s_col_no_bgcolor">
<div class="form-group col-12 s_website_form_field s_website_form_required"
data-type="char" data-name="Field">
<div class="row s_col_no_resize s_col_no_bgcolor"
style="display:none">
<label class="col-form-label col-sm-auto s_website_form_label"
style="width: 200px" for="studio1">
<span class="s_website_form_label_content">
Name
</span>
<span class="s_website_form_mark">*
</span>
</label>
<div class="col-sm">
<input class="form-control s_website_form_input"
name='customer' type="text"
t-att-value="customers"/>
</div>
</div>
<div class="row s_col_no_resize s_col_no_bgcolor">
<div class="vehicle_table_scroll" style=
"overflow-y: scroll; width: 100%;">
<div class="col-sm"
style="display: flex;justify-content: center;">
<div class="col-lg-8 col-md-6 subscription_book"
style="height: 40vh;over-flow-y: scroll;">
<table style="width:100%;border-collapse: separate;border-spacing: 0 20px;"
id="vehicle_booking_table">
<thead> <!-- Add the table header section -->
<tr style="background-color: #ececec45;-webkit-box-shadow: 0px 0px 6px #0000001f;">
<th style="padding: 10px 0px 10px 50px;">
Image
</th>
<th>Name</th>
<th>Price</th>
<th>Free km</th>
<th/>
</tr>
</thead>
<t t-foreach="vehicles or []"
t-as="vehicle">
<div class="veh_table_row">
<tr style="background-color: #ececec45;-webkit-box-shadow: 0px 0px 6px #0000001f;">
<td id="vehicle_img"
style="padding: 0px 20px;">
<img t-attf-src="/web/image/fleet.vehicle/{{ vehicle.id }}/image_128"
style="width: 100px; height: 100px; object-fit: contain;"
alt="Vehicle image"/>
</td>
<td id="vehicle_fuel"
t-att-data-vehicleid="vehicle.id"
t-att-value="vehicle.id">
<t t-esc="vehicle.name"/>
</td>
<td>
<span id="vehicle_price">
<t t-esc="(vehicle.duration * vehicle.subscription_price)+amount"/>
</span>
</td>
<td>
<span id="vehicle_free_km">
<t t-esc="vehicle.free_km"/>
</span>
</td>
<td>
<div class="form-group col-12 s_website_form_submit "
data-name="submit Button"
t-attf-id="'%s'% vehicle.id">
<button id="book_now_button"
t-if="not request.env.user.sudo().has_group('base.group_public')"
class="btn btn-primary book_now">
Book
Now
<input type="hidden"
t-att-value="vehicle.id"/> <!-- getting vehicle id in js . -->
</button>
<a href="/web/signup/user"
t-if="request.env.user.sudo().has_group('base.group_public')"
class="btn btn-primary book_now">
Book
Now
<input type="hidden"
t-att-value="vehicle.id"/> <!-- getting vehicle id in js . -->
</a>
</div>
</td>
</tr>
</div>
</t>
</table>
</div>
</div>
</div>
</div>
<br/>
<br/>
<div class="extra_charge_info"
style="width: 70%;margin: 0 auto;">
<div class="form-group col-12 s_website_form_submit"
style="margin-bottom: 30px;">
<tr style="height:10%">
<div class="col-sm"
style="display: flex;justify-content: center;align-items: center;">
<span style="font-weight: 650;margin-right: 20px;">
Extra KMs
</span>
<input id="extra_km"
type="text"
class="form-control s_website_form_input"
name="extra_kms"
style="width: 30%;"/>
</div>
</tr>
</div>
<div class="fuel_choice_and_invoice_type"
style="display: flex;align-items: center;justify-content: space-around;">
<table>
<input type="checkbox"
class="form-check-input"
id="checkbox_for_fuel"
style="display:none;"/>
<span style="font-weight: 700;">
Fuel choice
<td>
<div class="form-group col-12 s_website_form_submit"
id="with_fuel">
<div class="btn btn-primary">
With Fuel
</div>
</div>
</td>
<td>
<div class="form-group col-12 s_website_form_submit"
id="without_fuel">
<div class="btn btn-primary">
Without Fuel
</div>
</div>
</td>
</span>
</table>
<table>
<input type="checkbox"
class="form-check-input"
id="checkbox_for_invoice_type"
style="display:none;"/>
<span style="font-weight: 700;">
Invoice Type
<td>
<div class="form-group col-12 s_website_form_submit"
id="full_subscription">
<div class="btn btn-primary">
Full Payment
</div>
</div>
</td>
<td>
<div class="form-group col-12 s_website_form_submit"
id="monthly_subscription">
<div class="btn btn-primary">
Monthly Payment
</div>
</div>
</td>
</span>
</table>
</div>
</div>
</div>
</div>
<br/>
<br/>
</div>
</section>
</div>
</t>
</template>
</odoo>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save