diff --git a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.js b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.js
index 154d6da1d51..69f5e5119a3 100644
--- a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.js
+++ b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.js
@@ -10,7 +10,9 @@ export class ProductCard extends Component {
productId: Number,
price: String,
imageUrl: String,
+ productInfo: { Boolean, optional: true },
onClick: { type: Function, optional: true },
+ onProductInfoClick: { type: Function, optional: true },
};
static defaultProps = {
onClick: () => {},
diff --git a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.scss b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.scss
index 19c5280dbb5..b19c44f804b 100644
--- a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.scss
+++ b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.scss
@@ -24,3 +24,22 @@
box-shadow: 0 0 0 2px $primary;
}
}
+
+.product-information-tag {
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 0 30px 30px 0;
+ border-color: transparent #9a9ea180 transparent transparent;
+ position: absolute;
+ top: 0;
+ right: 0;
+ color: white;
+ text-align: center;
+}
+
+.product-information-tag-logo {
+ position: absolute;
+ left: 19px;
+ top: 3px;
+}
diff --git a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.xml b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.xml
index 5f3eb855e4c..843acf436b0 100644
--- a/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.xml
+++ b/addons/point_of_sale/static/src/app/generic_components/product_card/product_card.xml
@@ -8,6 +8,9 @@
t-on-click="props.onClick"
t-att-data-product-id="props.productId"
t-attf-aria-labelledby="article_product_{{props.productId}}">
+
+
+
diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.js b/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.js
deleted file mode 100644
index cc93848f3c8..00000000000
--- a/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/** @odoo-module */
-
-import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
-import { useService } from "@web/core/utils/hooks";
-import { ProductInfoPopup } from "@point_of_sale/app/screens/product_screen/product_info_popup/product_info_popup";
-import { Component } from "@odoo/owl";
-import { usePos } from "@point_of_sale/app/store/pos_hook";
-
-export class ProductInfoButton extends Component {
- static template = "point_of_sale.ProductInfoButton";
-
- setup() {
- this.pos = usePos();
- this.popup = useService("popup");
- this.pos = usePos();
- }
- async click() {
- const orderline = this.pos.get_order().get_selected_orderline();
- if (orderline) {
- const product = orderline.get_product();
- const quantity = orderline.get_quantity();
- const info = await this.pos.getProductInfo(product, quantity);
- this.popup.add(ProductInfoPopup, { info: info, product: product });
- }
- }
-}
-
-ProductScreen.addControlButton({
- component: ProductInfoButton,
- position: ["before", "SetFiscalPositionButton"],
-});
diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.xml b/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.xml
deleted file mode 100644
index 3839099b41f..00000000000
--- a/addons/point_of_sale/static/src/app/screens/product_screen/control_buttons/product_info_button/product_info_button.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/product_info_popup/product_info_popup.xml b/addons/point_of_sale/static/src/app/screens/product_screen/product_info_popup/product_info_popup.xml
index 162e203344c..639eb4823a3 100644
--- a/addons/point_of_sale/static/src/app/screens/product_screen/product_info_popup/product_info_popup.xml
+++ b/addons/point_of_sale/static/src/app/screens/product_screen/product_info_popup/product_info_popup.xml
@@ -4,9 +4,12 @@
diff --git a/addons/point_of_sale/views/product_view.xml b/addons/point_of_sale/views/product_view.xml
index 8c4e19b68c6..c8b903db57e 100644
--- a/addons/point_of_sale/views/product_view.xml
+++ b/addons/point_of_sale/views/product_view.xml
@@ -5,6 +5,9 @@
product.template
+
+
+
@@ -173,6 +176,9 @@
+
+
+
diff --git a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.js b/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.js
deleted file mode 100644
index 7b50262374e..00000000000
--- a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/** @odoo-module */
-
-import { ProductInfoButton } from "@point_of_sale/app/screens/product_screen/control_buttons/product_info_button/product_info_button";
-import { patch } from "@web/core/utils/patch";
-
-patch(ProductInfoButton.prototype, {
- hasOptionalProduct() {
- const orderline = this.pos.get_order().get_selected_orderline();
- if (orderline) {
- const optionalProductIds = orderline.product.optional_product_ids;
- return Object.values(this.pos.db.product_by_id).find((p) =>
- optionalProductIds.includes(p.product_tmpl_id)
- );
- }
- return false;
- },
-});
diff --git a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.xml b/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.xml
deleted file mode 100644
index d40f72e697b..00000000000
--- a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_button/product_info_button.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- { 'highlight text-bg-primary': this.hasOptionalProduct() }
-
-
-
diff --git a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_popup/product_info_popup.xml b/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_popup/product_info_popup.xml
index 5ae5200e29c..75dd991c21f 100644
--- a/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_popup/product_info_popup.xml
+++ b/addons/pos_sale_product_configurator/static/src/overrides/components/product_info_popup/product_info_popup.xml
@@ -1,9 +1,9 @@
-
-
-
Optional Products:
+
+
+
Optional Products:
diff --git a/addons/pos_self_order/__manifest__.py b/addons/pos_self_order/__manifest__.py
index ad3eaa4707c..08fc8a2436b 100644
--- a/addons/pos_self_order/__manifest__.py
+++ b/addons/pos_self_order/__manifest__.py
@@ -18,6 +18,7 @@
"views/pos_config_view.xml",
"views/pos_session_view.xml",
"views/custom_link_views.xml",
+ "views/product_views.xml",
"data/init_access.xml",
"views/res_config_settings_views.xml",
"views/point_of_sale_dashboard.xml",
@@ -25,6 +26,9 @@
],
"assets": {
# Assets
+ 'point_of_sale._assets_pos': [
+ 'pos_self_order/static/src/overrides/**/*',
+ ],
'web.assets_backend': [
"pos_self_order/static/src/upgrade_selection_field.js",
],
diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py
index 94ffa3b7c64..3557271be10 100644
--- a/addons/pos_self_order/models/pos_config.py
+++ b/addons/pos_self_order/models/pos_config.py
@@ -261,6 +261,7 @@ class PosConfig(models.Model):
[
("id", "not in", product_ids.ids),
("available_in_pos", "=", True),
+ ("self_order_available", "=", True),
*(
self.limit_categories
and self.iface_available_categ_ids
diff --git a/addons/pos_self_order/models/pos_session.py b/addons/pos_self_order/models/pos_session.py
index f62d72baf88..6c61fa0a373 100644
--- a/addons/pos_self_order/models/pos_session.py
+++ b/addons/pos_self_order/models/pos_session.py
@@ -40,3 +40,8 @@ class PosSession(models.Model):
})
return sessions
+
+ def _loader_params_product_product(self):
+ res = super()._loader_params_product_product()
+ res['search_params']['fields'].append('self_order_available')
+ return res
diff --git a/addons/pos_self_order/models/product_product.py b/addons/pos_self_order/models/product_product.py
index 5cf496ce303..7bdd490a7a1 100644
--- a/addons/pos_self_order/models/product_product.py
+++ b/addons/pos_self_order/models/product_product.py
@@ -2,11 +2,33 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from typing import List, Dict, Optional
-from odoo import models
+from odoo import api, models, fields
from odoo.addons.point_of_sale.models.pos_config import PosConfig
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ self_order_available = fields.Boolean(
+ string="Available in Self Order",
+ help="If this product is available in the Self Order screens",
+ default=True,
+ )
+
+ @api.constrains('available_in_pos')
+ def _check_combo_inclusions(self):
+ super()._check_combo_inclusions()
+ self.self_order_available = False
+
+ def write(self, vals_list):
+ res = super().write(vals_list)
+ if 'self_order_available' in vals_list:
+ for record in self:
+ for product in record.product_variant_ids:
+ product._send_availability_status()
+ return res
+
class ProductProduct(models.Model):
_inherit = "product.product"
@@ -100,6 +122,7 @@ class ProductProduct(models.Model):
"pos_combo_ids": self.combo_ids.mapped("id") or False,
"is_pos_groupable": self.uom_id.is_pos_groupable,
"write_date": self.write_date.timestamp(),
+ "self_order_available": self.self_order_available,
}
def _get_self_order_data(self, pos_config: PosConfig) -> List[Dict]:
@@ -107,3 +130,18 @@ class ProductProduct(models.Model):
product._get_product_for_ui(pos_config)
for product in self
]
+
+ def write(self, vals_list):
+ res = super().write(vals_list)
+ if 'self_order_available' in vals_list:
+ for record in self:
+ record._send_availability_status()
+ return res
+
+ def _send_availability_status(self):
+ config_self = self.env['pos.config'].sudo().search([('self_ordering_mode', '!=', 'nothing')])
+ for config in config_self:
+ if config.current_session_id and config.access_token:
+ self.env['bus.bus']._sendone(f'pos_config-{config.access_token}', 'PRODUCT_CHANGED', {
+ 'product': self._get_product_for_ui(config)
+ })
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 e8c235cd09c..51d445b3bf5 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
@@ -16,6 +16,9 @@ export class ComboSelection extends Component {
productClicked(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) {
+ return;
+ }
this.props.comboState.selectedProduct = productSelected;
if (productSelected.attributes.length === 0) {
this.props.next();
diff --git a/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.xml b/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.xml
index bd504c6e48e..adf88c4aa94 100644
--- a/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.xml
+++ b/addons/pos_self_order/static/src/app/components/combo_selection/combo_selection.xml
@@ -26,6 +26,9 @@
+
+
diff --git a/addons/pos_self_order/static/src/app/components/product_card/product_card.js b/addons/pos_self_order/static/src/app/components/product_card/product_card.js
index 70e83261a58..95dd5d420e9 100644
--- a/addons/pos_self_order/static/src/app/components/product_card/product_card.js
+++ b/addons/pos_self_order/static/src/app/components/product_card/product_card.js
@@ -74,12 +74,12 @@ export class ProductCard extends Component {
}
async selectProduct() {
- if (!this.selfOrder.ordering) {
+ const product = this.props.product;
+
+ if (!this.selfOrder.ordering || !product.self_order_available) {
return;
}
- const product = this.props.product;
-
if (product.isCombo) {
this.router.navigate("combo_selection", { id: product.id });
} else if (product.attributes.length > 0) {
diff --git a/addons/pos_self_order/static/src/app/components/product_card/product_card.xml b/addons/pos_self_order/static/src/app/components/product_card/product_card.xml
index 1b3c486c719..9e540b82f45 100644
--- a/addons/pos_self_order/static/src/app/components/product_card/product_card.xml
+++ b/addons/pos_self_order/static/src/app/components/product_card/product_card.xml
@@ -12,8 +12,13 @@
loading="lazy"
onerror="this.remove()" />
diff --git a/addons/pos_self_order/static/src/app/models/product.js b/addons/pos_self_order/static/src/app/models/product.js
index 18dbbe3112b..0fe76657e44 100644
--- a/addons/pos_self_order/static/src/app/models/product.js
+++ b/addons/pos_self_order/static/src/app/models/product.js
@@ -14,6 +14,7 @@ export class Product extends Reactive {
pos_combo_ids,
is_pos_groupable,
write_date,
+ self_order_available,
},
showPriceTaxIncluded
) {
@@ -33,6 +34,7 @@ export class Product extends Reactive {
this.pos_combo_ids = product.pos_combo_ids;
this.is_pos_groupable = product.is_pos_groupable;
this.write_date = product.write_date;
+ this.self_order_available = product.self_order_available;
// data
this.showPriceTaxIncluded = showPriceTaxIncluded;
diff --git a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js
index 422926e1970..db59d6f0e77 100644
--- a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js
+++ b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js
@@ -55,6 +55,10 @@ export class CartPage extends Component {
const mode = this.selfOrder.config.self_ordering_pay_after;
const order = this.selfOrder.currentOrder;
+ if (!this.selfOrder.verifyCart()) {
+ return;
+ }
+
if (mode === "meal" && !order.isSavedOnServer) {
this.sendInProgress = true;
await this.selfOrder.sendDraftOrderToServer();
diff --git a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml
index dc61db3a122..8153be36bd9 100644
--- a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml
+++ b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml
@@ -53,7 +53,7 @@
-
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 21bc260f948..72909bd0342 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
@@ -173,4 +173,8 @@ export class ComboPage extends Component {
this.state.editMode = true;
this.state.showQtyButtons = false;
}
+
+ get showQtyButtons() {
+ return this.state.showQtyButtons && this.props.product.self_order_available;
+ }
}
diff --git a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.xml b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.xml
index cd4e9e0d4bd..e4fab85d78f 100644
--- a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.xml
+++ b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.xml
@@ -80,7 +80,7 @@
-
+
diff --git a/addons/pos_self_order/static/src/app/pages/product_page/product_page.js b/addons/pos_self_order/static/src/app/pages/product_page/product_page.js
index 468d725a0ea..716e040af9e 100644
--- a/addons/pos_self_order/static/src/app/pages/product_page/product_page.js
+++ b/addons/pos_self_order/static/src/app/pages/product_page/product_page.js
@@ -143,4 +143,8 @@ export class ProductPage extends Component {
this.router.back();
}
}
+
+ get showQtyButtons() {
+ return this.state.showQtyButtons && this.props.product.self_order_available;
+ }
}
diff --git a/addons/pos_self_order/static/src/app/pages/product_page/product_page.xml b/addons/pos_self_order/static/src/app/pages/product_page/product_page.xml
index e172e21117c..46c14696636 100644
--- a/addons/pos_self_order/static/src/app/pages/product_page/product_page.xml
+++ b/addons/pos_self_order/static/src/app/pages/product_page/product_page.xml
@@ -20,7 +20,7 @@
-
+
-
+