Skip to content

Commit

Permalink
[ADD] website_sale_float_cart_qty: Add float qty in cart
Browse files Browse the repository at this point in the history
  • Loading branch information
unaiberis committed Aug 21, 2024
1 parent dc6e444 commit ee7402a
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 0 deletions.
6 changes: 6 additions & 0 deletions setup/website_sale_float_cart_qty/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
54 changes: 54 additions & 0 deletions website_sale_float_cart_qty/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

================================
Website Sale Float Cart Quantity
================================

Overview
--------

This module extends the functionality of the `website_sale` module in Odoo by allowing float quantities in the shopping cart instead of integer quantities.

Features
--------

- Enables users to add fractional quantities of products to the shopping cart.
- Overrides the `_changeCartQuantity` function from `website_sale` to handle float quantities.
- Uses `parseFloat` instead of `parseInt` for value conversion on the client-side.

Usage
-----

- Users can add decimal quantities of products to the shopping cart from the online store.

- Quantities are dynamically updated in the user interface after each quantity change.

Development
-----------

The `website_sale_float_cart_qty` module uses JavaScript to extend the functionality of Odoo's `website_sale` module. The `website_sale_float_cart_qty.js` file overrides the `_changeCartQuantity` function to handle float quantities and perform server communication via RPC.

Contributions
-------------

Contributions are welcome! If you want to contribute to the development of this module, feel free to submit a pull request or report issues on the official repository.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/avanzosc/odoo-addons/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.

Credits
=======

Contributors
------------
* Ana Juaristi <[email protected]>
* Unai Beristain <[email protected]>

Do not contact contributors directly about support or help with technical issues.
1 change: 1 addition & 0 deletions website_sale_float_cart_qty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions website_sale_float_cart_qty/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Website Sale Float Cart Quantity",
"summary": "Allow float quantities in cart for website sale module",
"version": "16.0.1.0.0",
"category": "Website",
"license": "LGPL-3",
"website": "https://github.com/OCA/e-commerce",
"author": "AvanzOSC, Odoo Community Association (OCA)",
"depends": [
"website_sale",
],
"assets": {
"web.assets_frontend": [
"website_sale_float_cart_qty/static/src/js/float_qty.js",
],
},
"installable": True,
}
1 change: 1 addition & 0 deletions website_sale_float_cart_qty/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import sale_order
127 changes: 127 additions & 0 deletions website_sale_float_cart_qty/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from odoo import _, fields, models
from odoo.exceptions import UserError


class SaleOrderInherit(models.Model):
_inherit = "sale.order"

cart_quantity = fields.Float(
compute="_compute_cart_info",
)

def _cart_update(self, product_id, line_id=None, add_qty=0, set_qty=0, **kwargs):
if (
(isinstance(add_qty, int) or isinstance(set_qty, int))
and add_qty is not False
and set_qty is not False
):
return super()._cart_update(product_id, line_id, add_qty, set_qty, **kwargs)
else:
# Add or set product quantity, add_qty can be negative
self.ensure_one()
self = self.with_company(self.company_id)

if self.state != "draft":
self.env["request"].session.pop("sale_order_id", None)
self.env["request"].session.pop("website_sale_cart_quantity", None)
raise UserError(

Check warning on line 27 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L25-L27

Added lines #L25 - L27 were not covered by tests
_(
"It is forbidden to modify a sales order which is not in draft status."
)
)

product = self.env["product.product"].browse(product_id).exists()
if add_qty and (not product or not product._is_add_to_cart_allowed()):
raise UserError(

Check warning on line 35 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L35

Added line #L35 was not covered by tests
_(
"The given product does not exist 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"]

Check warning on line 46 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L46

Added line #L46 was not covered by tests

quantity = 0
if set_qty:
quantity = set_qty
elif add_qty:
if order_line:
quantity = order_line.product_uom_qty + add_qty
else:
quantity = add_qty

Check warning on line 55 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L55

Added line #L55 was not covered by tests

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 = ""

Check warning on line 67 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L67

Added line #L67 was not covered by tests

# Round it to avoid infinite 0 with a one after it
quantity = round(quantity, 9)

order_line = self._cart_update_order_line(
product_id, quantity, order_line, **kwargs
)

if (
order_line
and order_line.price_unit == 0
and self.website_id.prevent_zero_price_sale
and product.detailed_type
not in self.env[
"product.template"
]._get_product_types_allow_zero_price()
):
raise UserError(

Check warning on line 85 in website_sale_float_cart_qty/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

website_sale_float_cart_qty/models/sale_order.py#L85

Added line #L85 was not covered by tests
_(
"""The given product does not have a price
therefore it cannot be added to cart.""",
)
)

return {
"line_id": order_line.id,
"quantity": quantity,
"option_ids": list(
set(
order_line.option_line_ids.filtered(
lambda line: line.order_id == order_line.order_id
).ids
)
),
"warning": warning,
}

def _compute_cart_info(self):
all_sums_are_int = True
for order in self:
total_quantity = sum(order.mapped("website_order_line.product_uom_qty"))

if not isinstance(total_quantity, int):
all_sums_are_int = False

only_services = all(
line.product_id.type == "service" for line in order.website_order_line
)
order.only_services = only_services
if all_sums_are_int:
return super()._compute_cart_info()
else:
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
)
86 changes: 86 additions & 0 deletions website_sale_float_cart_qty/static/src/js/float_qty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
odoo.define("website_sale_float_cart_qty.float_qty", function (require) {
"use strict";

var publicWidget = require("web.public.widget");
var wSaleUtils = require("website_sale.utils");
var core = require("web.core");
require("website_sale.website_sale");

publicWidget.registry.WebsiteSale.include({
/**
* Override the _changeCartQuantity method of WebsiteSale
* @param {jQuery} $input - The jQuery object representing the input field.
* @param {Number} value - The new value of the input field.
* @param {Array} $dom_optional - Array of DOM elements.
* @param {Number} line_id - The line ID associated with the cart line.
* @param {Array} productIDs - Array of product IDs.
*/

_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);

$input.val($input.val().replace(",", "."));

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() || 0, 10);
if (isNaN(check_value)) {
check_value = 1;
}
if (value !== check_value) {
$input.trigger("change");
return;
}
sessionStorage.setItem(
"website_sale_cart_quantity",
data.cart_quantity
);
if (!data.cart_quantity) {
return (window.location = "/shop/cart");
}
$input.val(data.quantity);
$(".js_quantity[data-line-id=" + line_id + "]")
.val(data.quantity)
.text(data.quantity);

wSaleUtils.updateCartNavBar(data);
wSaleUtils.showWarning(data.warning);
// Propagating the change to the express checkout forms
core.bus.trigger("cart_amount_changed", data.amount, data.minor_amount);
});
},

_onChangeCartQuantity: function (ev) {
var $input = $(ev.currentTarget);
if ($input.data("update_change")) {
return;
}
var value = $input.val().replace(",", ".");
value = parseFloat(value);
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);
},
});
});
1 change: 1 addition & 0 deletions website_sale_float_cart_qty/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_cart_float_qty
Loading

0 comments on commit ee7402a

Please sign in to comment.