[NEW] Addons creation - product_rental_web_bookings
This commit is contained in:
3
product_rental_web_bookings/__init__.py
Executable file
3
product_rental_web_bookings/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
59
product_rental_web_bookings/__manifest__.py
Executable file
59
product_rental_web_bookings/__manifest__.py
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
"name": "Product Rental Web Bookings",
|
||||||
|
"category": "Product",
|
||||||
|
"version": "14.0.1.0",
|
||||||
|
"summary": "Book products on several rental periods through website",
|
||||||
|
"author": "Elabore",
|
||||||
|
"website": "https://elabore.coop/",
|
||||||
|
"installable": True,
|
||||||
|
"application": True,
|
||||||
|
"auto_install": False,
|
||||||
|
"description": """
|
||||||
|
=======================
|
||||||
|
Product Rental Bookings
|
||||||
|
=======================
|
||||||
|
This module allows Odoo website visitors to rent products on several periods.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
Just install product_rental_bookings, all dependencies will be installed by default.
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
Bugs are tracked on `GitHub Issues
|
||||||
|
<https://github.com/elabore-coop/.../issues>`_. In case of trouble, please
|
||||||
|
check there if your issue has already been reported. If you spotted it first,
|
||||||
|
help us smashing it by providing a detailed and welcomed feedback.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Images
|
||||||
|
------
|
||||||
|
* Elabore: `Icon <https://elabore.coop/web/image/res.company/1/logo?unique=f3db262>`_.
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
* Stéphan Sainléger <https://github.com/stephansainleger>
|
||||||
|
|
||||||
|
Funders
|
||||||
|
-------
|
||||||
|
The development of this module has been financially supported by:
|
||||||
|
* Elabore (https://elabore.coop)
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
This module is maintained by ELABORE.
|
||||||
|
|
||||||
|
""",
|
||||||
|
"depends": ["base", "product_rental_bookings"],
|
||||||
|
"data": [
|
||||||
|
"views/product_template.xml",
|
||||||
|
"views/website_templates.xml",
|
||||||
|
],
|
||||||
|
"qweb": [],
|
||||||
|
}
|
4
product_rental_web_bookings/controllers/__init__.py
Executable file
4
product_rental_web_bookings/controllers/__init__.py
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import website_rental
|
||||||
|
from . import customer_portal
|
47
product_rental_web_bookings/controllers/customer_portal.py
Normal file
47
product_rental_web_bookings/controllers/customer_portal.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import http
|
||||||
|
from odoo.addons.portal.controllers.portal import CustomerPortal
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerPortal(CustomerPortal):
|
||||||
|
def _prepare_portal_layout_values(self):
|
||||||
|
values = super(CustomerPortal, self)._prepare_portal_layout_values()
|
||||||
|
rental_order_detail = request.env["rental.product.order"].sudo()
|
||||||
|
rental_order_count = rental_order_detail.search_count(
|
||||||
|
[("customer_name", "=", request.env.user.partner_id.id)]
|
||||||
|
)
|
||||||
|
values.update(
|
||||||
|
{
|
||||||
|
"rental_order_count": rental_order_count,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
@http.route("/order", type="http", auth="public", website=True)
|
||||||
|
def order(self, page=1, date_begin=None, date_end=None, sortby=None):
|
||||||
|
values = self._prepare_portal_layout_values()
|
||||||
|
order_id = (
|
||||||
|
request.env["rental.product.order"]
|
||||||
|
.sudo()
|
||||||
|
.search([("customer_name", "=", request.env.user.partner_id.id)])
|
||||||
|
)
|
||||||
|
searchbar_sortings = {
|
||||||
|
"date": {"label": ("Start Date"), "order": "from_date desc"},
|
||||||
|
"date1": {"label": ("End Date"), "order": "to_date desc"},
|
||||||
|
"stage": {"label": ("Stage"), "order": "state"},
|
||||||
|
}
|
||||||
|
# default sortby order
|
||||||
|
if not sortby:
|
||||||
|
sortby = "date"
|
||||||
|
sort_order = searchbar_sortings[sortby]["order"]
|
||||||
|
values.update(
|
||||||
|
{
|
||||||
|
"order_id": order_id,
|
||||||
|
"page_name": "rental_order",
|
||||||
|
"searchbar_sortings": searchbar_sortings,
|
||||||
|
"sortby": sortby,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return request.render("product_rental_bookings.rental_order", values)
|
566
product_rental_web_bookings/controllers/website_rental.py
Executable file
566
product_rental_web_bookings/controllers/website_rental.py
Executable file
@@ -0,0 +1,566 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from odoo import http
|
||||||
|
from odoo.addons.payment.controllers.portal import PaymentProcessing
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.osv import expression
|
||||||
|
|
||||||
|
|
||||||
|
class WebsiteRental(http.Controller):
|
||||||
|
@http.route("/search-product", type="http", auth="public", website=True)
|
||||||
|
def search_product(self, **post):
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.product_search", {"post_data": post}
|
||||||
|
)
|
||||||
|
|
||||||
|
def convert_float_to_hh_mm(self, session_id):
|
||||||
|
start_date = "{0:02.0f}:{1:02.0f}".format(
|
||||||
|
*divmod(session_id.start_time * 60, 60)
|
||||||
|
)
|
||||||
|
end_date = "{0:02.0f}:{1:02.0f}".format(*divmod(session_id.end_time * 60, 60))
|
||||||
|
start_date_fmt = (
|
||||||
|
start_date.split(":")[0] + ":" + start_date.split(":")[1] + ":" + "00"
|
||||||
|
)
|
||||||
|
end_date_fmt = (
|
||||||
|
end_date.split(":")[0] + ":" + end_date.split(":")[1] + ":" + "00"
|
||||||
|
)
|
||||||
|
return start_date_fmt, end_date_fmt
|
||||||
|
|
||||||
|
@http.route("/booking_form", type="http", auth="public", website=True)
|
||||||
|
def booking_form(self, **post):
|
||||||
|
product_list = []
|
||||||
|
if (
|
||||||
|
post
|
||||||
|
and post.get("based_on")
|
||||||
|
and post.get("class_type")
|
||||||
|
and post["location"]
|
||||||
|
):
|
||||||
|
if post.get("based_on") == "per_session":
|
||||||
|
rent_session_id = (
|
||||||
|
request.env["session.config"]
|
||||||
|
.sudo()
|
||||||
|
.browse(int(post.get("session_type")))
|
||||||
|
)
|
||||||
|
request.session["session_type"] = rent_session_id.name
|
||||||
|
request.session["session_type_id"] = rent_session_id.id
|
||||||
|
start_date, end_date = self.convert_float_to_hh_mm(rent_session_id)
|
||||||
|
date_from = datetime.strptime(
|
||||||
|
post["date_from"] + " " + start_date, "%d-%m-%Y %H:%M:%S"
|
||||||
|
)
|
||||||
|
date_to = datetime.strptime(
|
||||||
|
post["date_from"] + " " + end_date, "%d-%m-%Y %H:%M:%S"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
date_format = (
|
||||||
|
"%d-%m-%Y"
|
||||||
|
if post["date_from"] and post["date_to"]
|
||||||
|
else "%d-%m-%Y %H:%M"
|
||||||
|
)
|
||||||
|
date_from = datetime.strptime(
|
||||||
|
post["date_from"] if post["date_from"] else post["datetime_from"],
|
||||||
|
date_format,
|
||||||
|
)
|
||||||
|
date_to = datetime.strptime(
|
||||||
|
post["date_to"] + " 23:59:59"
|
||||||
|
if post["date_to"]
|
||||||
|
else post["datetime_to"] + ":00",
|
||||||
|
"%d-%m-%Y %H:%M:%S",
|
||||||
|
)
|
||||||
|
request.session["date_from"] = date_from
|
||||||
|
request.session["date_to"] = date_to
|
||||||
|
request.session["location"] = post["location"]
|
||||||
|
request.session["class_type"] = post["class_type"]
|
||||||
|
request.session["based_on"] = post["based_on"]
|
||||||
|
categ_ids = (
|
||||||
|
request.env["product.category"]
|
||||||
|
.sudo()
|
||||||
|
.search([("parent_id", "child_of", int(post["class_type"]))])
|
||||||
|
)
|
||||||
|
request.env.cr.execute(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
propro.id
|
||||||
|
FROM product_product propro
|
||||||
|
JOIN product_template protmp ON protmp.id = propro.product_tmpl_id
|
||||||
|
WHERE
|
||||||
|
protmp.categ_id IN %s AND
|
||||||
|
propro.is_rental='t' AND
|
||||||
|
propro.id IN (
|
||||||
|
Select product_id from stock_quant where location_id = %s);
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
tuple(categ_ids.ids),
|
||||||
|
post["location"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
product_data = request.env.cr.fetchall()
|
||||||
|
if product_data:
|
||||||
|
for products in product_data:
|
||||||
|
product = (
|
||||||
|
request.env["product.product"]
|
||||||
|
.sudo()
|
||||||
|
.search([("id", "in", products)])
|
||||||
|
)
|
||||||
|
available_products = (
|
||||||
|
request.env["stock.quant"]
|
||||||
|
.sudo()
|
||||||
|
.search(
|
||||||
|
[
|
||||||
|
("product_id", "=", product.id),
|
||||||
|
("quantity", ">", 0),
|
||||||
|
("location_id", "=", int(post.get("location"))),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if available_products.product_id and product.is_published:
|
||||||
|
product_list.append(available_products.product_id)
|
||||||
|
values = {
|
||||||
|
"product_id": product_list,
|
||||||
|
"location": post["location"],
|
||||||
|
"post_data": post,
|
||||||
|
}
|
||||||
|
return request.render("product_rental_bookings.product_search", values)
|
||||||
|
else:
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.product_search",
|
||||||
|
{"post_data": post, "error": True},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.product_search", {"post_data": post}
|
||||||
|
)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
'/booking_form/create_quotation/<model("product.product"):product_id>',
|
||||||
|
type="http",
|
||||||
|
auth="public",
|
||||||
|
website=True,
|
||||||
|
)
|
||||||
|
def create_qutation(self, product_id, **post):
|
||||||
|
product_order = {
|
||||||
|
"product_id": product_id,
|
||||||
|
"post_data": request.session["location"],
|
||||||
|
}
|
||||||
|
if request.session.get("based_on") in ["per_hour", "per_session"]:
|
||||||
|
delta = request.session.get("date_from") - request.session.get("date_to")
|
||||||
|
hours = abs(delta.total_seconds() / 3600.0)
|
||||||
|
product_order.update({"total_hours": hours})
|
||||||
|
return request.render("product_rental_bookings.rental_product", product_order)
|
||||||
|
|
||||||
|
@http.route("/product_cart_update", type="http", auth="public", website=True)
|
||||||
|
def product_cart_update(self, **post):
|
||||||
|
product_order_list = []
|
||||||
|
rental_order_obj = request.env["rental.product.order"]
|
||||||
|
order_id = rental_order_obj.search(
|
||||||
|
[
|
||||||
|
("customer_name", "=", request.env.user.partner_id.id),
|
||||||
|
("state", "=", "draft"),
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
if post and post["product_id"]:
|
||||||
|
product_id = (
|
||||||
|
request.env["product.product"]
|
||||||
|
.sudo()
|
||||||
|
.search([("id", "=", post["product_id"])])
|
||||||
|
)
|
||||||
|
enter_hour = post.get("enter_hour")
|
||||||
|
if not enter_hour:
|
||||||
|
total_day = (
|
||||||
|
request.session.get("date_to") - request.session.get("date_from")
|
||||||
|
).days
|
||||||
|
product_order_list.append(
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"product_id": product_id.id,
|
||||||
|
"price_based": "per_day",
|
||||||
|
"enter_days": total_day + 1,
|
||||||
|
"enter_hour": 0.0,
|
||||||
|
"qty_needed": int(post["qty_needed"]),
|
||||||
|
"price": product_id.rental_amount,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif enter_hour and request.session["based_on"] == "per_session":
|
||||||
|
product_order_list.append(
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"product_id": product_id.id,
|
||||||
|
"price_based": request.session.get("based_on"),
|
||||||
|
"enter_days": 0.0,
|
||||||
|
"enter_hour": float(post["enter_hour"]),
|
||||||
|
"qty_needed": int(post["qty_needed"]),
|
||||||
|
"price": product_id.rental_amount_per_session,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
product_order_list.append(
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"product_id": product_id.id,
|
||||||
|
"price_based": request.session.get("based_on"),
|
||||||
|
"enter_days": 0.0,
|
||||||
|
"enter_hour": float(post["enter_hour"]),
|
||||||
|
"qty_needed": int(post["qty_needed"]),
|
||||||
|
"price": product_id.rental_amount_per_hour,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if order_id:
|
||||||
|
order_lines_product = []
|
||||||
|
for order_line in order_id.product_order_lines_ids:
|
||||||
|
order_lines_product.append(order_line.product_id.id)
|
||||||
|
order_id.write(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"product_order_lines_ids": product_order_list,
|
||||||
|
"is_hours": True
|
||||||
|
if request.session.get("based_on")
|
||||||
|
in ["per_session", "per_hour"]
|
||||||
|
else order_id.is_hours,
|
||||||
|
"is_days": True
|
||||||
|
if request.session.get("based_on") == "per_day"
|
||||||
|
else order_id.is_days,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.rental_product_cart_update",
|
||||||
|
{
|
||||||
|
"product_order_id": order_id,
|
||||||
|
"location_id": request.session["location"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
from_date, to_date = rental_order_obj.sudo().start_end_date_global(
|
||||||
|
request.session.get("date_from"), request.session.get("date_to")
|
||||||
|
)
|
||||||
|
order_id = rental_order_obj.sudo().create(
|
||||||
|
{
|
||||||
|
"customer_name": request.env.user.partner_id.id,
|
||||||
|
"from_date": rental_order_obj.sudo().convert_TZ_UTC(from_date),
|
||||||
|
"to_date": rental_order_obj.sudo().convert_TZ_UTC(to_date),
|
||||||
|
"state": "draft",
|
||||||
|
"is_true": True,
|
||||||
|
"product_order_lines_ids": product_order_list,
|
||||||
|
"location_id": request.session["location"],
|
||||||
|
"is_hours": True
|
||||||
|
if request.session.get("based_on")
|
||||||
|
in ["per_session", "per_hour"]
|
||||||
|
else order_id.is_hours,
|
||||||
|
"is_days": True
|
||||||
|
if request.session.get("based_on") == "per_day"
|
||||||
|
else order_id.is_days,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
order_id.product_order_lines_ids._get_subtotal()
|
||||||
|
request.session["order_id"] = order_id.id
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.rental_product_cart_update",
|
||||||
|
{"product_order_id": order_id, "location_id": request.session["location"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
@http.route("/view_cart", type="http", auth="public", website=True)
|
||||||
|
def view_cart(self, **post):
|
||||||
|
rental_product_order_obj = request.env["rental.product.order"]
|
||||||
|
if post:
|
||||||
|
order_id = rental_product_order_obj.sudo().search(
|
||||||
|
[("id", "=", post["rental_order"])]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
order_id = rental_product_order_obj.sudo().search(
|
||||||
|
[("id", "=", request.session.get("order_id"))]
|
||||||
|
)
|
||||||
|
request.session["order_id"] = order_id.id
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.rental_product_cart_update",
|
||||||
|
{"product_order_id": order_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_rental_payment_values(self, order, **kwargs):
|
||||||
|
values = dict(
|
||||||
|
website_rental_order=order,
|
||||||
|
errors=[],
|
||||||
|
partner=order.customer_name.id,
|
||||||
|
order=order,
|
||||||
|
payment_action_id=request.env.ref("payment.action_payment_acquirer").id,
|
||||||
|
return_url="/rental/payment/validate",
|
||||||
|
bootstrap_formatting=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = expression.AND(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"&",
|
||||||
|
("state", "in", ["enabled", "test"]),
|
||||||
|
("company_id", "=", order.company_id.id),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"|",
|
||||||
|
("country_ids", "=", False),
|
||||||
|
("country_ids", "in", [order.customer_name.country_id.id]),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
acquirers = request.env["payment.acquirer"].search(domain)
|
||||||
|
values["acquirers"] = [
|
||||||
|
acq
|
||||||
|
for acq in acquirers
|
||||||
|
if (acq.payment_flow == "form" and acq.view_template_id)
|
||||||
|
or (acq.payment_flow == "s2s" and acq.registration_view_template_id)
|
||||||
|
]
|
||||||
|
values["tokens"] = request.env["payment.token"].search(
|
||||||
|
[
|
||||||
|
("partner_id", "=", order.customer_name.id),
|
||||||
|
("acquirer_id", "in", acquirers.ids),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
"/rental/payment/validate", type="http", auth="public", website=True, csrf=False
|
||||||
|
)
|
||||||
|
def payment_validate(self, transaction_id=None, rental_order_id=None, **post):
|
||||||
|
"""Method that should be called by the server when receiving an update
|
||||||
|
for a transaction. State at this point :
|
||||||
|
- UDPATE ME
|
||||||
|
"""
|
||||||
|
# vals = {}
|
||||||
|
outstanding_info = False
|
||||||
|
order = (
|
||||||
|
request.env["rental.product.order"]
|
||||||
|
.sudo()
|
||||||
|
.browse(request.session.get("order_id"))
|
||||||
|
)
|
||||||
|
if order:
|
||||||
|
if not order.invoice_ids:
|
||||||
|
order.confirm()
|
||||||
|
invoice_id = order.contract_ids[0].create_invoice()
|
||||||
|
order.contract_ids[0].create_invoice()
|
||||||
|
invoices = order.mapped("invoice_ids").filtered(
|
||||||
|
lambda inv: inv.state == "posted"
|
||||||
|
)
|
||||||
|
for inv in invoices:
|
||||||
|
if inv.invoice_has_outstanding:
|
||||||
|
outstanding_info = json.loads(
|
||||||
|
inv.invoice_outstanding_credits_debits_widget
|
||||||
|
)
|
||||||
|
transaction_id = request.env["payment.transaction"].browse(
|
||||||
|
request.session.get("__website_sale_last_tx_id")
|
||||||
|
)
|
||||||
|
if outstanding_info:
|
||||||
|
credit_aml_id = False
|
||||||
|
if "content" in outstanding_info:
|
||||||
|
for item in outstanding_info["content"]:
|
||||||
|
credit_aml_id = outstanding_info["content"][0]["id"]
|
||||||
|
if credit_aml_id and inv.state == "posted":
|
||||||
|
inv.js_assign_outstanding_line(credit_aml_id)
|
||||||
|
assert order.id == request.session.get("order_id")
|
||||||
|
return request.render(
|
||||||
|
"product_rental_bookings.rental_order_comfirmation", {"order": order}
|
||||||
|
)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
[
|
||||||
|
"/rental/payment/transaction/",
|
||||||
|
"/rental/payment/transaction/<int:so_id>",
|
||||||
|
"/rental/payment/transaction/<int:so_id>/<string:access_token>",
|
||||||
|
],
|
||||||
|
type="json",
|
||||||
|
auth="public",
|
||||||
|
website=True,
|
||||||
|
)
|
||||||
|
def payment_transaction(
|
||||||
|
self,
|
||||||
|
acquirer_id,
|
||||||
|
save_token=False,
|
||||||
|
so_id=None,
|
||||||
|
access_token=None,
|
||||||
|
token=None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
"""Json method that creates a payment.transaction, used to create a
|
||||||
|
transaction when the user clicks on 'pay now' button. After having
|
||||||
|
created the transaction, the event continues and the user is redirected
|
||||||
|
to the acquirer website.
|
||||||
|
|
||||||
|
:param int acquirer_id: id of a payment.acquirer record. If not set the
|
||||||
|
user is redirected to the checkout page
|
||||||
|
"""
|
||||||
|
# Ensure a payment acquirer is selected
|
||||||
|
if not acquirer_id:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
acquirer_id = int(acquirer_id)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
# Retrieve the sale order
|
||||||
|
if so_id:
|
||||||
|
env = request.env["rental.product.order"]
|
||||||
|
domain = [("id", "=", so_id)]
|
||||||
|
if access_token:
|
||||||
|
env = env.sudo()
|
||||||
|
domain.append(("access_token", "=", access_token))
|
||||||
|
order = env.search(domain, limit=1)
|
||||||
|
else:
|
||||||
|
order = (
|
||||||
|
request.env["rental.product.order"]
|
||||||
|
.sudo()
|
||||||
|
.browse(request.session.get("order_id"))
|
||||||
|
)
|
||||||
|
# Ensure there is something to proceed
|
||||||
|
assert order.customer_name.id != request.website.partner_id.id
|
||||||
|
# Create transaction
|
||||||
|
vals = {
|
||||||
|
"acquirer_id": acquirer_id,
|
||||||
|
"return_url": "/rental/payment/validate",
|
||||||
|
"currency_id": request.env.user.company_id.currency_id.id,
|
||||||
|
"amount": order.total_amount,
|
||||||
|
"partner_id": order.customer_name.id,
|
||||||
|
}
|
||||||
|
if save_token:
|
||||||
|
vals["type"] = "form_save"
|
||||||
|
if token:
|
||||||
|
vals["payment_token_id"] = int(token)
|
||||||
|
transaction = order._create_payment_transaction(vals)
|
||||||
|
last_tx_id = request.session.get("__website_sale_last_tx_id")
|
||||||
|
PaymentProcessing.add_payment_transaction(transaction)
|
||||||
|
request.session["__website_sale_last_tx_id"] = transaction.id
|
||||||
|
return transaction.render_rental_button(order)
|
||||||
|
|
||||||
|
@http.route("/checkout", type="http", auth="public", website=True, csrf=False)
|
||||||
|
def checkout(self, **post):
|
||||||
|
rental_order_id = (
|
||||||
|
request.env["rental.product.order"]
|
||||||
|
.sudo()
|
||||||
|
.search([("id", "=", request.session.get("order_id"))])
|
||||||
|
)
|
||||||
|
render_values = self._get_rental_payment_values(rental_order_id, **post)
|
||||||
|
if render_values["errors"]:
|
||||||
|
render_values.pop("acquirers", "")
|
||||||
|
render_values.pop("tokens", "")
|
||||||
|
return request.render("product_rental_bookings.checkout_process", render_values)
|
||||||
|
|
||||||
|
@http.route("/get_rate_details", type="json", auth="public", website=True)
|
||||||
|
def get_rate_details(self, units, product_id):
|
||||||
|
|
||||||
|
total_day = (
|
||||||
|
request.session.get("date_to").date()
|
||||||
|
- request.session.get("date_from").date()
|
||||||
|
).days
|
||||||
|
product_id = (
|
||||||
|
request.env["product.product"].sudo().search([("id", "=", product_id)])
|
||||||
|
)
|
||||||
|
if units == "per_day":
|
||||||
|
product_details = {
|
||||||
|
"rate": product_id.rental_amount,
|
||||||
|
"total_days": total_day + 1,
|
||||||
|
"from_date": request.session.get("date_from"),
|
||||||
|
"to_date": request.session.get("date_to"),
|
||||||
|
}
|
||||||
|
return product_details
|
||||||
|
else:
|
||||||
|
product_details = {
|
||||||
|
"rate": product_id.rental_amount_per_hour
|
||||||
|
if units == "per_hour"
|
||||||
|
else product_id.rental_amount_per_session,
|
||||||
|
"from_date": request.session.get("date_from"),
|
||||||
|
"to_date": request.session.get("date_to"),
|
||||||
|
}
|
||||||
|
return product_details
|
||||||
|
|
||||||
|
@http.route("/product_ordel_line/remove", type="json", auth="user")
|
||||||
|
def remove_line(self, order_line_id, **kwrgs):
|
||||||
|
line_id = request.env["product.order.line"].browse(int(order_line_id))
|
||||||
|
order_id = line_id.product_order_id
|
||||||
|
line_id.sudo().unlink()
|
||||||
|
if not any(order_id.product_order_lines_ids):
|
||||||
|
order_id.sudo().unlink()
|
||||||
|
return {"order_id": order_id}
|
||||||
|
|
||||||
|
@http.route("/check/quantity", type="json", auth="public")
|
||||||
|
def quantity_check(self, quantity, product_id):
|
||||||
|
Product_obj = request.env["product.product"]
|
||||||
|
order_line_obj = request.env["product.order.line"]
|
||||||
|
product = Product_obj.sudo().search([("id", "=", product_id)])
|
||||||
|
rental_order_obj = request.env["rental.product.order"]
|
||||||
|
from_date, to_date = rental_order_obj.sudo().start_end_date_global(
|
||||||
|
request.session.get("date_from"), request.session.get("date_to")
|
||||||
|
)
|
||||||
|
start_date = datetime.strptime(
|
||||||
|
rental_order_obj.sudo().convert_TZ_UTC(from_date)
|
||||||
|
if request.env.user.tz
|
||||||
|
else from_date,
|
||||||
|
"%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
end_date = datetime.strptime(
|
||||||
|
rental_order_obj.sudo().convert_TZ_UTC(to_date)
|
||||||
|
if request.env.user.tz
|
||||||
|
else to_date,
|
||||||
|
"%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
order_line_ids = order_line_obj.sudo().search(
|
||||||
|
[
|
||||||
|
("product_order_id.state", "=", "confirm"),
|
||||||
|
("product_id", "=", product_id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
total_in_order_qty = 0
|
||||||
|
for order_line in order_line_ids:
|
||||||
|
if (
|
||||||
|
(start_date <= order_line.product_order_id.from_date <= end_date)
|
||||||
|
or (start_date <= order_line.product_order_id.to_date <= end_date)
|
||||||
|
and order_line.product_order_id.picking_count == 1
|
||||||
|
):
|
||||||
|
total_in_order_qty += (
|
||||||
|
order_line.qty_needed
|
||||||
|
if order_line.product_order_id.picking_count == 1
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
(
|
||||||
|
order_line.product_order_id.from_date
|
||||||
|
<= start_date
|
||||||
|
<= order_line.product_order_id.to_date
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
order_line.product_order_id.from_date
|
||||||
|
<= end_date
|
||||||
|
<= order_line.product_order_id.to_date
|
||||||
|
)
|
||||||
|
and order_line.product_order_id.picking_count == 1
|
||||||
|
):
|
||||||
|
total_in_order_qty += (
|
||||||
|
order_line.qty_needed
|
||||||
|
if order_line.product_order_id.picking_count == 1
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
if product.sudo().qty_available and total_in_order_qty:
|
||||||
|
qty_available = product.sudo().qty_available - total_in_order_qty
|
||||||
|
else:
|
||||||
|
total_in_order_qty = order_line_ids.filtered(
|
||||||
|
lambda x: x.qty_needed if x.product_order_id.picking_count > 1 else 0
|
||||||
|
)
|
||||||
|
qty_available = product.sudo().qty_available + sum(
|
||||||
|
total_in_order_qty.mapped("qty_needed")
|
||||||
|
)
|
||||||
|
total_price_days = product.rental_amount
|
||||||
|
total_price_hour = product.rental_amount_per_hour
|
||||||
|
if not quantity or not qty_available >= quantity:
|
||||||
|
return False
|
||||||
|
return {
|
||||||
|
"product_price_days": total_price_days,
|
||||||
|
"product_price_hour": total_price_hour,
|
||||||
|
}
|
55
product_rental_web_bookings/static/src/css/index.css
Executable file
55
product_rental_web_bookings/static/src/css/index.css
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
.search-box{width:50%;background:#000000a3;border-radius:30px;margin:0 auto;margin-top:50px;margin-bottom:50px;padding:10px;font-size:16px;color:#fff;border: 3px solid;
|
||||||
|
border-color: black;}
|
||||||
|
.top-deals{padding:40px 0;background:#ededed}
|
||||||
|
.top-deals .col-xs-12{font-family:'Roboto',sans-serif;font-weight:700;font-size:40px;line-height:60px;color:#191919}
|
||||||
|
.top-deals .rline{width:100px;display:block;height:5px;background:#d42316;margin:0 auto;margin-top:20px}
|
||||||
|
.top-deals .box{background:#4ebab1;padding-bottom:150px;margin:0 auto;max-width:450px;margin-top:40px;-moz-box-shadow:1px 1px 5px #ccc;-webkit-box-shadow:1px 1px 5px #ccc;box-shadow:1px 1px 5px #ccc}
|
||||||
|
.top-deals .box .box-town-image{background:#4ebab1;width:50%;height:20px;}
|
||||||
|
.top-deals .box img{height:50%;width:50%;object-fit:cover}
|
||||||
|
.image{overflow:hidden;position:relative}
|
||||||
|
.heading-font{color:#fff;font-size:12px;line-height:18px;text-transform:uppercase;margin-top:65px;}
|
||||||
|
.heading-font{letter-spacing:0 !important}.text-transform{text-transform:uppercase}
|
||||||
|
.heading-font{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul{padding-left:20px}ul li{margin-bottom:8px}ol{padding-left:20px}ol li{margin-bottom:8px}ul.header-menu ul{margin-top:0}ul ol,ol ol,ol ul,ul ul{margin-top:8px}img{max-width:100%;height:auto}a{color:#6c98e1}a:hover{color:#6c98e1}a:focus{color:#6c98e1}a:active{color:#6c98e1}}
|
||||||
|
.image{overflow:hidden;position:relative}.rmv_txt_drctn:hover .image:before{opacity:1}
|
||||||
|
.special-offers .title{position:relative;padding:0 130px;margin-bottom:16px;text-align:center;font-weight:700;font-size:36px;line-height:normal;color:#fff}
|
||||||
|
.listing-car-items-units{margin:0 -15px}.listing-car-items .listing-car-item{padding:10px 15px}
|
||||||
|
.listing-car-items .listing-car-item .listing-car-item-inner{max-width:350px;width:auto;margin-bottom:30px;}
|
||||||
|
.listing-car-items .listing-car-item .car-title{margin-top:40px;color:#fff;overflow:hidden;text-transform:uppercase;font-size:14px;line-height:18px;font-weight:700;text-decoration:none;text-align:left}
|
||||||
|
.listing-car-items .listing-car-item .car-title .colored{color:#cc6119}
|
||||||
|
.listing-car-items .listing-car-item .price{position:relative;float:right;padding:6px 15px 5px 18px;margin-left:23px;background-color:#6c98e1;text-align:right}
|
||||||
|
.listing-car-items .listing-car-item .price .regular-price,.listing-car-items .listing-car-item .price .sale-price,
|
||||||
|
.listing-car-items .listing-car-item .price .normal-price{position:relative;z-index:6}.listing-car-items .listing-car-item .price .regular-price{font-size:13px;line-height:normal;text-decoration:line-through}
|
||||||
|
.listing-car-items .listing-car-item .price .normal-price,.listing-car-items .listing-car-item .price .sale-price{color:#fff;font-size:16px;line-height:20px;font-weight:700}.car-listing-row
|
||||||
|
.listing-car-item-meta .price .normal-price,.car-listing-row
|
||||||
|
.listing-car-item-meta .price .sale-price{color:#fff;font-size:16px;line-height:20px;font-weight:700}
|
||||||
|
.listing-car-items .listing-car-item .price .normal-price{line-height:36px}.listing-car-items .listing-car-item .price:before{position:absolute;content:'';display:block;top:0;left:-14px;height:100%;width:32px;background-color:#6c98e1;-webkit-transform:skew(-18deg,0deg);transform:skew(-18deg,0deg);z-index:5}
|
||||||
|
.listing-car-items .listing-car-item .listing-car-item-meta{border-radius: 15px;padding:3px 0px 19px 1px;width: 900px;background-color:#373c3f;text-align:left;vertical-align: middle;}
|
||||||
|
.title{margin-bottom:10px;font-weight:700;text-transform:uppercase;font-size:14px;line-height:10px}
|
||||||
|
.title{text-transform:uppercase;border-bottom:1px solid #ddd}
|
||||||
|
.title{font-size:16px;font-weight:400;color:#232628}
|
||||||
|
.title{position:relative;min-height:42px;text-transform:uppercase}
|
||||||
|
.car-meta-top{min-height:9px;padding-bottom:9px;border-bottom:1px solid #4e5254}.listing-car-items .listing-car-item
|
||||||
|
.listing-car-item-meta .car-meta-bottom ul{list-style:none;padding:0;margin:0;border-radius:37px}
|
||||||
|
.listing-car-items .listing-car-item .listing-car-item-meta .car-meta-bottom ul li{display:inline-block;margin-top:18px;margin-right:16px;margin-bottom:0;color:#c9c9c9;vertical-align:middle;line-height:13px}
|
||||||
|
.dp-in{display:inline-block;vertical-align:top;max-width:100%}.car-listing-tabs-unit .stm-listing-tabs{margin-top:20px}.car-listing-tabs-unit .car-listing-top-part{position:relative;}.car-listing-tabs-unit .car-listing-top-part:before{position:absolute;content:'';display:block;top:0;bottom:0;left:-2000px;right:-2000px;z-index:-1}
|
||||||
|
.stm-base-background-color{background-color:#cc6119 !important}.stm-base-color{color:#cc6119 !important}
|
||||||
|
.stm-image-links .stm-image-link .inner img{filter:grayscale(0%) !important;-webkit-filter:grayscale(0%) !important}img{-webkit-transform:translateZ(0)}.testimonials-carousel .owl-controls{display:none}.testimonial-unit
|
||||||
|
.image{margin-right:20px !important}.wpb_column .
|
||||||
|
.widget_car_location #stm-dealer-gmap{width:100%;height:253px}@media(max-width:440px){}
|
||||||
|
.vc_custom_1448532088457{margin-bottom: 0px !important;padding-top: 51px !important;padding-bottom: 40px !important;background-position: 0 0 !important;background-repeat: repeat !important;}
|
||||||
|
.car-listing-tabs-unit .car-listing-top-part:before {background-color: #232628}
|
||||||
|
.font-color{font-size:16px;font-weight:400;color:#000000}
|
||||||
|
.select{max-width:100px;}
|
||||||
|
.css_quantity {
|
||||||
|
max-width: 125px;
|
||||||
|
}.css_quantity input[name="add_qty"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.kilometer{max-width:70px;}
|
||||||
|
.search_product{margin-top:8px; padding: 0 7px;}
|
||||||
|
.scroll_down {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
228
product_rental_web_bookings/static/src/js/main.js
Executable file
228
product_rental_web_bookings/static/src/js/main.js
Executable file
@@ -0,0 +1,228 @@
|
|||||||
|
odoo.define('product_rental_bookings.main', function (require) {
|
||||||
|
"use strict";
|
||||||
|
var ajax = require('web.ajax');
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
$('.total_days').hide();
|
||||||
|
var error = $('#error').val();
|
||||||
|
if (error == "True") {
|
||||||
|
$("#myModal").modal();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#date_from').datepicker({
|
||||||
|
minDate: new Date(),
|
||||||
|
dateFormat: "dd-mm-yy",
|
||||||
|
yearRange: new Date().getFullYear().toString() + ':' + new Date().getFullYear().toString(),
|
||||||
|
onClose: function (selectedDate) { $("#date_to").datepicker("option", "minDate", selectedDate); }
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#date_to').datepicker({
|
||||||
|
dateFormat: "dd-mm-yy",
|
||||||
|
yearRange: new Date().getFullYear().toString() + ':' + new Date().getFullYear().toString(),
|
||||||
|
onClose: function (selectedDate) { $("#date_from").datepicker("option", "maxDate", selectedDate); }
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#datetime_from').datetimepicker({
|
||||||
|
minDate: new Date(),
|
||||||
|
format: 'd-m-Y H:i',
|
||||||
|
onClose: function (selectedDate) {
|
||||||
|
$("#datetime_to").datetimepicker("option", "minDateTime", selectedDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#datetime_to').datetimepicker({
|
||||||
|
minDate: new Date(),
|
||||||
|
format: 'd-m-Y H:i',
|
||||||
|
onClose: function (selectedDate) {
|
||||||
|
$("#datetime_from").datetimepicker("option", "maxDateTime", selectedDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#datetime_from').on('change', function () {
|
||||||
|
var date_from = $('#datetime_from').val()
|
||||||
|
var date_to = $('#datetime_to').val()
|
||||||
|
if (date_from && date_to && date_from > date_to) {
|
||||||
|
alert('From Datetime is after To Datetime')
|
||||||
|
$('#datetime_from').val('')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#datetime_to').on('change', function () {
|
||||||
|
var date_from = $('#datetime_from').val()
|
||||||
|
var date_to = $('#datetime_to').val()
|
||||||
|
if (date_from && date_to && date_from > date_to) {
|
||||||
|
alert('To Datetime is before From Datetime')
|
||||||
|
$('#datetime_to').val('')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.rate_option').click(function () {
|
||||||
|
var select_value = $(this).val();
|
||||||
|
var qty = $(this).parent().parent().find('.qty').val()
|
||||||
|
var hour = $(this).parent().parent().find('.enter_hour').val()
|
||||||
|
var day = $(this).parent().parent().find('.total_day').val()
|
||||||
|
ajax.jsonRpc('/get_rate_details', 'call', {
|
||||||
|
'product_id': $(document).find('.product_id').val(),
|
||||||
|
'units': $(this).val(),
|
||||||
|
}).then(function (product_details) {
|
||||||
|
var rate = parseInt(product_details['rate'])
|
||||||
|
var total_day = parseInt(product_details['total_days'])
|
||||||
|
var from_date = (product_details['from_date'])
|
||||||
|
var to_date = (product_details['to_date'])
|
||||||
|
if (select_value == 'per_day') {
|
||||||
|
var html = '';
|
||||||
|
var day = '';
|
||||||
|
$('.total_hour').hide();
|
||||||
|
$('.enter_hour').hide();
|
||||||
|
$('.total_hours').hide();
|
||||||
|
$('.total_day').show();
|
||||||
|
$('.total_days').show();
|
||||||
|
html += "<td>" + rate + "</td>";
|
||||||
|
day += "<td>" + total_day + "</td>"
|
||||||
|
from_date += "<td>" + from_date + "</td>"
|
||||||
|
to_date += "<td>" + to_date + "</td>"
|
||||||
|
$('.rate_value').html(html);
|
||||||
|
$('.total_day').html(day)
|
||||||
|
$('.rate_value').addClass('rate_cls');
|
||||||
|
$('.total_day').addClass('day_cls');
|
||||||
|
$('.date_from').html(from_date)
|
||||||
|
$('.date_to').html(to_date)
|
||||||
|
calculate_total(rate * total_day);
|
||||||
|
$($('.calculate_total')).show()
|
||||||
|
$("input[name='qty_needed']").val(1)
|
||||||
|
}
|
||||||
|
else if (select_value == 'per_hour') {
|
||||||
|
$('.total_hour').show();
|
||||||
|
$('.enter_hour').show();
|
||||||
|
$('.total_hours').show();
|
||||||
|
$('.total_day').hide();
|
||||||
|
$('.total_days').hide();
|
||||||
|
calculate_total(rate * hour);
|
||||||
|
$($('.calculate_total')).show()
|
||||||
|
$("input[name='qty_needed']").val(1)
|
||||||
|
}
|
||||||
|
else if (select_value == 'per_session') {
|
||||||
|
$('.total_hour').show();
|
||||||
|
$('.enter_hour').show();
|
||||||
|
$('.total_hours').show();
|
||||||
|
$('.total_day').hide();
|
||||||
|
$('.total_days').hide();
|
||||||
|
calculate_total(rate);
|
||||||
|
$($('.calculate_total')).show()
|
||||||
|
$("input[name='qty_needed']").val(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$(".rate_option").trigger("click");
|
||||||
|
|
||||||
|
$('.quantity').on('change', function () {
|
||||||
|
var numberPattern = /\d+/g;
|
||||||
|
var rate_value = parseInt($('.rate_cls').html().match(numberPattern));
|
||||||
|
var hour = parseInt($('.enter_hour').val());
|
||||||
|
var total_day = parseInt($('.day_cls').html().match(numberPattern));
|
||||||
|
if ($(this).parent().parent().parent().find('.rate_option').val() == 'per_hour') {
|
||||||
|
calculate_total(rate_value * $(this).val() * hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// const total_amt = $('.calculate_total').html();
|
||||||
|
$("input[name='qty_needed']").on('change', function (e) {
|
||||||
|
var quantity = e.currentTarget.value;
|
||||||
|
var product_id = $("input[name='product_id']").val();
|
||||||
|
ajax.jsonRpc('/check/quantity', 'call', {
|
||||||
|
'product_id': parseInt(product_id),
|
||||||
|
'quantity': parseInt(quantity)
|
||||||
|
}).then(function (response) {
|
||||||
|
if (response == false) {
|
||||||
|
alert("Quantity Not Available.")
|
||||||
|
e.currentTarget.value = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($('.rate_option').val() == 'per_day') {
|
||||||
|
var total_day = ($('.total_day').html().replace('<td>', '')).replace('</td>', '');
|
||||||
|
$('.calculate_total').html(total_day * response.product_price_days * parseInt(quantity))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var hour = $("input[name='enter_hour']").val()
|
||||||
|
$('.calculate_total').html(parseInt(hour) * response.product_price_hour * parseInt(quantity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.currentTarget.value == 0) {
|
||||||
|
$(".js_check_product").attr("disabled", true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(".js_check_product").attr("disabled", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.js_delete_product').click(function () {
|
||||||
|
var ele = $(this)
|
||||||
|
var id = $(this).attr("id");
|
||||||
|
ajax.jsonRpc('/product_ordel_line/remove', 'call', {
|
||||||
|
'order_line_id': id,
|
||||||
|
}).then(function () {
|
||||||
|
ele.closest('tr').remove()
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#based_on').click(function () {
|
||||||
|
var select_value = $(this).val();
|
||||||
|
if (select_value == 'per_hour') {
|
||||||
|
$('#session_selection').hide();
|
||||||
|
$('#for_hour_selection').show();
|
||||||
|
$('#for_day_selection').hide();
|
||||||
|
$("#datetime_from").prop("required", true);
|
||||||
|
$("#datetime_to").prop("required", true);
|
||||||
|
$("#date_from").prop("required", false);
|
||||||
|
$("#date_to").prop("required", false);
|
||||||
|
$("#session_type").prop("required", false);
|
||||||
|
$("#date_from").val('');
|
||||||
|
$("#date_to").val('');
|
||||||
|
}
|
||||||
|
else if (select_value == 'per_day') {
|
||||||
|
$('#session_selection').hide();
|
||||||
|
$('#for_hour_selection').hide();
|
||||||
|
$('#for_day_selection').show();
|
||||||
|
$("#to_date").show();
|
||||||
|
$("#date_from").prop("required", true);
|
||||||
|
$("#date_to").prop("required", true);
|
||||||
|
$("#datetime_from").prop("required", false);
|
||||||
|
$("#datetime_to").prop("required", false);
|
||||||
|
$("#session_type").prop("required", false);
|
||||||
|
$("#session_type").val('');
|
||||||
|
$("#datetime_from").val('');
|
||||||
|
$("#datetime_to").val('');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#session_selection').show();
|
||||||
|
$('#for_hour_selection').hide();
|
||||||
|
$('#for_day_selection').show();
|
||||||
|
$("#date_from").prop("required", true);
|
||||||
|
$("#to_date").hide();
|
||||||
|
$("#date_to").prop("required", false);
|
||||||
|
$("#datetime_from").prop("required", false);
|
||||||
|
$("#datetime_to").prop("required", false);
|
||||||
|
$("#datetime_from").val('');
|
||||||
|
$("#datetime_to").val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function calculate_total(calculated_value) {
|
||||||
|
$('.calculate_total').html("<td>" + calculated_value + "</td>");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -0,0 +1,140 @@
|
|||||||
|
//## Website Sale frontent design
|
||||||
|
//## ----------------------------
|
||||||
|
|
||||||
|
// Theming variables
|
||||||
|
$o-rental-wizard-thickness: 0.125rem;
|
||||||
|
$o-rental-wizard-dot-size: 0.625rem;
|
||||||
|
$o-rental-wizard-dot-active-glow: 0.25rem;
|
||||||
|
|
||||||
|
$o-rental-wizard-color-inner: white;
|
||||||
|
$o-rental-wizard-color-default: gray('200');
|
||||||
|
|
||||||
|
$o-rental-wizard-dot-active: theme-color('primary');
|
||||||
|
$o-rental-wizard-dot-completed: theme-color('success');
|
||||||
|
|
||||||
|
$o-rental-wizard-label-default: $text-muted;
|
||||||
|
$o-rental-wizard-label-active: $body-color;
|
||||||
|
$o-rental-wizard-label-completed: $success;
|
||||||
|
|
||||||
|
.progress-rental-wizard {
|
||||||
|
// Scoped variables
|
||||||
|
$tmp-dot-radius: ($o-rental-wizard-dot-size + $o-rental-wizard-thickness)*0.5;
|
||||||
|
$tmp-check-size: max($font-size-base, $o-rental-wizard-dot-size + $o-rental-wizard-thickness + $o-rental-wizard-dot-active-glow*2);
|
||||||
|
$tmp-check-pos: $o-rental-wizard-dot-size*0.5 - $tmp-check-size*0.5;
|
||||||
|
|
||||||
|
margin-top: $grid-gutter-width*0.5;
|
||||||
|
padding: 0 $grid-gutter-width*0.5;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-step {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin-top: $tmp-dot-radius + $o-rental-wizard-thickness*3.5;
|
||||||
|
float: left;
|
||||||
|
width: percentage(1/3);
|
||||||
|
|
||||||
|
.o_wizard_has_extra_step + & {
|
||||||
|
width: percentage(1/4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
&.disabled, &.complete {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.progress-rental-wizard-dot {
|
||||||
|
width: $o-rental-wizard-dot-size;
|
||||||
|
height: $o-rental-wizard-dot-size;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: $o-rental-wizard-color-inner;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 0 $o-rental-wizard-thickness $o-rental-wizard-color-default;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
@include o-position-absolute($left: 50%);
|
||||||
|
margin: (-$tmp-dot-radius) 0 0 (-$o-rental-wizard-dot-size*0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-steplabel {
|
||||||
|
color: $o-rental-wizard-label-default;
|
||||||
|
margin: 5px 0 5px 5px;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: block;
|
||||||
|
margin: (0.625rem + $tmp-dot-radius) 0 20px 0;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
margin-left: -15px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-bar {
|
||||||
|
height: $o-rental-wizard-thickness;
|
||||||
|
background-color: $o-rental-wizard-color-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.progress-rental-wizard-dot {
|
||||||
|
animation: fadeIn 1s ease 0s 1 normal none running;
|
||||||
|
background: $o-rental-wizard-dot-active;
|
||||||
|
box-shadow: 0 0 0 ($o-rental-wizard-dot-active-glow - 0.0625rem) $o-rental-wizard-color-inner,
|
||||||
|
0 0 0 $o-rental-wizard-dot-active-glow rgba($o-rental-wizard-dot-active, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-steplabel {
|
||||||
|
color: $o-rental-wizard-label-active;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.complete {
|
||||||
|
.progress-rental-wizard-dot {
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
@include o-position-absolute($tmp-check-pos, $left: $tmp-check-pos);
|
||||||
|
width: $tmp-check-size;
|
||||||
|
height: $tmp-check-size;
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
background: $o-rental-wizard-color-inner;
|
||||||
|
color: $o-rental-wizard-dot-completed;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: $tmp-check-size;
|
||||||
|
font-family: FontAwesome;
|
||||||
|
|
||||||
|
content: "\f058";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-steplabel {
|
||||||
|
color: $o-rental-wizard-label-completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.disabled) {
|
||||||
|
.progress-rental-wizard-dot:after {
|
||||||
|
color: $o-rental-wizard-label-completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-rental-wizard-steplabel {
|
||||||
|
color: $o-rental-wizard-label-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
800
product_rental_web_bookings/views/product_template.xml
Normal file
800
product_rental_web_bookings/views/product_template.xml
Normal file
@@ -0,0 +1,800 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<template id="wishlist_frontend" inherit_id="web.assets_frontend">
|
||||||
|
<xpath expr="." position="inside">
|
||||||
|
<script type="text/javascript" src="/product_rental_bookings/static/src/js/main.js" />
|
||||||
|
<link rel="stylesheet" href="/product_rental_bookings/static/src/css/index.css" />
|
||||||
|
<link rel="profile" href="http://gmpg.org/xfn/11" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
|
||||||
|
<script src="http://code.jquery.com/jquery-1.9.1.js" />
|
||||||
|
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js" />
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<template id="header_company_menu_11" inherit_id="website.navbar_nav" name="Search Product">
|
||||||
|
<xpath expr="//ul[@id='top_menu']/t" position="before">
|
||||||
|
<li class="nav-item search_product">
|
||||||
|
<a href="/search-product">Book Product</a>
|
||||||
|
</li>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="product_search" name="Product Search">
|
||||||
|
<t t-set="no_footer">1</t>
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<t t-if="error">
|
||||||
|
<input type="hidden" t-att-value="error" id="error" />
|
||||||
|
<div class="modal fade" id="myModal" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"></button>
|
||||||
|
<h4 class="modal-title"></h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Rent Product not available right Now</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<div id="wrap" class="oe_structure oe_empty">
|
||||||
|
<div id="myCarousel1513682046664" class="carousel slide s_banner" data-interval="1000000" style="height: 450px;">
|
||||||
|
<ol class="carousel-indicators"></ol>
|
||||||
|
<div class="carousel-inner">
|
||||||
|
<div class="item bg-alpha oe_img_bg">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<form class="form" name="bookingform" id="bookingform" action="/booking_form" method="post">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<input type="hidden" name="lang" value="en" />
|
||||||
|
<center>
|
||||||
|
<div class="search-box">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>Based On</lable>
|
||||||
|
<select class="form-control" id='based_on' name='based_on' required="1">
|
||||||
|
<t t-if="post_data.get('based_on') == 'per_day'">
|
||||||
|
<option t-att-value="per_day" t-att-selected="per_day">
|
||||||
|
Days
|
||||||
|
</option>
|
||||||
|
<option value="per_hour">Hours</option>
|
||||||
|
<option value="per_session">Session</option>
|
||||||
|
</t>
|
||||||
|
<t t-if="post_data.get('based_on') == 'per_hour'">
|
||||||
|
<option t-att-value="per_hour" t-att-selected="per_hour">
|
||||||
|
Hours
|
||||||
|
</option>
|
||||||
|
<option value="per_day">Days</option>
|
||||||
|
<option value="per_session">Session</option>
|
||||||
|
</t>
|
||||||
|
<t t-if="post_data.get('based_on') == 'per_session'">
|
||||||
|
<option t-att-value="per_session" t-att-selected="per_session">
|
||||||
|
Session
|
||||||
|
</option>
|
||||||
|
<option value="per_day">Days</option>
|
||||||
|
<option value="per_hour">Hours</option>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-if="not post_data.get('based_on') or post_data.get('based_on') not in ['per_day', 'per_hour', 'per_session']">
|
||||||
|
<option t-if="request.env['ir.config_parameter'].sudo().get_param('enabled_day_rent')" value="per_day">Days</option>
|
||||||
|
<option t-if="request.env['ir.config_parameter'].sudo().get_param('enabled_hour_rent')" value="per_hour">Hours</option>
|
||||||
|
<option t-if="request.env['ir.config_parameter'].sudo().get_param('enabled_session_rent')" value="per_session">Session</option>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12" id="session_selection" style="display:None;">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>Session</lable>
|
||||||
|
<t t-set="session_type" t-value="request.env['session.config'].sudo().search([])" />
|
||||||
|
<select class="form-control" id='session_type' name='session_type' required="1">
|
||||||
|
<option value="">Select Session
|
||||||
|
</option>
|
||||||
|
<t t-foreach="session_type" t-as="session_type">
|
||||||
|
<t t-if="post_data.get('session_type')">
|
||||||
|
<option t-att-value="session_type.id" t-att-selected="session_type.id == int(post_data.get('session_type'))">
|
||||||
|
<t t-esc="session_type.name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<option t-att-value='session_type.id'>
|
||||||
|
<t t-esc="session_type.name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12" id="for_day_selection">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>From Date</lable>
|
||||||
|
<input id="date_from" class="form-control" placeholder="dd-mm-yyyy" required="1" t-att-value="post_data.get('date_from')" name="date_from" type="text" size="20" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8" id="to_date">
|
||||||
|
<lable>To Date</lable>
|
||||||
|
<input id="date_to" class="form-control" placeholder="dd-mm-yyyy" required="1" t-att-value="post_data.get('date_to')" name="date_to" type="text" size="20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12" id="for_hour_selection" style="display:None;">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>From DateTime</lable>
|
||||||
|
<input id="datetime_from" class="form-control" placeholder="dd-mm-yyyy HH:MM" t-att-value="post_data.get('datetime_from')" name="datetime_from" type="text" size="20" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>To DateTime</lable>
|
||||||
|
<input id="datetime_to" class="form-control" placeholder="dd-mm-yyyy HH:MM" t-att-value="post_data.get('datetime_to')" name="datetime_to" type="text" size="20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>Type</lable>
|
||||||
|
<t t-set="class_type" t-value="request.env['product.category'].sudo().search([])" />
|
||||||
|
<select class="form-control" id='class_type' name='class_type' required="1">
|
||||||
|
<option value="">Select Product Categoty
|
||||||
|
</option>
|
||||||
|
<t t-foreach="class_type" t-as="class_type">
|
||||||
|
<t t-if="post_data.get('class_type')">
|
||||||
|
<option t-att-value="class_type.id" t-att-selected="class_type.id == int(post_data.get('class_type'))">
|
||||||
|
<t t-esc="class_type.name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<option t-att-value='class_type.id'>
|
||||||
|
<t t-esc="class_type.name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<lable>Location</lable>
|
||||||
|
<t t-set="location" t-value="request.env['stock.location'].sudo().search([('usage', '=', 'internal')])" />
|
||||||
|
<select name="location" id="location" class="form-control full-width" required="1">
|
||||||
|
<t t-foreach="location" t-as="location_id">
|
||||||
|
<t t-if="post_data.get('location')">
|
||||||
|
<option t-att-value="location_id.id" t-att-selected="location_id.id == int(post_data.get('location'))">
|
||||||
|
<t t-esc="location_id.complete_name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<option t-att-value='location_id.id'>
|
||||||
|
<t t-esc="location_id.complete_name" />
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3"></div>
|
||||||
|
<div class="col-md-3"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12"></div>
|
||||||
|
</div>
|
||||||
|
<center>
|
||||||
|
<div class="" style="margin-top:10px;">
|
||||||
|
<button class="btn btn-lg" id="search_button" name="search_button" style="background-color: #008CBA;border-color: #008CBA;">
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t t-if="product_id">
|
||||||
|
<div class="">
|
||||||
|
<div id="result_div" class="listing-car-items col vc_custom_1448532088457" align="center">
|
||||||
|
<div class="container">
|
||||||
|
<div class="card-deck row">
|
||||||
|
<t t-foreach="product_id" t-as="product_id">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<form class="myre card" name="qutation_create" t-att-id="product_id.id" style="max-height: 281px;margin-bottom: 10px;" t-att-action="'/booking_form/create_quotation/%s' % slug(product_id)" method="post">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<input type="hidden" t-att-value="product_id.id" name="product_id_text" class="product_id_text" />
|
||||||
|
<button class="btn book_now" type="submit" name="book_now" style="background-color: white;">
|
||||||
|
<div class="card-body">
|
||||||
|
<span t-field="product_id.image_1920" class="card-img-top" t-options="{'widget': 'image','style':'width: 100px;height: 100px;'}" />
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">
|
||||||
|
<t t-esc="product_id.name" />
|
||||||
|
</h5>
|
||||||
|
<span class="card-text">
|
||||||
|
<span>
|
||||||
|
<t t-if="product_id.rental_amount and request.session['based_on'] == 'per_day'">
|
||||||
|
<b>
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount" />
|
||||||
|
/Day
|
||||||
|
</b>
|
||||||
|
</t>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<p class="card-text">
|
||||||
|
<span>
|
||||||
|
<t t-if="product_id.rental_amount_per_hour and request.session['based_on'] == 'per_hour'">
|
||||||
|
<b>
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount_per_hour" />
|
||||||
|
/Hour
|
||||||
|
</b>
|
||||||
|
</t>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="card-text">
|
||||||
|
<span>
|
||||||
|
<t t-if="product_id.rental_amount_per_hour and request.session['based_on'] == 'per_session'">
|
||||||
|
<b>
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount_per_session" />
|
||||||
|
/Session
|
||||||
|
</b>
|
||||||
|
</t>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="rental_product" name="Rental Product">
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4"></div>
|
||||||
|
<div class="col-sm-3 mb8"></div>
|
||||||
|
<div class="col-sm-3"></div>
|
||||||
|
<div class="col-sm-2 text-right"></div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" t-att-value="product_id.id" class="product_id" />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-2"></div>
|
||||||
|
<div class="col-sm-3 col-lg-4 col-lg-offset-1" id="product_details">
|
||||||
|
<form action="/product_cart_update" class="js_add_cart_variants" method="POST" onkeydown="return event.key != 'Enter';">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<input type="hidden" t-att-value="product_id.id" class="product_id_cart" name="product_id_cart" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<t t-placeholder="select">
|
||||||
|
<input type="hidden" class="product_id" name="product_id" t-att-value="product_id.id" />
|
||||||
|
</t>
|
||||||
|
<table style="width:150%;">
|
||||||
|
<tbody class="font-color">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span itemprop="image" t-field="product_id.image_1920" t-options="{'widget': 'image', 'class': 'product_detail_img', 'alt-field': 'name', 'zoom': 'image'}" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<h3 itemprop="name" t-field="product_id.name">Product Name</h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="height:70px;">
|
||||||
|
<td>Date From</td>
|
||||||
|
<td class='date_from'>
|
||||||
|
<t t-esc="request.session['date_from']" />
|
||||||
|
</td>
|
||||||
|
<td>Date To</td>
|
||||||
|
<td class='date_to'>
|
||||||
|
<t t-esc="request.session['date_to']" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="height:70px;">
|
||||||
|
<td>Based On</td>
|
||||||
|
<td class="price_based">
|
||||||
|
<select class="form-control rate_option" readonly='1' style="pointer-events: none;">
|
||||||
|
<t t-if="request.session['based_on'] == 'per_day'">
|
||||||
|
<option value="per_day">
|
||||||
|
Days
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
<t t-if="request.session['based_on'] == 'per_hour'">
|
||||||
|
<option value="per_hour">
|
||||||
|
Hours
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
<t t-if="request.session['based_on'] == 'per_session'">
|
||||||
|
<option value="per_session">
|
||||||
|
Session
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="total_hours">Total Hours</td>
|
||||||
|
<td class="total_hours">
|
||||||
|
<input class='form-control kilometer enter_hour' id="entered_hour" type="text" readonly="1" t-att-value="total_hours" name="enter_hour" />
|
||||||
|
</td>
|
||||||
|
<td class="total_days">Total Days</td>
|
||||||
|
<td class="total_day" name="total_day"></td>
|
||||||
|
</tr>
|
||||||
|
<tr style="height:70px;" class="total_days">
|
||||||
|
<td class="total_days">Rate Per Day</td>
|
||||||
|
<td class="total_days" style="height:30px;">
|
||||||
|
<lable class="cls_per_day_amount">
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount" />
|
||||||
|
</lable>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="height:70px;" class="total_hour">
|
||||||
|
<t t-if="request.session['based_on'] == 'per_session'">
|
||||||
|
<td class="enter_hour">Rate Per Session</td>
|
||||||
|
<td class="enter_hour" style="height:30px;">
|
||||||
|
<lable class="cls_per_day_amount">
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount_per_session" />
|
||||||
|
</lable>
|
||||||
|
</td>
|
||||||
|
<td>Session</td>
|
||||||
|
<td style="height:30px;">
|
||||||
|
<t t-esc="request.session['session_type']" />
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<td class="enter_hour">Rate Per Hour</td>
|
||||||
|
<td class="enter_hour" style="height:30px;">
|
||||||
|
<lable class="cls_per_day_amount">
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<t t-esc="product_id.rental_amount_per_hour" />
|
||||||
|
</lable>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr style="height:70px;">
|
||||||
|
<td>Total Amount</td>
|
||||||
|
<td>
|
||||||
|
<t t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
<span class="calculate_total" />
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="quantity_needed">Enter quantity</td>
|
||||||
|
<td class="quantity_needed">
|
||||||
|
<input class='form-control qty_needed' type="text" name="qty_needed" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<t t-if=" not request.env.user._is_public()">
|
||||||
|
<button class="btn btn-primary btn-lg mt8 js_check_product a-submit" type="submit" name="update_cart">Add To Cart
|
||||||
|
</button>
|
||||||
|
</t>
|
||||||
|
<t t-if="request.env.user._is_public()">
|
||||||
|
<a class="btn btn-primary" t-att-href="'/web/login?redirect=/booking_form/create_quotation/%s' % slug(product_id)">
|
||||||
|
Sign in
|
||||||
|
</a>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="o_not_editable"></div>
|
||||||
|
<hr />
|
||||||
|
<p class="text-muted">
|
||||||
|
30-day money-back guarantee
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="rental_order" name="Rental Cart Lines">
|
||||||
|
<t t-call="portal.portal_layout">
|
||||||
|
<t t-set="breadcrumbs_searchbar" t-value="True" />
|
||||||
|
<t t-call="portal.portal_searchbar">
|
||||||
|
<t t-set="title">Rental Order</t>
|
||||||
|
</t>
|
||||||
|
<div id="wrap">
|
||||||
|
<div class="container oe_website_sale">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2 oe_cart">
|
||||||
|
<table class="table table-striped table-condensed js_cart_lines" id="cart_product" style="margin-top:35px;">
|
||||||
|
<thead style="background: lavender;">
|
||||||
|
<tr>
|
||||||
|
<th>Order Number</th>
|
||||||
|
<th>From Date</th>
|
||||||
|
<th>To Date</th>
|
||||||
|
<th>State</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<t t-foreach="order_id" t-as="order">
|
||||||
|
<form name="Rental Order Form" id="rental_order_form" action="/view_cart" method="post">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<tr>
|
||||||
|
<td t-if="order.res_number">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="order.res_number" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="order.from_date">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="order.from_date" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="order.to_date">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="order.to_date" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="order.state">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="order.state" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="submit" name="View Cart" value="View Cart" class="btn-info" />
|
||||||
|
</td>
|
||||||
|
<input type="hidden" t-att-value="order.id" name="rental_order" class="order" />
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="checkout_process" name="Checkout Process">
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<div id="wrap">
|
||||||
|
<div class="container oe_website_sale" style="height: 450px;">
|
||||||
|
<div class="main_content" style="position: relative;margin-top:50px;">
|
||||||
|
<div style="float:left;width:60%;padding: 10px;">
|
||||||
|
<t t-call="product_rental_bookings.payment" />
|
||||||
|
</div>
|
||||||
|
<div style="float:left;width:40%;margin-top:50px;">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Total Days/Hour</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>Sub Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<t t-foreach="website_rental_order.product_order_lines_ids" t-as="product_line">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product_line.product_id.name" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<t t-if="product_line.price_based == 'per_day'">
|
||||||
|
<span t-esc="int(product_line.enter_days)" />
|
||||||
|
Days
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<span t-esc="product_line.enter_hour" />
|
||||||
|
Hours
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product_line.qty_needed" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<!-- <t t-set="subtotal" t-value="product_line.sub_total * product_line.qty_needed"/>-->
|
||||||
|
<span t-esc="product_line.sub_total" />
|
||||||
|
<span t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<span style="float:right;">
|
||||||
|
<strong>Total:-</strong>
|
||||||
|
<!-- <t t-esc="subtotal"/>AAA-->
|
||||||
|
<span t-esc="website_rental_order.total_amount" />
|
||||||
|
<span t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="payment" name="Payment">
|
||||||
|
<t t-set="additional_title">Shop - Select Payment Acquirer</t>
|
||||||
|
<t t-set="no_footer">1</t>
|
||||||
|
<t>
|
||||||
|
<div class="col-xl">
|
||||||
|
<div class="wizard">
|
||||||
|
<div class="progress-rental-wizard">
|
||||||
|
<a class="no-decoration">
|
||||||
|
<div id="wizard-step10" class="progress-rental-wizard-step complete">
|
||||||
|
<div class="progress-rental-wizard-bar d-none d-md-block" />
|
||||||
|
<span class="progress-rental-wizard-dot d-none d-md-inline-block"></span>
|
||||||
|
<div class="text-center progress-rental-wizard-steplabel">
|
||||||
|
Review Order
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="no-decoration">
|
||||||
|
<div id="wizard-step40" class="progress-rental-wizard-step active">
|
||||||
|
<div class="progress-rental-wizard-bar d-none d-md-block" />
|
||||||
|
<span class="progress-rental-wizard-dot d-none d-md-inline-block"></span>
|
||||||
|
<div class="text-center progress-rental-wizard-steplabel">
|
||||||
|
Confirm Order
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<div id="wrap">
|
||||||
|
<div class="container oe_website_sale py-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12" t-if="errors">
|
||||||
|
<t t-foreach="errors" t-as="error">
|
||||||
|
<div class="alert alert-danger" t-if="error" role="alert">
|
||||||
|
<h4>
|
||||||
|
<t t-esc="error[0]" />
|
||||||
|
</h4>
|
||||||
|
<t t-esc="error[1]" />
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-xl order-xl-1 oe_cart">
|
||||||
|
<div id="payment_method" class="mt-3" t-if="(acquirers or tokens) and website_rental_order.total_amount">
|
||||||
|
<h3 class="mb24">Pay with</h3>
|
||||||
|
<t t-call="payment.payment_tokens_list">
|
||||||
|
<t t-set="mode" t-value="'payment'" />
|
||||||
|
<t t-set="submit_txt">Pay Now</t>
|
||||||
|
<t t-set="icon_right" t-value="1" />
|
||||||
|
<t t-set="icon_class" t-value="'fa-chevron-right'" />
|
||||||
|
<t t-set="submit_class" t-value="'btn btn-primary'" />
|
||||||
|
<t t-set="pms" t-value="tokens" />
|
||||||
|
<t t-set="form_action" t-value="'/rental/payment/validate'" />
|
||||||
|
<t t-set="prepare_tx_url" t-value="'/rental/payment/transaction/'" />
|
||||||
|
<t t-set="partner_id" t-value="partner" />
|
||||||
|
|
||||||
|
<t t-set="back_button_icon_class" t-value="'fa-chevron-left'" />
|
||||||
|
<t t-set="back_button_txt" t-value="'Return to Cart'" />
|
||||||
|
<t t-set="back_button_class" t-value="'btn btn-secondary'" />
|
||||||
|
<t t-set="back_button_link" t-value="'/view_cart'" />
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
<div t-if="not acquirers" class="mt-2">
|
||||||
|
<a role="button" class="btn-link" groups="base.group_system" t-attf-href="/web#return_label=Website&action=#{payment_action_id}">
|
||||||
|
<i class="fa fa-arrow-right"></i>
|
||||||
|
Add payment acquirers
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="js_payment mt-3" t-if="not website_rental_order.total_amount" id="payment_method">
|
||||||
|
<form action="/rental/payment/validate" method="post" class="float-right">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<button class="btn btn-primary" type="submit" name="payment">
|
||||||
|
<span class="fa fa-chevron-right" />
|
||||||
|
Pay Now
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_structure" id="oe_structure_website_sale_payment_2" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="rental_order_comfirmation">
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<div class="container">
|
||||||
|
<center style="margin-top: 60px;height: 350px;">
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-check-circle" style="font-size: 9.00rem;color: #29b329;" id="checkmark" />
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<h2>
|
||||||
|
<b>
|
||||||
|
Order #
|
||||||
|
<t t-esc="order.res_number" />
|
||||||
|
</b>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 50px;">
|
||||||
|
<h4>
|
||||||
|
<span>
|
||||||
|
Your Order has been confirmed
|
||||||
|
<br />
|
||||||
|
We will contact you soon
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="rental_product_cart_update" name="Product Cart">
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<div id="wrap">
|
||||||
|
<div class="container oe_website_sale">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2 oe_cart">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 o_website_sale_rightfit">
|
||||||
|
<form name="Checkout Form" id="checkout_form" action="/checkout" method="post">
|
||||||
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||||
|
<!-- <input type="hidden" name="location" t-att-value="request.session['location_id']"/>-->
|
||||||
|
<!-- <h1><t t-esc="request.session['location']"/>AAAA</h1>-->
|
||||||
|
<div class="row">
|
||||||
|
<t t-if="product_order_id">
|
||||||
|
<div class="col-xl">
|
||||||
|
<div class="wizard">
|
||||||
|
<div class="progress-rental-wizard">
|
||||||
|
<a class="no-decoration">
|
||||||
|
<div id="wizard-step10" class="progress-rental-wizard-step active">
|
||||||
|
<div class="progress-rental-wizard-bar d-none d-md-block" />
|
||||||
|
<span class="progress-rental-wizard-dot d-none d-md-inline-block"></span>
|
||||||
|
<div class="text-center progress-rental-wizard-steplabel">
|
||||||
|
Review Order
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="no-decoration">
|
||||||
|
<div id="wizard-step40" class="progress-rental-wizard-step">
|
||||||
|
<div class="progress-rental-wizard-bar d-none d-md-block" />
|
||||||
|
<span class="progress-rental-wizard-dot d-none d-md-inline-block"></span>
|
||||||
|
<div class="text-center progress-rental-wizard-steplabel">
|
||||||
|
Confirm Order
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
<h2 class="mb8 mt8">Product Cart</h2>
|
||||||
|
<t t-call="product_rental_bookings.cart_lines" />
|
||||||
|
<div class="clearfix" />
|
||||||
|
<input type="hidden" t-att-value="product_order_id" id="product_order_id_exist" />
|
||||||
|
<a role="button" href="/search-product" class="btn btn-secondary mb32 d-none d-xl-inline-block">
|
||||||
|
<span class="fa fa-chevron-left" />
|
||||||
|
<span class="">Rent More</span>
|
||||||
|
</a>
|
||||||
|
<t t-if="product_order_id">
|
||||||
|
<input type="submit" name="process_checkout" id="process_checkout" value="Process Checkout" class="btn-info mb16" style="float:right; line-height:1.5 ;font-size:1.09375rem; padding: 0.5rem 1rem; border-radius:0.3rem" />
|
||||||
|
</t>
|
||||||
|
<div class="oe_structure" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_structure" />
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="cart_lines" name="Rental Cart Lines">
|
||||||
|
<table class="table table-striped table-condensed js_cart_lines" id="cart_product">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="td-img">Product</th>
|
||||||
|
<th></th>
|
||||||
|
<th>Total Days/Hour</th>
|
||||||
|
<th class="text-center td-price">Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>Total Price</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<t t-foreach="product_order_id.product_order_lines_ids" t-as="line">
|
||||||
|
<tr class="product_order_line">
|
||||||
|
<td>
|
||||||
|
<span t-field="line.product_id.image_1920" t-class="img rounded o_image_64_max" t-options="{'widget': 'image', 'class': 'img-rounded'}" />
|
||||||
|
</td>
|
||||||
|
<td t-if="line.product_id.name" class='td-product_name'>
|
||||||
|
<div>
|
||||||
|
<strong t-esc="line.product_id.with_context(display_default_code=False).display_name" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="line.price_based in ['per_hour','per_session']">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="line.enter_hour" />
|
||||||
|
hours
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-else="">
|
||||||
|
<div>
|
||||||
|
<strong t-esc="int(line.enter_days)" />
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="line.price">
|
||||||
|
<div t-if="line.price_based == 'per_day'" style="display: inline-flex;">
|
||||||
|
<strong t-esc="line.product_id.rental_amount" />
|
||||||
|
<strong t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</div>
|
||||||
|
<div t-if="line.price_based == 'per_hour'" style="display: inline-flex;">
|
||||||
|
<strong t-esc="line.product_id.rental_amount_per_hour" />
|
||||||
|
<strong t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</div>
|
||||||
|
<div t-if="line.price_based == 'per_session'" style="display: inline-flex;">
|
||||||
|
<strong t-esc="line.product_id.rental_amount_per_session" />
|
||||||
|
<strong t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td t-if="line.qty_needed">
|
||||||
|
<strong t-esc="line.qty_needed" />
|
||||||
|
</td>
|
||||||
|
<td t-if="line.sub_total">
|
||||||
|
<div style="display: inline-flex;">
|
||||||
|
<strong t-esc="line.sub_total" />
|
||||||
|
<strong t-esc="request.env.user.company_id.currency_id.symbol" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="td-action">
|
||||||
|
<a aria-label="Remove from cart" t-att-id="line.id" title="Remove from cart" class='js_delete_product no-decoration'>
|
||||||
|
<small>
|
||||||
|
<i class='fa fa-trash-o' style="font-size: 25px;"></i>
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="product_rental_order" name="Product Order" inherit_id="portal.portal_my_home" priority="20">
|
||||||
|
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
|
||||||
|
<t t-call="portal.portal_docs_entry">
|
||||||
|
<t t-set="title">Rental Orders</t>
|
||||||
|
<t t-set="url" t-value="'/order'" />
|
||||||
|
<t t-set="count" t-value="rental_order_count" />
|
||||||
|
</t>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="portal_my_rental_orders" name="Rental Orders" inherit_id="portal.portal_breadcrumbs" priority="20">
|
||||||
|
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
|
||||||
|
<li t-if="page_name == 'rental_order'" t-attf-class="breadcrumb-item">
|
||||||
|
Rental Orders
|
||||||
|
</li>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
12
product_rental_web_bookings/views/website_templates.xml
Normal file
12
product_rental_web_bookings/views/website_templates.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="submenu_inherit" inherit_id="website.submenu" name="Submenu">
|
||||||
|
<xpath expr="//li[1]" position="replace">
|
||||||
|
<li t-if="submenu.is_visible and not has_visible_submenu" t-attf-class="#{item_class or ''}">
|
||||||
|
<a t-att-href="submenu.clean_url()" t-attf-class="#{link_class or ''} #{'active' if submenu.clean_url() and unslug_url(request.httprequest.path) == unslug_url(submenu.clean_url()) else ''}" role="menuitem" t-ignore="true" t-att-target="'_blank' if submenu.new_window else None">
|
||||||
|
<span t-field="submenu.name" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
Reference in New Issue
Block a user