[FIX] point_of_sale: display attributes
In the current system, the order widget experienced undesired updates to all order lines when a new product with the same attribute was added. Our recent changes address this issue, ensuring that existing attribute values remain unaffected by the addition of new ones. Moreover, we've refined the display of attribute information. If an attribute is designated as "never," it will now be incorporated into the order line note. Conversely, attributes with different settings will continue to be displayed in their usual format. closes odoo/odoo#152213 Signed-off-by: Vlad Stroia (vlst) <vlst@odoo.com>
This commit is contained in:
@@ -36,7 +36,7 @@
|
||||
<t t-esc="line.internalNote" />
|
||||
</li>
|
||||
<li t-if="line.attributes">
|
||||
<t t-foreach="line.attributes" t-as="attribute" t-key="attribute.id">
|
||||
<div t-foreach="line.attributes" t-as="attribute" t-key="attribute.id">
|
||||
<t t-esc="attribute.name"/>:
|
||||
<t t-foreach="attribute.valuesForOrderLine" t-as="value" t-key="value.id">
|
||||
<t t-if="value_index !== 0" t-esc="' | '"/>
|
||||
@@ -46,7 +46,7 @@
|
||||
</t>
|
||||
|
||||
</t><br/>
|
||||
</t>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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"
|
||||
),
|
||||
|
||||
+4
@@ -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 [
|
||||
{
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user