add website_decimal_quantity

This commit is contained in:
Luis 2026-10-02 10:05:22 +02:00
parent 0323b6672e
commit 9d69e5f095
67 changed files with 1858 additions and 0 deletions

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# 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

View file

@ -0,0 +1,202 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# 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 fields
from odoo import http
from odoo.http import request
from werkzeug.exceptions import NotFound
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']

View file

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# 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))