From 8bbe9c8be3000bfaab7e096d01d1851b39a8cba9 Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Thu, 19 Oct 2023 11:50:08 +0200 Subject: [PATCH] [FIX] point_of_sale,pos_self_order: correct component prices In this commit, we are changing the way prices of the components of a combo product is calculated. The idea is that a combo product's price should behave similarly as normal products. This results to a simpler logic in calculating the prices of the components. In order to get the fraction that will be used to scale the prices of the component products, the lst_price of the combo product is divided by the total of the pos.combo.base_price. This ratio is then multiplied to each pos.combo.base_price to get the pro-rated unit price of each component. We then proceed to normal calculation of taxes based on the tax configuration of the selected products. Furthermore, we are now taking into account the extra prices from the selected attributes. If the orderline is a component of a combo (menu) product, the extra prices of the attributes are added on top of the prorated unit price, just like we do with the `pos.combo.line`'s `combo_price`. E.g.: ``` product.product.1 -> lst_price = 10 A1 -> price_extra = 5 A3 -> price_extra = 3 pos.order.line.1, product.product.1, attributes=[A1,A3] -> price_unit = 10 + 5 + 3 = 18 ``` One more thing, there is a regression in the combo configurator popup where the total price of the selection is removed. It should be restored in the future with calculation that properly follows the description above. closes odoo/odoo#139915 Signed-off-by: Adrien Guilliams (adgu) --- addons/point_of_sale/models/pos_session.py | 5 +- .../combo_configurator_popup.js | 9 -- .../combo_configurator_popup.xml | 10 +- .../static/src/app/store/models.js | 51 +++++----- .../static/tests/tours/PosComboTour.js | 17 ++-- .../tests/tours/SplitBillScreen.tour.js | 10 +- addons/pos_self_order/controllers/orders.py | 97 +++++++++---------- addons/pos_self_order/models/pos_config.py | 1 + .../attribute_selection.js | 19 ++++ .../attribute_selection.xml | 4 +- .../combo_selection/combo_selection.js | 5 + .../static/src/app/models/line.js | 2 + .../src/app/pages/combo_page/combo_page.js | 43 ++++---- 13 files changed, 144 insertions(+), 129 deletions(-) diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index e6e760cc440..0e28431a1a0 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -1638,7 +1638,10 @@ class PosSession(models.Model): for key, group in groupby(sorted(product_template_attribute_values, key=key1), key=key2): attribute_line_id, attribute = key values = [{**ptav.product_attribute_value_id.read(['name', 'is_custom', 'html_color', 'image'])[0], - 'price_extra': ptav.price_extra} for ptav in list(group)] + 'price_extra': ptav.price_extra, + # id of a value should be from the "product.template.attribute.value" record + 'id': ptav.id, + } for ptav in list(group)] res[attribute_line_id] = { 'id': attribute_line_id, 'name': attribute.name, diff --git a/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.js b/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.js index 47f6d9de5eb..88ae9ee194f 100644 --- a/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.js +++ b/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.js @@ -17,15 +17,6 @@ export class ComboConfiguratorPopup extends AbstractAwaitablePopup { }); } - getTotalPrice() { - const selectedComponents = this.getPayload(); - const extraPrice = selectedComponents.reduce((acc, x) => { - const product = this.pos.db.product_by_id[x.product_id[0]]; - return acc + product.get_display_price({ price: x.combo_price }); - }, 0); - return this.props.product.lst_price + extraPrice; - } - areAllCombosSelected() { return Object.values(this.state.combo).every((x) => Boolean(x)); } diff --git a/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.xml b/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.xml index 14e949998f9..ecf2bd62f5e 100644 --- a/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.xml +++ b/addons/point_of_sale/static/src/app/store/combo_configurator_popup/combo_configurator_popup.xml @@ -34,16 +34,20 @@
+ -
- Total +
+ + + Complete the selection to proceed +
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 2a87673ee7f..818bb73170a 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -2027,49 +2027,44 @@ export class Order extends PosModel { } } async addComboLines(comboParent, options) { - const originalPrices = {}; - - let originalTotal = 0; - for (const comboLine of options.comboLines) { - const product = this.pos.db.product_by_id[comboLine.product_id[0]]; + const parentLstPrice = comboParent.product.lst_price; + const originalTotal = options.comboLines.reduce((acc, comboLine) => { const originalPrice = this.pos.db.combo_by_id[comboLine.combo_id[0]].base_price; - originalTotal += product.get_display_price({ price: originalPrice }); + return acc + originalPrice; + }, 0); - // Keep track of the original price of each product for the subsequent for loop. - originalPrices[product.id] = originalPrice; - } + let remainingTotal = parentLstPrice; - const targetPrice = comboParent.product.lst_price; - const childPriceFactor = targetPrice / originalTotal; - for (const comboLine of options.comboLines) { + for (let i = 0; i < options.comboLines.length; i++) { + const comboLine = options.comboLines[i]; const product = this.pos.db.product_by_id[comboLine.product_id[0]]; - const childUnitPrice = originalPrices[product.id] * childPriceFactor; + const combo = this.pos.db.combo_by_id[comboLine.combo_id[0]]; + let priceUnit = round_di( + (combo.base_price * parentLstPrice) / originalTotal, + this.pos.dp["Product Price"] + ); + remainingTotal -= priceUnit; + if (i == options.comboLines.length - 1) { + priceUnit += remainingTotal; + } await this.pos.addProductToCurrentOrder(product, { - price: childUnitPrice, + price: priceUnit, comboParent, }); } - // Adjust the price of the last combo line to make sure the total price of the combo - // is the same as the target price. + // Take into account the extra prices. const childLines = this.get_orderlines().filter( (l) => l.comboParent?.uuid === comboParent.uuid ); - const totalComboPrice = childLines.reduce((acc, l) => acc + l.get_display_price(), 0); - const diff = targetPrice - totalComboPrice; - if (!this.env.utils.floatIsZero(diff)) { - const lastLine = childLines[childLines.length - 1]; - lastLine.set_unit_price(lastLine.get_unit_price() + diff); - } - - // Finally, take into account the combo price for each combo line. for (const comboLine of options.comboLines) { - if (this.env.utils.floatIsZero(comboLine.combo_price)) { - continue; - } const presentLine = childLines.find((l) => l.product.id === comboLine.product_id[0]); if (presentLine) { - presentLine.set_unit_price(presentLine.get_unit_price() + comboLine.combo_price); + const attributesPriceExtra = (presentLine.attribute_value_ids ?? []) + .map((id) => this.pos.db.attribute_value_by_id[id]?.price_extra || 0) + .reduce((acc, price) => acc + price, 0); + const totalPriceExtra = attributesPriceExtra + comboLine.combo_price; + presentLine.set_unit_price(presentLine.get_unit_price() + totalPriceExtra); } } } diff --git a/addons/point_of_sale/static/tests/tours/PosComboTour.js b/addons/point_of_sale/static/tests/tours/PosComboTour.js index 5004ccbd14b..3b1e7c309d3 100644 --- a/addons/point_of_sale/static/tests/tours/PosComboTour.js +++ b/addons/point_of_sale/static/tests/tours/PosComboTour.js @@ -34,19 +34,14 @@ registry.category("web_tour.tours").add("PosComboPriceTaxIncludedTour", { combo.select("Combo Product 8"), combo.isSelected("Combo Product 8"), combo.isNotSelected("Combo Product 7"), - { - content: "check that the total amount is properly displayed", - trigger: `.combo-configurator-popup .total-amount:contains("59.60")`, - isCheck: true, - }, combo.confirm(), ...ProductScreen.selectedOrderlineHas("Office Combo"), ...ProductScreen.clickOrderline("Combo Product 3"), - ...ProductScreen.selectedOrderlineHas("Combo Product 3", "1.0", "12.92"), + ...ProductScreen.selectedOrderlineHas("Combo Product 3", "1.0", "13.43"), ...ProductScreen.clickOrderline("Combo Product 5"), - ...ProductScreen.selectedOrderlineHas("Combo Product 5", "1.0", "17.87"), + ...ProductScreen.selectedOrderlineHas("Combo Product 5", "1.0", "18.67"), ...ProductScreen.clickOrderline("Combo Product 8"), - ...ProductScreen.selectedOrderlineHas("Combo Product 8", "1.0", "28.81"), + ...ProductScreen.selectedOrderlineHas("Combo Product 8", "1.0", "30.00"), // check that you cannot change the quantity of a combo product ...ProductScreen.pressNumpad("2"), @@ -61,7 +56,7 @@ registry.category("web_tour.tours").add("PosComboPriceTaxIncludedTour", { combo.select("Combo Product 5"), combo.select("Combo Product 8"), combo.confirm(), - ...ProductScreen.totalAmountIs("59.60"), + ...ProductScreen.totalAmountIs("62.10"), ...ProductScreen.clickPayButton(), ...PaymentScreen.clickPaymentMethod("Bank"), ...PaymentScreen.clickValidate(), @@ -74,8 +69,8 @@ registry.category("web_tour.tours").add("PosComboPriceTaxIncludedTour", { combo.select("Combo Product 4"), combo.select("Combo Product 6"), combo.confirm(), - ...ProductScreen.totalAmountIs("50.00"), - ...inLeftSide(Order.hasTax("8.92")), + ...ProductScreen.totalAmountIs("59.17"), + ...inLeftSide(Order.hasTax("10.56")), // the split screen is tested in `pos_restaurant` ], }); diff --git a/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js b/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js index 9b80d95fcd1..5822d93b436 100644 --- a/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js +++ b/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js @@ -170,7 +170,7 @@ registry.category("web_tour.tours").add("SplitBillScreenTour4PosCombo", { ...SplitBillScreen.orderlineHas("Combo Product 4", "1", "0"), ...SplitBillScreen.orderlineHas("Combo Product 7", "1", "0"), - ...SplitBillScreen.subtotalIs("52.13"), + ...SplitBillScreen.subtotalIs("54.13"), ...SplitBillScreen.clickPay(), ...PaymentScreen.clickPaymentMethod("Bank"), ...PaymentScreen.clickValidate(), @@ -181,11 +181,11 @@ registry.category("web_tour.tours").add("SplitBillScreenTour4PosCombo", { ...ProductScreen.selectedOrderlineHas("Minute Maid", "1.0"), ...ProductScreen.clickOrderline("Office Combo"), ...ProductScreen.clickOrderline("Combo Product 2"), - ...ProductScreen.selectedOrderlineHas("Combo Product 2", "1.0", "6.15", "Office Combo"), + ...ProductScreen.selectedOrderlineHas("Combo Product 2", "1.0", "6.67", "Office Combo"), ...ProductScreen.clickOrderline("Combo Product 4"), - ...ProductScreen.selectedOrderlineHas("Combo Product 4", "1.0", "13.54", "Office Combo"), + ...ProductScreen.selectedOrderlineHas("Combo Product 4", "1.0", "14.66", "Office Combo"), ...ProductScreen.clickOrderline("Combo Product 7"), - ...ProductScreen.selectedOrderlineHas("Combo Product 7", "1.0", "20.31", "Office Combo"), - ...ProductScreen.totalAmountIs("42.53"), + ...ProductScreen.selectedOrderlineHas("Combo Product 7", "1.0", "22.00", "Office Combo"), + ...ProductScreen.totalAmountIs("45.86"), ], }); diff --git a/addons/pos_self_order/controllers/orders.py b/addons/pos_self_order/controllers/orders.py index 0e0eb1ab321..36f05b868ba 100644 --- a/addons/pos_self_order/controllers/orders.py +++ b/addons/pos_self_order/controllers/orders.py @@ -4,6 +4,7 @@ import uuid from datetime import timedelta from odoo import http, fields from odoo.http import request +from odoo.tools import float_round from werkzeug.exceptions import NotFound, BadRequest, Unauthorized class PosSelfOrderController(http.Controller): @@ -202,57 +203,57 @@ class PosSelfOrderController(http.Controller): appended_uuid = [] newLines = [] pricelist = pos_config.pricelist_id - if take_away and pos_config.self_ordering_mode == 'kiosk': - config_fiscal_pos = pos_config.self_ordering_alternative_fp_id - else: - config_fiscal_pos = pos_config.default_fiscal_position_id + sale_price_digits = pos_config.env['decimal.precision'].precision_get('Product Price') + + combo_line_ids = [line['combo_line_id'] for line in lines if line.get('combo_line_id')] + combo_lines = pos_config.env['pos.combo.line'].search([('id', 'in', combo_line_ids)]) + attribute_value_ids = sum([line.get('attribute_value_ids', []) for line in lines], []) + fetched_attributes = pos_config.env['product.template.attribute.value'].search([('id', 'in', attribute_value_ids)]) + + fiscal_pos = pos_config.default_fiscal_position_id + + if take_away and pos_config.self_ordering_alternative_fp_id: + fiscal_pos = pos_config.self_ordering_alternative_fp_id for line in lines: if line.get('uuid') in appended_uuid or not line.get('product_id'): continue + line_qty = line.get('qty') product = pos_config.env['product.product'].browse(int(line.get('product_id'))) - # todo take into account the price extra - price_unit = pricelist._get_product_price(product, quantity=line.get('qty')) if pricelist else product.lst_price - selected_account_tax = config_fiscal_pos.map_tax(product.taxes_id) if config_fiscal_pos else product.taxes_id + lst_price = pricelist._get_product_price(product, quantity=line_qty) if pricelist else product.lst_price - # parent_product_taxe_ids = None children = [l for l in lines if l.get('combo_parent_uuid') == line.get('uuid')] + pos_combo_lines = combo_lines.browse([child.get('combo_line_id') for child in children]) + if len(children) > 0: - total_price = 0 - unit_price_by_id = {} - for child in children: - product = pos_config.env['product.product'].browse(int(child.get('product_id'))) - child_selected_account_tax = config_fiscal_pos.map_tax(product.taxes_id) if config_fiscal_pos else product.taxes_id - tax_results = child_selected_account_tax.compute_all( - pricelist._get_product_price(product, quantity=line.get('qty')) if pricelist else product.lst_price, - pos_config.currency_id, - child.get('qty'), - product, - ) - unit_price_by_id[child['uuid']] = pricelist._get_product_price(product, quantity=line.get('qty')) if pricelist else product.lst_price - total_price += tax_results.get('total_included') if pos_config.iface_tax_included == 'total' else tax_results.get('total_excluded') - ratio = (price_unit * line.get('qty') / total_price) - for child in children: - child_line_combo = pos_config.env['pos.combo'].browse(int(child.get('combo_id'))) - child_line_combo_line = child_line_combo.combo_line_ids.filtered(lambda l: l.product_id.id == child.get('product_id'))[0] - child_product = pos_config.env["product.product"].browse(int(child.get('product_id'))) - child_price_unit = ratio * unit_price_by_id[child['uuid']] + child_line_combo_line.combo_price - child_selected_account_tax = config_fiscal_pos.map_tax(child_product.taxes_id) if config_fiscal_pos else child_product.taxes_id - child_tax_results = child_selected_account_tax.compute_all( - child_price_unit, - pos_config.currency_id, - child.get('qty'), - child_product, - ) + original_total = sum(pos_combo_lines.mapped("combo_id.base_price")) + remaining_total = lst_price + factor = lst_price / original_total + + for i, child in enumerate(children): + child_product = pos_config.env['product.product'].browse(int(child.get('product_id'))) + pos_combo_line = pos_combo_lines.browse(child.get('combo_line_id')) + price_unit = float_round(pos_combo_line.combo_id.base_price * factor, precision_digits=sale_price_digits) + remaining_total -= price_unit + if i == len(children) - 1: + price_unit += remaining_total + + selected_attributes = fetched_attributes.browse(child.get('attribute_value_ids', [])) + price_unit += pos_combo_line.combo_price + sum([attr.price_extra for attr in selected_attributes]) + + price_unit_fp = child_product._get_price_unit_after_fp(price_unit, pos_config.currency_id, fiscal_pos) + taxes = fiscal_pos.map_tax(child_product.taxes_id) if fiscal_pos else child_product.taxes_id + pdetails = taxes.compute_all(price_unit_fp, pos_config.currency_id, line_qty, child_product) + newLines.append({ - 'price_unit': child_price_unit, - 'price_subtotal': child_tax_results.get('total_excluded'), - 'price_subtotal_incl': child_tax_results.get('total_included'), + 'price_unit': price_unit_fp, + 'price_subtotal': pdetails.get('total_excluded'), + 'price_subtotal_incl': pdetails.get('total_included'), 'custom_attribute_value_ids': [[0, 0, cAttr] for cAttr in child.get('custom_attribute_value_ids')] if child.get('custom_attribute_value_ids') else [], 'id': child.get('id'), 'order_id': pos_order_id, - 'tax_ids': product.taxes_id, + 'tax_ids': child_product.taxes_id, 'uuid': child.get('uuid'), 'product_id': child.get('product_id'), 'qty': child.get('qty'), @@ -263,25 +264,23 @@ class PosSelfOrderController(http.Controller): 'combo_id': child.get('combo_id'), }) appended_uuid.append(child.get('uuid')) - price_unit = 0 - tax_results = selected_account_tax.compute_all( - price_unit, - pos_config.currency_id, - line.get('qty'), - product, - ) + lst_price = 0 + + price_unit_fp = product._get_price_unit_after_fp(lst_price, pos_config.currency_id, fiscal_pos) + taxes_after_fp = fiscal_pos.map_tax(product.taxes_id) if fiscal_pos else product.taxes_id + pdetails = taxes_after_fp.compute_all(price_unit_fp, pos_config.currency_id, line_qty, product) newLines.append({ - 'price_unit': price_unit, - 'price_subtotal': tax_results.get('total_excluded'), - 'price_subtotal_incl': tax_results.get('total_included'), + 'price_unit': price_unit_fp, + 'price_subtotal': pdetails.get('total_excluded'), + 'price_subtotal_incl': pdetails.get('total_included'), 'id': line.get('id'), 'order_id': pos_order_id, 'tax_ids': product.taxes_id, 'uuid': line.get('uuid'), 'product_id': line.get('product_id'), - 'qty': line.get('qty'), + 'qty': line_qty, 'customer_note': line.get('customer_note'), 'attribute_value_ids': line.get('attribute_value_ids') or [], 'custom_attribute_value_ids': [[0, 0, cAttr] for cAttr in line.get('custom_attribute_value_ids')] if line.get('custom_attribute_value_ids') else [], diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py index 206c9d3d831..fca890f5ed9 100644 --- a/addons/pos_self_order/models/pos_config.py +++ b/addons/pos_self_order/models/pos_config.py @@ -348,6 +348,7 @@ class PosConfig(models.Model): "custom_links": self._get_self_order_custom_links(), "currency_id": self.currency_id.id, "pos_payment_methods": payment_methods if self.self_ordering_mode == "kiosk" else [], + "currency_decimals": self.currency_id.decimal_places, "pos_category": self._get_available_categories().read(["name", "sequence", "has_image"]), "products": self._get_available_products()._get_self_order_data(self), "combos": self._get_combos_data(), diff --git a/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.js b/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.js index 41871d96c9f..dd562a4b466 100644 --- a/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.js +++ b/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.js @@ -4,6 +4,7 @@ import { Component, useState } from "@odoo/owl"; import { ProductCustomAttribute } from "@point_of_sale/app/store/models/product_custom_attribute"; import { useSelfOrder } from "@pos_self_order/app/self_order_service"; import { attributeFlatter, attributeFormatter } from "@pos_self_order/app/utils"; +import { floatIsZero } from "@web/core/utils/numbers"; export class AttributeSelection extends Component { static template = "pos_self_order.AttributeSelection"; @@ -90,4 +91,22 @@ export class AttributeSelection extends Component { ? this.selectedValues[attribute.id][value.id] : parseInt(this.selectedValues[attribute.id]) === value.id; } + + _getPriceExtra(value) { + const isTakeAway = this.selfOrder.take_away; + const priceExtra = isTakeAway + ? value.price_extra.display_price_default + : value.price_extra.display_price_alternative; + return priceExtra; + } + + shouldShowPriceExtra(value) { + const priceExtra = this._getPriceExtra(value); + return !floatIsZero(priceExtra, this.selfOrder.config.currency_decimals); + } + + getfPriceExtra(value) { + const priceExtra = this._getPriceExtra(value); + return this.selfOrder.formatMonetary(priceExtra); + } } diff --git a/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.xml b/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.xml index 5b2eb352110..b88903f6db4 100644 --- a/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.xml +++ b/addons/pos_self_order/static/src/app/components/attribute_selection/attribute_selection.xml @@ -14,8 +14,8 @@ d-flex align-items-center justify-content-center h-100 rounded border ratio ratio-16x9">
- - + + + +
diff --git a/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.js b/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.js index 35a06ac7b37..42c2d73e517 100644 --- a/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.js +++ b/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.js @@ -14,6 +14,11 @@ export class ComboSelection extends Component { } productClicked(lineId) { + // Keep track of the current combo line id. + // It servers as additional info for each line so that when calculating prices, + // no need to look for the specific combo line the product belongs to. + this.env.currentComboLineId.value = lineId; + const comboLine = this.props.combo.combo_line_ids.find((line) => line.id == lineId); const productSelected = this.selfOrder.productByIds[comboLine.product_id[0]]; if (!productSelected.self_order_available) { diff --git a/addons/pos_self_order/static/src/app/models/line.js b/addons/pos_self_order/static/src/app/models/line.js index be1f3d0dd74..80135d4d600 100644 --- a/addons/pos_self_order/static/src/app/models/line.js +++ b/addons/pos_self_order/static/src/app/models/line.js @@ -18,6 +18,7 @@ export class Line extends Reactive { custom_attribute_value_ids, combo_parent_uuid, combo_id, + combo_line_id, child_lines, }) { super(); @@ -39,6 +40,7 @@ export class Line extends Reactive { this.custom_attribute_value_ids = line.custom_attribute_value_ids || []; this.combo_parent_uuid = line.combo_parent_uuid || null; this.combo_id = line.combo_id || null; + this.combo_line_id = line.combo_line_id || null; this.child_lines = line.child_lines || []; this.initCustomAttribute(); diff --git a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js index 98b74043b55..319a3b84e7c 100644 --- a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js +++ b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js @@ -17,7 +17,14 @@ export class ComboPage extends Component { this.selfOrder = useSelfOrder(); this.selfOrder.lastEditedProductId = this.props.product.id; this.router = useService("router"); - useSubEnv({ selectedValues: {}, customValues: {}, editable: this.editableProductLine }); + useSubEnv({ + selectedValues: {}, + customValues: {}, + editable: this.editableProductLine, + currentComboLineId: { + value: null, + }, + }); if (!this.props.product) { this.router.navigate("productList"); @@ -79,28 +86,21 @@ export class ComboPage extends Component { next() { const combo = this.currentCombo; const index = this.state.selectedCombos.findIndex((c) => c.id === combo.id); + const selectedCombo = { + id: combo.id, + name: combo.name, + combo_line_id: this.env.currentComboLineId.value, + product: { + id: this.state.selectedProduct.id, + name: this.state.selectedProduct.name, + variants: { ...this.env.selectedValues }, + customValues: { ...this.env.customValues }, + }, + }; if (index !== -1) { - this.state.selectedCombos[index] = { - id: combo.id, - name: combo.name, - product: { - id: this.state.selectedProduct.id, - name: this.state.selectedProduct.name, - variants: { ...this.env.selectedValues }, - customValues: { ...this.env.customValues }, - }, - }; + this.state.selectedCombos[index] = selectedCombo; } else { - this.state.selectedCombos.push({ - id: combo.id, - name: combo.name, - product: { - id: this.state.selectedProduct.id, - name: this.state.selectedProduct.name, - variants: { ...this.env.selectedValues }, - customValues: { ...this.env.customValues }, - }, - }); + this.state.selectedCombos.push(selectedCombo); } this.resetState(); if (this.state.editMode) { @@ -156,6 +156,7 @@ export class ComboPage extends Component { custom_attribute_value_ids: Object.values(combo.product.customValues), combo_parent_uuid: parent_line.uuid, combo_id: combo.id, + combo_line_id: combo.combo_line_id, }); child_line.full_product_name = constructFullProductName( child_line,