From bf5ccd62324be23f99aeb6a04060ad1e2ae54360 Mon Sep 17 00:00:00 2001 From: parp-odoo Date: Thu, 10 Oct 2024 07:33:42 +0000 Subject: [PATCH] [IMP] pos: display cart item quantity on product card Before this commit: - The product card did not show the number of items in the cart. After this commit: - Added quantity display on the product card which indicates how many of each product is in the cart. closes odoo/odoo#188736 Task: 4244560 X-original-commit: 3fa1deb6430c7d1c89eae63262b85912fe7c4a42 Signed-off-by: David Monnom (moda) Signed-off-by: Parthkumar Patel (parp) --- .../app/components/product_card/product_card.js | 1 + .../app/components/product_card/product_card.xml | 7 +++++-- .../static/src/app/models/pos_order.js | 4 ++++ .../app/screens/product_screen/product_screen.js | 15 ++++++++++++++- .../app/screens/product_screen/product_screen.xml | 1 + .../static/tests/pos/tours/product_screen_tour.js | 6 ++++++ .../tests/pos/tours/utils/product_screen_util.js | 13 +++++++++++++ 7 files changed, 44 insertions(+), 3 deletions(-) diff --git a/addons/point_of_sale/static/src/app/components/product_card/product_card.js b/addons/point_of_sale/static/src/app/components/product_card/product_card.js index 2fe1f1e35effd..49f54760cb148 100644 --- a/addons/point_of_sale/static/src/app/components/product_card/product_card.js +++ b/addons/point_of_sale/static/src/app/components/product_card/product_card.js @@ -14,6 +14,7 @@ export class ProductCard extends Component { onClick: { type: Function, optional: true }, onProductInfoClick: { type: Function, optional: true }, showWarning: { type: Boolean, optional: true }, + productCartQty: { type: [Number, undefined], optional: true }, }; static defaultProps = { onClick: () => {}, diff --git a/addons/point_of_sale/static/src/app/components/product_card/product_card.xml b/addons/point_of_sale/static/src/app/components/product_card/product_card.xml index 54f445b6f7948..07b0c31406783 100644 --- a/addons/point_of_sale/static/src/app/components/product_card/product_card.xml +++ b/addons/point_of_sale/static/src/app/components/product_card/product_card.xml @@ -14,11 +14,14 @@
-
-
+
+

diff --git a/addons/point_of_sale/static/src/app/models/pos_order.js b/addons/point_of_sale/static/src/app/models/pos_order.js index a132793b13b88..0641d36a3c116 100644 --- a/addons/point_of_sale/static/src/app/models/pos_order.js +++ b/addons/point_of_sale/static/src/app/models/pos_order.js @@ -85,6 +85,10 @@ export class PosOrder extends Base { return this.state !== "draft"; } + get totalQuantity() { + return this.lines.reduce((sum, line) => sum + line.getQuantity(), 0); + } + get isUnsyncedPaid() { return this.finalized && typeof this.id === "string"; } diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js index 0447289835c05..d5e8e50cf0bef 100644 --- a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js +++ b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js @@ -3,7 +3,7 @@ import { useService } from "@web/core/utils/hooks"; import { useBarcodeReader } from "@point_of_sale/app/hooks/barcode_reader_hook"; import { _t } from "@web/core/l10n/translation"; import { usePos } from "@point_of_sale/app/hooks/pos_hook"; -import { Component, onMounted, useState, reactive, onWillRender } from "@odoo/owl"; +import { Component, onMounted, useEffect, useState, reactive, onWillRender } from "@odoo/owl"; import { CategorySelector } from "@point_of_sale/app/components/category_selector/category_selector"; import { Input } from "@point_of_sale/app/components/inputs/input/input"; import { @@ -52,6 +52,7 @@ export class ProductScreen extends Component { this.state = useState({ previousSearchWord: "", currentOffset: 0, + quantityByProductTmplId: {}, }); onMounted(() => { this.pos.openOpeningControl(); @@ -84,6 +85,18 @@ export class ProductScreen extends Component { this.numberBuffer.use({ useWithBarcode: true, }); + + useEffect( + () => { + this.state.quantityByProductTmplId = this.currentOrder?.lines?.reduce((acc, ol) => { + acc[ol.product_id.product_tmpl_id.id] + ? (acc[ol.product_id.product_tmpl_id.id] += ol.qty) + : (acc[ol.product_id.product_tmpl_id.id] = ol.qty); + return acc; + }, {}); + }, + () => [this.currentOrder.totalQuantity] + ); } getNumpadButtons() { diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.xml b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.xml index 727d3b69cd20c..85afa17efbfe5 100644 --- a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.xml +++ b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.xml @@ -37,6 +37,7 @@ imageUrl="pos.config.show_product_images and this.getProductImage(product)" onClick.bind="() => this.addProductToOrder(product)" productInfo="true" + productCartQty="this.state.quantityByProductTmplId[product.id]" onProductInfoClick.bind="() => this.onProductInfoClick(product)" />
diff --git a/addons/point_of_sale/static/tests/pos/tours/product_screen_tour.js b/addons/point_of_sale/static/tests/pos/tours/product_screen_tour.js index 0f51fceeabce1..65ffb90fab962 100644 --- a/addons/point_of_sale/static/tests/pos/tours/product_screen_tour.js +++ b/addons/point_of_sale/static/tests/pos/tours/product_screen_tour.js @@ -45,6 +45,12 @@ registry.category("web_tour.tours").add("ProductScreenTour", { ...ProductScreen.selectedOrderlineHasDirect("Desk Organizer", "123.0", "627.3"), ...[".", "5"].map(Numpad.click), ...ProductScreen.selectedOrderlineHasDirect("Desk Organizer", "123.5", "629.85"), + ]), + // Check effects of numpad on product card quantity + ProductScreen.productCardQtyIs("Desk Organizer", "123.5"), + inLeftSide([ + // Re-select the order line after switching to the product screen + { ...ProductScreen.clickLine("Desk Organizer", "123.5")[0], isActive: ["mobile"] }, Numpad.click("Price"), Numpad.isActive("Price"), Numpad.click("1"), diff --git a/addons/point_of_sale/static/tests/pos/tours/utils/product_screen_util.js b/addons/point_of_sale/static/tests/pos/tours/utils/product_screen_util.js index a368d7739c1a3..3f9f19b5a0e3a 100644 --- a/addons/point_of_sale/static/tests/pos/tours/utils/product_screen_util.js +++ b/addons/point_of_sale/static/tests/pos/tours/utils/product_screen_util.js @@ -87,6 +87,9 @@ export function clickDisplayedProduct( if (isCheckNeed) { step.push(...selectedOrderlineHas(name, nextQuantity, nextPrice)); } + if (isCheckNeed && nextQuantity) { + step.push(...productCardQtyIs(name, nextQuantity)); + } return step; } @@ -461,6 +464,16 @@ export function cashDifferenceIs(val) { }, ]; } +export function productCardQtyIs(productName, qty) { + qty = `${Number.parseFloat(Number.parseFloat(qty).toFixed(2))}`; + return [ + { + content: `'${productName}' should have '${qty}' quantity`, + trigger: `article.product .product-content:has(.product-name:contains("${productName}")):has(.product-cart-qty:contains("${qty}"))`, + }, + ]; +} + // Temporarily put it here. It should be in the utility methods for the backend views. export function lastClosingCashIs(val) { return [