[ADD] website_decimal_quantity: modified version of Cybro
|
|
@ -7,7 +7,7 @@ readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/templat
|
|||
manifest-required-authors=BAKEUP
|
||||
manifest-required-keys=license
|
||||
manifest-deprecated-keys=description,active
|
||||
license-allowed=LGPL-3,OPL-1
|
||||
license-allowed=LGPL-3,OPL-1,AGPL-3
|
||||
valid-odoo-versions=16.0
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
|
|
|||
69
website_decimal_quantity/README.rst
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
========================
|
||||
Website Decimal Quantity
|
||||
========================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:57abd0a526646836d53d961ac63f79f774c0b591a63f96fcc61f9fb0ec6e196d
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-bruecksen%2Fife_nfu-lightgray.png?logo=github
|
||||
:target: https://github.com/bruecksen/ife_nfu/tree/16.0/website_decimal_quantity
|
||||
:alt: bruecksen/ife_nfu
|
||||
|
||||
|badge1| |badge2| |badge3|
|
||||
|
||||
The app allows to select quantity in decimal for products in Website/Shop",
|
||||
"description": """
|
||||
The app allows to select quantity in decimal for products in Website/Shop
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
- 16.0.1.0.0: Initial version
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/bruecksen/ife_nfu/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/bruecksen/ife_nfu/issues/new?body=module:%20website_decimal_quantity%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Cybrosys Techno Solutions
|
||||
* BAKEUP
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- Cybrosys Techno Solutions
|
||||
- BAKEUP
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is part of the `bruecksen/ife_nfu <https://github.com/bruecksen/ife_nfu/tree/16.0/website_decimal_quantity>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute.
|
||||
22
website_decimal_quantity/__init__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import controllers
|
||||
from . import models
|
||||
43
website_decimal_quantity/__manifest__.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
{
|
||||
"name": "Website Decimal Quantity",
|
||||
"version": "16.0.1.0.0",
|
||||
"category": "Website/Website",
|
||||
"author": "Cybrosys Techno Solutions,BAKEUP",
|
||||
"company": "Cybrosys Techno Solutions",
|
||||
"maintainer": "BAKEUP",
|
||||
"images": ["static/description/banner.png"],
|
||||
"website": "https://www.bakeup.org",
|
||||
"depends": ["website_sale"],
|
||||
"data": ["views/website_sale_templates.xml"],
|
||||
"assets": {
|
||||
"web.assets_frontend": [
|
||||
"/website_decimal_quantity/static/src/js/variant_mixin.js",
|
||||
"/website_decimal_quantity/static/src/js/website_sale.js",
|
||||
"/website_decimal_quantity/static/src/js/website_sale_utils.js",
|
||||
]
|
||||
},
|
||||
"license": "AGPL-3",
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
"application": False,
|
||||
}
|
||||
22
website_decimal_quantity/controllers/__init__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import main
|
||||
from . import website_sale_product_configurator
|
||||
193
website_decimal_quantity/controllers/main.py
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from odoo import fields, http
|
||||
from odoo.http import request
|
||||
from odoo.tools.json import scriptsafe as json_scriptsafe
|
||||
|
||||
from odoo.addons.payment import utils as payment_utils
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
|
||||
|
||||
class WebsiteSaleDecimal(WebsiteSale):
|
||||
"""
|
||||
WebsiteSaleDecimal extends WebsiteSale class to update the methods: cart, cart_update,
|
||||
cart_update_json and cart_quantity.
|
||||
"""
|
||||
|
||||
@http.route()
|
||||
def cart(self, access_token=None, revive="", **post):
|
||||
"""
|
||||
Main cart management + abandoned cart revival
|
||||
access_token: Abandoned cart SO access token
|
||||
revive: Revival method when abandoned cart. Can be 'merge' or 'squash'
|
||||
"""
|
||||
order = request.website.sale_get_order()
|
||||
if order and order.state != "draft":
|
||||
request.session["sale_order_id"] = None
|
||||
order = request.website.sale_get_order()
|
||||
request.session["website_sale_cart_quantity"] = round(
|
||||
sum(order.mapped("website_order_line.product_uom_qty")), 1
|
||||
)
|
||||
|
||||
values = {}
|
||||
if access_token:
|
||||
abandoned_order = request.env["sale.order"].sudo().search([("access_token", "=", access_token)], limit=1)
|
||||
if not abandoned_order:
|
||||
raise NotFound()
|
||||
if abandoned_order.state != "draft":
|
||||
values.update({"abandoned_proceed": True})
|
||||
elif revive == "squash" or (revive == "merge" and not request.session.get("sale_order_id")):
|
||||
request.session["sale_order_id"] = abandoned_order.id
|
||||
return request.redirect("/shop/cart")
|
||||
elif revive == "merge":
|
||||
abandoned_order.order_line.write({"order_id": request.session["sale_order_id"]})
|
||||
abandoned_order.action_cancel()
|
||||
elif abandoned_order.id != request.session.get("sale_order_id"):
|
||||
values.update({"access_token": abandoned_order.access_token})
|
||||
|
||||
values.update({"website_sale_order": order, "date": fields.Date.today(), "suggested_products": []})
|
||||
if order:
|
||||
order.order_line.filtered(lambda l: not l.product_id.active).unlink()
|
||||
values["suggested_products"] = order._cart_accessories()
|
||||
values.update(self._get_express_shop_payment_values(order))
|
||||
|
||||
if post.get("type") == "popover":
|
||||
# force no-cache so IE11 doesn't cache this XHR
|
||||
return request.render("website_sale.cart_popover", values, headers={"Cache-Control": "no-cache"})
|
||||
|
||||
return request.render("website_sale.cart", values)
|
||||
|
||||
@http.route()
|
||||
def cart_update(
|
||||
self,
|
||||
product_id,
|
||||
add_qty=1,
|
||||
set_qty=0,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_values=None,
|
||||
express=False,
|
||||
**kwargs
|
||||
):
|
||||
"""This route is called when adding a product to cart (no options)."""
|
||||
sale_order = request.website.sale_get_order(force_create=True)
|
||||
if sale_order.state != "draft":
|
||||
request.session["sale_order_id"] = None
|
||||
sale_order = request.website.sale_get_order(force_create=True)
|
||||
|
||||
if product_custom_attribute_values:
|
||||
product_custom_attribute_values = json_scriptsafe.loads(product_custom_attribute_values)
|
||||
|
||||
if no_variant_attribute_values:
|
||||
no_variant_attribute_values = json_scriptsafe.loads(no_variant_attribute_values)
|
||||
|
||||
sale_order._cart_update(
|
||||
product_id=int(product_id),
|
||||
add_qty=add_qty,
|
||||
set_qty=set_qty,
|
||||
product_custom_attribute_values=product_custom_attribute_values,
|
||||
no_variant_attribute_values=no_variant_attribute_values,
|
||||
**kwargs
|
||||
)
|
||||
request.session["website_sale_cart_quantity"] = round(
|
||||
sum(sale_order.mapped("website_order_line.product_uom_qty")), 1
|
||||
)
|
||||
|
||||
if express:
|
||||
return request.redirect("/shop/checkout?express=1")
|
||||
|
||||
return request.redirect("/shop/cart")
|
||||
|
||||
@http.route()
|
||||
def cart_update_json(
|
||||
self,
|
||||
product_id,
|
||||
line_id=None,
|
||||
add_qty=None,
|
||||
set_qty=None,
|
||||
display=True,
|
||||
product_custom_attribute_values=None,
|
||||
no_variant_attribute_values=None,
|
||||
**kw
|
||||
):
|
||||
"""
|
||||
This route is called :
|
||||
- When changing quantity from the cart.
|
||||
- When adding a product from the wishlist.
|
||||
- When adding a product to cart on the same page
|
||||
(without redirection).
|
||||
"""
|
||||
order = request.website.sale_get_order(force_create=True)
|
||||
if order.state != "draft":
|
||||
request.website.sale_reset()
|
||||
if kw.get("force_create"):
|
||||
order = request.website.sale_get_order(force_create=True)
|
||||
else:
|
||||
return {}
|
||||
|
||||
if product_custom_attribute_values:
|
||||
product_custom_attribute_values = json_scriptsafe.loads(product_custom_attribute_values)
|
||||
|
||||
if no_variant_attribute_values:
|
||||
no_variant_attribute_values = json_scriptsafe.loads(no_variant_attribute_values)
|
||||
|
||||
values = order._cart_update(
|
||||
product_id=product_id,
|
||||
line_id=line_id,
|
||||
add_qty=add_qty,
|
||||
set_qty=set_qty,
|
||||
product_custom_attribute_values=product_custom_attribute_values,
|
||||
no_variant_attribute_values=no_variant_attribute_values,
|
||||
**kw
|
||||
)
|
||||
request.session["website_sale_cart_quantity"] = round(
|
||||
sum(order.mapped("website_order_line.product_uom_qty")), 1
|
||||
)
|
||||
|
||||
if not order.cart_quantity:
|
||||
request.website.sale_reset()
|
||||
return values
|
||||
|
||||
values["cart_quantity"] = round(sum(order.mapped("website_order_line.product_uom_qty")), 1)
|
||||
values["minor_amount"] = (payment_utils.to_minor_currency_units(order.amount_total, order.currency_id),)
|
||||
values["amount"] = order.amount_total
|
||||
|
||||
if not display:
|
||||
return values
|
||||
|
||||
values["website_sale.cart_lines"] = request.env["ir.ui.view"]._render_template(
|
||||
"website_sale.cart_lines",
|
||||
{"website_sale_order": order, "date": fields.Date.today(), "suggested_products": order._cart_accessories()},
|
||||
)
|
||||
values["website_sale.short_cart_summary"] = request.env["ir.ui.view"]._render_template(
|
||||
"website_sale.short_cart_summary", {"website_sale_order": order}
|
||||
)
|
||||
return values
|
||||
|
||||
@http.route()
|
||||
def cart_quantity(self):
|
||||
"""
|
||||
This method updates the cart quantity count in the session based on the order's website order lines.
|
||||
"""
|
||||
if "website_sale_cart_quantity" not in request.session:
|
||||
return request.website.sale_get_order().mapped("website_order_line.product_uom_qty")
|
||||
return request.session["website_sale_cart_quantity"]
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
import json
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.website_sale_product_configurator.controllers.main import WebsiteSale
|
||||
|
||||
|
||||
class WebsiteSaleDecimal(WebsiteSale):
|
||||
"""
|
||||
WebsiteSaleDecimal extends WebsiteSale class to update the methods: cart_options_update_json.
|
||||
"""
|
||||
|
||||
@http.route()
|
||||
def cart_options_update_json(self, product_and_options, goto_shop=None, lang=None, **kwargs):
|
||||
"""This route is called when submitting the optional product modal.
|
||||
The product without parent is the main product, the other are
|
||||
options.
|
||||
Options need to be linked to their parents with a unique ID.
|
||||
The main product is the first product in the list and the options
|
||||
need to be right after their parent.
|
||||
product_and_options {
|
||||
'product_id',
|
||||
'product_template_id',
|
||||
'quantity',
|
||||
'parent_unique_id',
|
||||
'unique_id',
|
||||
'product_custom_attribute_values',
|
||||
'no_variant_attribute_values'
|
||||
}
|
||||
"""
|
||||
if lang:
|
||||
request.website = request.website.with_context(lang=lang)
|
||||
|
||||
order = request.website.sale_get_order(force_create=True)
|
||||
if order.state != "draft":
|
||||
request.session["sale_order_id"] = None
|
||||
order = request.website.sale_get_order(force_create=True)
|
||||
|
||||
product_and_options = json.loads(product_and_options)
|
||||
if product_and_options:
|
||||
# The main product is the first, optional products are the rest
|
||||
main_product = product_and_options[0]
|
||||
value = order._cart_update(
|
||||
product_id=main_product["product_id"],
|
||||
add_qty=main_product["quantity"],
|
||||
product_custom_attribute_values=main_product["product_custom_attribute_values"],
|
||||
no_variant_attribute_values=main_product["no_variant_attribute_values"],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if value["line_id"]:
|
||||
# Link option with its parent iff line has been created.
|
||||
option_parent = {main_product["unique_id"]: value["line_id"]}
|
||||
for option in product_and_options[1:]:
|
||||
parent_unique_id = option["parent_unique_id"]
|
||||
option_value = order._cart_update(
|
||||
product_id=option["product_id"],
|
||||
set_qty=option["quantity"],
|
||||
linked_line_id=option_parent[parent_unique_id],
|
||||
product_custom_attribute_values=option["product_custom_attribute_values"],
|
||||
no_variant_attribute_values=option["no_variant_attribute_values"],
|
||||
**kwargs
|
||||
)
|
||||
option_parent[option["unique_id"]] = option_value["line_id"]
|
||||
request.session["website_sale_cart_quantity"] = round(
|
||||
sum(order.mapped("website_order_line.product_uom_qty")), 1
|
||||
)
|
||||
|
||||
return str(round(sum(order.mapped("website_order_line.product_uom_qty")), 1))
|
||||
9
website_decimal_quantity/doc/RELEASE_NOTES.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
## Module <website_decimal_quantity>
|
||||
|
||||
#### 15.02.2023
|
||||
|
||||
#### Version 16.0.1.0.0
|
||||
|
||||
#### ADD
|
||||
|
||||
- Initial Commit for Website Decimal Quantity.
|
||||
21
website_decimal_quantity/models/__init__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from . import sale_order
|
||||
106
website_decimal_quantity/models/sale_order.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#############################################################################
|
||||
#
|
||||
# Cybrosys Technologies Pvt. Ltd.
|
||||
#
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
||||
# Author: Vivek @ cybrosys,(odoo@cybrosys.com)
|
||||
#
|
||||
# You can modify it under the terms of the GNU AFFERO
|
||||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
# (AGPL v3) along with this program.
|
||||
# If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#############################################################################
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = "sale.order"
|
||||
"""
|
||||
Inherit the 'sale.order' model to overwrite the _compute_cart_info and _cart_update functions.
|
||||
"""
|
||||
|
||||
@api.depends("order_line.product_uom_qty", "order_line.product_id")
|
||||
def _compute_cart_info(self):
|
||||
"""
|
||||
Making cart_quantity integer is avoided in order
|
||||
to represent it in decimal values
|
||||
"""
|
||||
for order in self:
|
||||
order.cart_quantity = sum(order.mapped("website_order_line.product_uom_qty"))
|
||||
order.only_services = all(line.product_id.type == "service" for line in order.website_order_line)
|
||||
|
||||
def _cart_update(self, product_id, line_id=None, add_qty=0, set_qty=0, **kwargs): # noqa: C901
|
||||
"""Add or set product quantity, add_qty can be negative.
|
||||
|
||||
Making add_qty and set_qty integer are avoided in order
|
||||
to represent them as decimal values.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.state != "draft":
|
||||
request.session.pop("sale_order_id", None)
|
||||
request.session.pop("website_sale_cart_quantity", None)
|
||||
raise UserError(_("It is forbidden to modify a sales order which is not in draft status."))
|
||||
product = self.env["product.product"].browse(product_id).exists()
|
||||
if not product or not product._is_add_to_cart_allowed():
|
||||
raise UserError(_("The given product does not exist therefore it cannot be added to cart."))
|
||||
if product.lst_price == 0 and product.website_id.prevent_zero_price_sale:
|
||||
raise UserError(_("The given product does not have a price therefore it cannot be added to cart."))
|
||||
if line_id is not False:
|
||||
order_line = self._cart_find_product_line(product_id, line_id, **kwargs)[:1]
|
||||
else:
|
||||
order_line = self.env["sale.order.line"]
|
||||
try:
|
||||
if add_qty:
|
||||
pass
|
||||
except ValueError:
|
||||
add_qty = 1
|
||||
try:
|
||||
if set_qty:
|
||||
pass
|
||||
except ValueError:
|
||||
set_qty = 0
|
||||
quantity = 0
|
||||
if set_qty:
|
||||
quantity = set_qty
|
||||
elif add_qty is not None:
|
||||
if order_line:
|
||||
quantity = order_line.product_uom_qty + (add_qty or 0)
|
||||
else:
|
||||
quantity = add_qty or 0
|
||||
if quantity > 0:
|
||||
quantity, warning = self._verify_updated_quantity(order_line, product_id, quantity, **kwargs)
|
||||
else:
|
||||
# If the line will be removed anyway, there is no need to verify
|
||||
# the requested quantity update.
|
||||
warning = ""
|
||||
if order_line and quantity <= 0:
|
||||
# Remove zero or negative lines
|
||||
order_line.unlink()
|
||||
order_line = self.env["sale.order.line"]
|
||||
elif order_line:
|
||||
# Update existing line
|
||||
update_values = self._prepare_order_line_update_values(order_line, quantity, **kwargs)
|
||||
if update_values:
|
||||
self._update_cart_line_values(order_line, update_values)
|
||||
elif quantity >= 0:
|
||||
# Create new line
|
||||
order_line_values = self._prepare_order_line_values(product_id, quantity, **kwargs)
|
||||
order_line = self.env["sale.order.line"].sudo().create(order_line_values)
|
||||
return {
|
||||
"line_id": order_line.id,
|
||||
"quantity": quantity,
|
||||
"option_ids": list(
|
||||
set(order_line.option_line_ids.filtered(lambda l: l.order_id == order_line.order_id).ids)
|
||||
),
|
||||
"warning": warning,
|
||||
}
|
||||
2
website_decimal_quantity/readme/CONTRIBUTORS.rst
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- Cybrosys Techno Solutions
|
||||
- BAKEUP
|
||||
3
website_decimal_quantity/readme/DESCRIPTION.rst
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
The app allows to select quantity in decimal for products in Website/Shop",
|
||||
"description": """
|
||||
The app allows to select quantity in decimal for products in Website/Shop
|
||||
1
website_decimal_quantity/readme/HISTORY.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
- 16.0.1.0.0: Initial version
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 310 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 911 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 878 B |
|
After Width: | Height: | Size: 653 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 627 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 988 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 589 B |
BIN
website_decimal_quantity/static/description/assets/misc/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 967 B |
BIN
website_decimal_quantity/static/description/assets/misc/star.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 5 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/1.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/2.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/3.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/4.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/5.gif
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
website_decimal_quantity/static/description/assets/modules/6.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 122 KiB |
BIN
website_decimal_quantity/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
website_decimal_quantity/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
612
website_decimal_quantity/static/description/index.html
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;">
|
||||
<!-- TITLE BAR -->
|
||||
<div
|
||||
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>
|
||||
</div>
|
||||
<!-- END OF TITLE BAR -->
|
||||
|
||||
<!-- APP HERO -->
|
||||
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">Website
|
||||
Decimal Quantity</h1>
|
||||
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">Select Decimal Product Quantity
|
||||
from Website</p>
|
||||
<!-- END OF APP HERO -->
|
||||
<img src="assets/screenshots/hero.gif"
|
||||
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/>
|
||||
|
||||
</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="#configuration">
|
||||
<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;">Configuration</span>
|
||||
<span
|
||||
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View configurations 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="#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;">
|
||||
See key screenshots of 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 allows users to choose products from the website shop in decimal quantities. This can be helpful
|
||||
for users who need to select products in decimal quantities rather than multiples of one.
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF OVERVIEW SECTION -->
|
||||
|
||||
<!-- CONFIGURATION SECTION -->
|
||||
<div class="d-flex align-items-center"
|
||||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"
|
||||
id="configuration">
|
||||
<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;">
|
||||
Configuration
|
||||
</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">
|
||||
No additional configuration required.
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF CONFIGURATION 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;">Decimal Product Quantity</span>
|
||||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 450 !important; color: #282F33 !important; font-size: 1rem !important;">
|
||||
Allows users to select product in decimal quantities from product and cart page in Website/Shop.</p>
|
||||
</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;">Simultaneous Price Calculation</span>
|
||||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 450 !important; color: #282F33 !important; font-size: 1rem !important;">
|
||||
Simultaneous price calculation in the cart according to the selected decimal quantities.</p>
|
||||
</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;">Decimal Product
|
||||
Quantity in Product Page</h3>
|
||||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Select product
|
||||
quantities in decimal values from Product Page.</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;">Decimal Product
|
||||
Quantity in Cart Page</h3>
|
||||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Select product
|
||||
quantities in decimal values from Cart Page.</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;">Simultaneous price
|
||||
calculation in cart</h3>
|
||||
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Price is calculated in
|
||||
the cart according to the decimal product values.</p>
|
||||
<img src="assets/screenshots/3.png" class="img-thumbnail">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SCREENSHOTS SECTION -->
|
||||
|
||||
<!-- RELATED PRODUCTS -->
|
||||
<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/categories.png"/>
|
||||
</div>
|
||||
<h2 class="mt-2"
|
||||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
|
||||
Related
|
||||
Products
|
||||
</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div id="demo1" class="row carousel slide" data-ride="carousel">
|
||||
<!-- The slideshow -->
|
||||
<div class="carousel-inner" style="padding: 30px;">
|
||||
<div class="carousel-item" style="min-height: 198.656px;">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
|
||||
style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/16.0/export_stockinfo_xls/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/1.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/16.0/inventory_stock_dashboard_odoo/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/2.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/16.0/customer_product_qrcode/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/3.png">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item active"
|
||||
style="min-height: 198.656px;">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
|
||||
style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/16.0/list_view_sticky_header/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/4.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/16.0/openai_product_tag_descrption/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/5.gif">
|
||||
</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/16.0/advanced_vat_invoice/"
|
||||
target="_blank">
|
||||
<div style="border-radius:10px">
|
||||
<img class="img img-responsive center-block"
|
||||
style="border-radius: 0px;"
|
||||
src="assets/modules/6.png">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Left and right controls -->
|
||||
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
|
||||
style="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="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 RELATED 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 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 & 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 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"
|
||||
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"
|
||||
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 -->
|
||||
28
website_decimal_quantity/static/src/js/variant_mixin.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
odoo.define("website_decimal_qty.VariantMixin", function (require) {
|
||||
"use strict";
|
||||
|
||||
var VariantMixin = require("sale.VariantMixin");
|
||||
|
||||
/**
|
||||
* A custom implementation of the onClickAddCartJSON function in the
|
||||
* sale.VariantMixin module. It allows for decimal quantities to be added to the cart
|
||||
* by updating the input value with increments of 0.1.
|
||||
*/
|
||||
|
||||
VariantMixin.onClickAddCartJSON = function (ev) {
|
||||
ev.preventDefault();
|
||||
var $link = $(ev.currentTarget);
|
||||
var $input = $link.closest(".input-group").find("input");
|
||||
var min = parseFloat($input.data("min") || 0);
|
||||
var max = parseFloat($input.data("max") || Infinity);
|
||||
var previousQty = parseFloat($input.val() || 0, 10);
|
||||
var quantity = ($link.has(".fa-minus").length ? -0.1 : 0.1) + previousQty;
|
||||
var newQt = quantity > min ? (quantity < max ? quantity : max) : min;
|
||||
if (newQt !== previousQty) {
|
||||
var newQty = newQt.toFixed(1);
|
||||
$input.val(newQty).trigger("change");
|
||||
}
|
||||
newQty = newQt.toFixed(1);
|
||||
return false;
|
||||
};
|
||||
});
|
||||
86
website_decimal_quantity/static/src/js/website_sale.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
odoo.define("website_decimal_qty.website_sale", function (require) {
|
||||
"use strict";
|
||||
|
||||
var publicWidget = require("web.public.widget");
|
||||
var wSaleUtils = require("website_sale.utils");
|
||||
require("website_sale.website_sale");
|
||||
|
||||
/**
|
||||
* This module extends the website_sale module to support decimal quantity input for adding products to the cart.
|
||||
* It overrides the onClickAddCartJSON function from sale.VariantMixin to add support for decimal quantity input,
|
||||
* and extends the WebsiteSale widget to handle updating cart quantities when a decimal quantity is entered.
|
||||
*/
|
||||
|
||||
publicWidget.registry.WebsiteSale.include({
|
||||
_changeCartQuantity: function ($input, value, $dom_optional, line_id, productIDs) {
|
||||
_.each($dom_optional, function (elem) {
|
||||
$(elem).find(".js_quantity").text(value);
|
||||
productIDs.push($(elem).find("span[data-product-id]").data("product-id"));
|
||||
});
|
||||
$input.data("update_change", true);
|
||||
|
||||
this._rpc({
|
||||
route: "/shop/cart/update_json",
|
||||
params: {
|
||||
line_id: line_id,
|
||||
product_id: parseInt($input.data("product-id"), 10),
|
||||
set_qty: value,
|
||||
},
|
||||
}).then(function (data) {
|
||||
$input.data("update_change", false);
|
||||
var check_value = parseFloat($input.val());
|
||||
|
||||
if (isNaN(check_value)) {
|
||||
check_value = 1;
|
||||
}
|
||||
if (value !== check_value) {
|
||||
$input.trigger("change");
|
||||
return;
|
||||
}
|
||||
if (!data.cart_quantity) {
|
||||
return (window.location = "/shop/cart");
|
||||
}
|
||||
wSaleUtils.updateCartNavBar(data);
|
||||
$input.val(data.quantity);
|
||||
|
||||
$(".js_quantity[data-line-id=" + line_id + "]")
|
||||
.val(data.quantity)
|
||||
.text(data.quantity);
|
||||
|
||||
if (data.warning) {
|
||||
var cart_alert = $(".oe_cart").parent().find("#data_warning");
|
||||
if (cart_alert.length === 0) {
|
||||
$(".oe_cart").prepend(
|
||||
'<div class="alert alert-danger alert-dismissable" role="alert" id="data_warning">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' +
|
||||
data.warning +
|
||||
"</div>"
|
||||
);
|
||||
} else {
|
||||
cart_alert.html(
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' +
|
||||
data.warning
|
||||
);
|
||||
}
|
||||
$input.val(data.quantity);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_onChangeCartQuantity: function (ev) {
|
||||
var $input = $(ev.currentTarget);
|
||||
if ($input.data("update_change")) {
|
||||
return;
|
||||
}
|
||||
var value = parseFloat($input.val() || 0, 10);
|
||||
if (isNaN(value)) {
|
||||
value = 1;
|
||||
}
|
||||
var $dom = $input.closest("tr");
|
||||
var $dom_optional = $dom.nextUntil(":not(.optional_product.info)");
|
||||
var line_id = parseInt($input.data("line-id"), 10);
|
||||
var productIDs = [parseInt($input.data("product-id"), 10)];
|
||||
this._changeCartQuantity($input, value, $dom_optional, line_id, productIDs);
|
||||
},
|
||||
});
|
||||
});
|
||||
31
website_decimal_quantity/static/src/js/website_sale_utils.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
odoo.define("website_decimal_quantity.utils", function (require) {
|
||||
"use strict";
|
||||
|
||||
var utils = require("website_sale.utils");
|
||||
|
||||
/**
|
||||
* This module overrides the updateCartNavBar function of the website_sale.utils module to provide
|
||||
* support for decimal quantities in the cart.
|
||||
* It replaces the cart quantity text with an animated icon and updates the cart summary and lines with the new data.
|
||||
*/
|
||||
|
||||
utils.updateCartNavBar = function (data) {
|
||||
$(".my_cart_quantity")
|
||||
.parents("li.o_wsale_my_cart")
|
||||
.removeClass("d-none")
|
||||
.end()
|
||||
.addClass("o_mycart_zoom_animation")
|
||||
.delay(300)
|
||||
.queue(function () {
|
||||
$(this)
|
||||
.toggleClass("fa fa-warning", !data.cart_quantity)
|
||||
.attr("title", data.warning)
|
||||
.text(data.cart_quantity || "")
|
||||
.removeClass("o_mycart_zoom_animation")
|
||||
.dequeue();
|
||||
});
|
||||
|
||||
$(".js_cart_lines").first().before(data["website_sale.cart_lines"]).end().remove();
|
||||
$(".js_cart_summary").replaceWith(data["website_sale.short_cart_summary"]);
|
||||
};
|
||||
});
|
||||
31
website_decimal_quantity/views/website_sale_templates.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- This template replaces the default quantity selector with a text input field
|
||||
that allows for decimal values. -->
|
||||
<template
|
||||
id="product_quantity_decimal"
|
||||
inherit_id="website_sale.product_quantity"
|
||||
name="Select Quantity"
|
||||
priority="99"
|
||||
>
|
||||
<xpath expr="//input[@name='add_qty']" position="replace">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control quantity text-center"
|
||||
data-min="0.1"
|
||||
name="add_qty"
|
||||
t-att-value="add_qty or 1"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- This template replaces the default cart popover button with a button that
|
||||
displays the total number of items in the cart with one decimal point precision. -->
|
||||
<template id="cart_popover_decimal" inherit_id="website_sale.cart_popover" name="Cart Popover">
|
||||
<xpath expr="//a[hasclass('btn-primary')]/span[hasclass('o_wsale_cart_quantity')]" position="attributes">
|
||||
<attribute
|
||||
name="t-esc"
|
||||
>round(sum(website_sale_order.mapped('website_order_line.product_uom_qty')), 1)</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||