diff --git a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml index 0c34a98d75a..06498e94a67 100644 --- a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml +++ b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml @@ -36,7 +36,7 @@
  • - +
    : @@ -46,7 +46,7 @@
    -
    +
  • diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index 4da50821fcf..b9993c52532 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { random5Chars, uuidv4, qrCodeSrc, constructFullProductName } from "@point_of_sale/utils"; +import { random5Chars, uuidv4, qrCodeSrc } from "@point_of_sale/utils"; // FIXME POSREF - unify use of native parseFloat and web's parseFloat. We probably don't need the native version. import { parseFloat as oParseFloat } from "@web/views/fields/parsers"; import { @@ -574,11 +574,7 @@ export class Orderline extends PosModel { this.price_extra = parseFloat(price_extra) || 0.0; } set_full_product_name() { - this.full_product_name = constructFullProductName( - this, - this.pos.db.attribute_value_by_id, - this.product.display_name - ); + this.full_product_name = this.product.display_name; } get_price_extra() { return this.price_extra; @@ -738,7 +734,10 @@ export class Orderline extends PosModel { orderline.compute_fixed_price(order_line_price), this.pos.currency.decimal_places ); - // only orderlines of the same product can be merged + let hasSameAttributes = Object.keys(Object(orderline.attribute_value_ids)).length === Object.keys(Object(this.attribute_value_ids)).length; + if(hasSameAttributes && Object(orderline.attribute_value_ids)?.length && Object(this.attribute_value_ids)?.length) { + hasSameAttributes = orderline.attribute_value_ids.every((value, index) => value === this.attribute_value_ids[index]); + } return ( !this.skipChange && orderline.getNote() === this.getNote() && @@ -759,7 +758,8 @@ export class Orderline extends PosModel { orderline.get_customer_note() === this.get_customer_note() && !this.refunded_orderline_id && !this.isPartOfCombo() && - !orderline.isPartOfCombo() + !orderline.isPartOfCombo() && + hasSameAttributes ); } is_pos_groupable() { @@ -1077,13 +1077,18 @@ export class Orderline extends PosModel { return Boolean(this.comboParent || this.comboLines?.length); } findAttribute(values) { - const listOfAttributes = Object.values(this.pos.attributes_by_ptal_id).filter( + const listOfAttributes = []; + Object.values(this.pos.attributes_by_ptal_id).filter( (attribute) => { const attFound = attribute.values.filter((target) => { return Object.values(values).includes(target.id); }); if (attFound.length > 0) { - attribute.valuesForOrderLine = attFound; + const modifiedAttribute = { + ...attribute, + valuesForOrderLine: attFound, + }; + listOfAttributes.push(modifiedAttribute); return true; } return false; diff --git a/addons/point_of_sale/static/src/utils.js b/addons/point_of_sale/static/src/utils.js index d00254c19c3..1bad27f7ef0 100644 --- a/addons/point_of_sale/static/src/utils.js +++ b/addons/point_of_sale/static/src/utils.js @@ -29,10 +29,8 @@ export function deduceUrl(url) { } return url; } - export function constructFullProductName(line, attribute_value_by_id, display_name) { let attributeString = ""; - if (line.attribute_value_ids && line.attribute_value_ids.length > 0) { for (const valId of line.attribute_value_ids) { const value = attribute_value_by_id[valId]; diff --git a/addons/point_of_sale/static/tests/tours/ProductConfigurator.tour.js b/addons/point_of_sale/static/tests/tours/ProductConfigurator.tour.js index e7fbee1ca0b..ae872b8502d 100644 --- a/addons/point_of_sale/static/tests/tours/ProductConfigurator.tour.js +++ b/addons/point_of_sale/static/tests/tours/ProductConfigurator.tour.js @@ -4,6 +4,8 @@ import * as ProductScreen from "@point_of_sale/../tests/tours/helpers/ProductScr import * as Chrome from "@point_of_sale/../tests/tours/helpers/ChromeTourMethods"; import * as ProductConfigurator from "@point_of_sale/../tests/tours/helpers/ProductConfiguratorTourMethods"; import { registry } from "@web/core/registry"; +import * as Order from "@point_of_sale/../tests/tours/helpers/generic_components/OrderWidgetMethods"; +import { inLeftSide } from "@point_of_sale/../tests/tours/helpers/utils"; registry.category("web_tour.tours").add("ProductConfiguratorTour", { test: true, @@ -43,7 +45,7 @@ registry.category("web_tour.tours").add("ProductConfiguratorTour", { // Check that the product has been added to the order with correct attributes and price ProductScreen.selectedOrderlineHas( - "Configurable Chair (Red, Metal, Other: Custom Fabric)", + "Configurable Chair", "1.0", "11.0" ), @@ -56,12 +58,18 @@ registry.category("web_tour.tours").add("ProductConfiguratorTour", { ProductConfigurator.pickRadio("Other"), ProductConfigurator.fillCustomAttribute("Custom Fabric"), ProductConfigurator.confirmAttributes(), - ProductScreen.selectedOrderlineHas( - "Configurable Chair (Red, Metal, Other: Custom Fabric)", - "2.0", - "22.0" + inLeftSide(Order.hasLine({ + withClass: ".selected", + productName: "Configurable Chair", + quantity: "2", + price: "22.0", + atts: { + "Color": "Red ($ 1.00)", + "Chair Legs": "Metal", + "Fabrics": "Other" + } + }) ), - // Orderlines with different attributes shouldn't be merged ProductScreen.clickHomeCategory(), ProductScreen.clickDisplayedProduct("Configurable Chair"), @@ -70,7 +78,7 @@ registry.category("web_tour.tours").add("ProductConfiguratorTour", { ProductConfigurator.pickRadio("Leather"), ProductConfigurator.confirmAttributes(), ProductScreen.selectedOrderlineHas( - "Configurable Chair (Blue, Metal, Leather)", + "Configurable Chair", "1.0", "10.0" ), diff --git a/addons/point_of_sale/static/tests/tours/helpers/generic_components/OrderWidgetMethods.js b/addons/point_of_sale/static/tests/tours/helpers/generic_components/OrderWidgetMethods.js index c84bacc1906..2c18fb904f9 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/generic_components/OrderWidgetMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/generic_components/OrderWidgetMethods.js @@ -31,6 +31,7 @@ export function hasLine({ comboParent, discount, oldPrice, + atts } = {}) { let trigger = `.order-container .orderline${withClass}`; if (withoutClass) { @@ -60,6 +61,9 @@ export function hasLine({ if (oldPrice) { trigger += `:has(.info-list .price-per-unit s:contains("${oldPrice}"))`; } + if(atts) { + trigger += Object.entries(atts).map(([key, value]) => `:has(.info-list div:contains("${key}: ${value}"))`).join(); + } const args = JSON.stringify(arguments[0]); return [ { diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index c54ee11ce05..d1afed74432 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -570,7 +570,6 @@ class TestUi(TestPointOfSaleHttpCommon): self.assertEqual(n_paid, 2, 'There should be 2 paid order.') def test_04_product_configurator(self): - self.main_pos_config.with_user(self.pos_user).open_ui() self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config, 'ProductConfiguratorTour', login="pos_user")