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