diff --git a/addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js b/addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js index 80bca406ced..d91a315a7dd 100644 --- a/addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js +++ b/addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js @@ -9,22 +9,22 @@ patch(CartPage.prototype, { return super.pay(...arguments); } + if (this.sendInProgress) { + return; + } + + const order = this.selfOrder.currentOrder; const mode = this.selfOrder.config.self_ordering_pay_after; const isOnlinePayment = this.selfOrder.pos_payment_methods.find((p) => p.is_online_payment); const service = this.selfOrder.config.self_ordering_service_mode; const takeAway = this.selfOrder.currentOrder.take_away; - if (this.sendInProgress) { - return; - } - if (!this.selfOrder.table && service === "table" && !takeAway) { this.state.selectTable = true; return; } if (mode === "meal" && isOnlinePayment) { - const order = this.selfOrder.currentOrder; if (!order) { this.selfOrder.notification.add(_t("The current order is invalid."), { type: "danger", @@ -39,17 +39,28 @@ patch(CartPage.prototype, { this.checkAndOpenPaymentPage(order); } } else if (mode === "each") { - this.sendInProgress = true; - const order = await this.selfOrder.sendDraftOrderToServer(); - this.sendInProgress = false; this.checkAndOpenPaymentPage(order); } else { return super.pay(...arguments); } }, - checkAndOpenPaymentPage(order) { + async checkAndOpenPaymentPage(order) { + const isOnlinePayment = this.selfOrder.pos_payment_methods.find((p) => p.is_online_payment); + if (order) { if (order.state === "draft") { + if (!isOnlinePayment) { + this.selfOrder.notification.add( + _t("The current order cannot be paid (no online payment method)."), + { type: "danger" } + ); + return; + } + + if (!order.isSavedOnServer) { + await this.selfOrder.sendDraftOrderToServer(); + } + this.selfOrder.openOnlinePaymentPage(order); } else { this.selfOrder.notification.add( diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py index 92028c04b9e..48146d3fca7 100644 --- a/addons/pos_self_order/models/pos_config.py +++ b/addons/pos_self_order/models/pos_config.py @@ -150,6 +150,9 @@ class PosConfig(models.Model): if vals.get('self_ordering_mode') == 'kiosk' or record.self_ordering_mode == 'kiosk': vals['self_ordering_pay_after'] = 'each' + if (not vals.get('module_pos_restaurant') and not record.module_pos_restaurant) and vals.get('self_ordering_mode') == 'mobile': + vals['self_ordering_pay_after'] = 'each' + return super().write(vals) @api.depends("module_pos_restaurant") @@ -175,7 +178,7 @@ class PosConfig(models.Model): self.ensure_one() table_qr_code = [] - if self.self_ordering_mode == 'mobile': + if self.self_ordering_mode == 'mobile' and self.module_pos_restaurant and self.self_ordering_service_mode == 'table': table_qr_code.extend([{ 'name': floor.name, 'type': 'table', diff --git a/addons/pos_self_order/models/product_product.py b/addons/pos_self_order/models/product_product.py index fe5a6b043ad..8e7284127cd 100644 --- a/addons/pos_self_order/models/product_product.py +++ b/addons/pos_self_order/models/product_product.py @@ -16,13 +16,19 @@ class ProductTemplate(models.Model): default=True, ) - @api.constrains('available_in_pos') - def _check_combo_inclusions(self): - super()._check_combo_inclusions() - self.self_order_available = False + @api.onchange('available_in_pos') + def _on_change_available_in_pos(self): + for record in self: + if not record.available_in_pos: + record.self_order_available = False def write(self, vals_list): + if 'available_in_pos' in vals_list: + if not vals_list['available_in_pos']: + vals_list['self_order_available'] = False + res = super().write(vals_list) + if 'self_order_available' in vals_list: for record in self: for product in record.product_variant_ids: diff --git a/addons/pos_self_order/models/res_config_settings.py b/addons/pos_self_order/models/res_config_settings.py index aa18917157d..f68fcf77d74 100644 --- a/addons/pos_self_order/models/res_config_settings.py +++ b/addons/pos_self_order/models/res_config_settings.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from odoo import models, fields, api, _ -from odoo.exceptions import UserError +from odoo.exceptions import ValidationError from odoo.tools.misc import split_every from werkzeug.urls import url_unquote @@ -28,7 +28,12 @@ class ResConfigSettings(models.TransientModel): user_id = self.pos_self_ordering_default_user_id if not user_id.has_group("point_of_sale.group_pos_user") and not user_id.has_group("point_of_sale.group_pos_manager"): - raise UserError(_("The user must be a POS user")) + raise ValidationError(_("The user must be a POS user")) + + @api.onchange("pos_self_ordering_service_mode") + def _onchange_pos_self_order_service_mode(self): + if self.pos_self_ordering_service_mode == 'counter': + self.pos_self_ordering_pay_after = "each" @api.onchange("pos_self_ordering_default_language_id", "pos_self_ordering_available_language_ids") def _onchange_pos_self_order_kiosk_default_language(self): @@ -37,45 +42,47 @@ class ResConfigSettings(models.TransientModel): if not self.pos_self_ordering_default_language_id and self.pos_self_ordering_available_language_ids: self.pos_self_ordering_default_language_id = self.pos_self_ordering_available_language_ids[0] - @api.onchange("pos_self_ordering_mode") + @api.onchange("pos_self_ordering_mode", "pos_module_pos_restaurant") def _onchange_pos_self_order_kiosk(self): if self.pos_self_ordering_mode == 'kiosk': self.is_kiosk_mode = True + self.pos_module_pos_restaurant = False self.pos_self_ordering_pay_after = "each" - - elif self.pos_self_ordering_mode == 'mobile' and not self.pos_module_pos_restaurant: - raise UserError(_("In Self-Order mode, you must have the Restaurant module")) else: self.is_kiosk_mode = False - @api.onchange("pos_self_ordering_pay_after", "pos_self_ordering_mode") - def _onchange_service_and_ordering(self): - if self.pos_self_ordering_pay_after == "meal" and self.pos_self_ordering_mode == 'mobile': - self.pos_self_ordering_service_mode = 'table' + if not self.pos_module_pos_restaurant: + self.pos_self_ordering_service_mode = 'counter' @api.onchange("pos_self_ordering_pay_after", "pos_self_ordering_mode") def _onchange_pos_self_order_pay_after(self): if self.pos_self_ordering_pay_after == "meal" and self.pos_self_ordering_mode == 'kiosk': - raise UserError(_("Only pay after each is available with kiosk mode.")) + raise ValidationError(_("Only pay after each is available with kiosk mode.")) + + if self.pos_self_ordering_service_mode == 'counter' and self.pos_self_ordering_mode == 'mobile': + self.pos_self_ordering_pay_after = "each" if self.pos_self_ordering_pay_after == "each" and not self.module_pos_preparation_display: self.module_pos_preparation_display = True - @api.onchange("pos_self_ordering_service_mode") - def _onchange_pos_self_ordering_service_mode(self): - table_ids = self.pos_floor_ids.table_ids - if self.pos_self_ordering_service_mode == 'table' and self.pos_self_ordering_mode == 'mobile' and not table_ids: - raise UserError(_("In Self-Order mode, you must have at least one table to use the table service mode")) + def custom_link_action(self): + self.ensure_one() + return { + "type": "ir.actions.act_window", + "res_model": "pos_self_order.custom_link", + "views": [[False, "tree"]], + "domain": [['pos_config_ids', 'in', self.pos_config_id.id]], + } def generate_qr_codes_page(self): """ Generate the data needed to print the QR codes page """ - if self.pos_self_ordering_mode == 'mobile': + if self.pos_self_ordering_mode == 'mobile' and self.pos_module_pos_restaurant: table_ids = self.pos_config_id.floor_ids.table_ids if not table_ids: - raise UserError(_("In Self-Order mode, you must have at least one table to generate QR codes")) + raise ValidationError(_("In Self-Order mode, you must have at least one table to generate QR codes")) url = url_unquote(self.pos_config_id._get_self_order_url(table_ids[0].id)) name = table_ids[0].name @@ -94,7 +101,8 @@ class ResConfigSettings(models.TransientModel): } for floor in self.pos_config_id._get_qr_code_data() ], - 'table_mode': self.pos_self_ordering_mode, + 'table_mode': self.pos_self_ordering_mode and self.pos_module_pos_restaurant and self.pos_self_ordering_service_mode == 'table', + 'self_order': self.pos_self_ordering_mode == 'mobile', 'table_example': { 'name': name, 'decoded_url': url or "", diff --git a/addons/pos_self_order/static/src/app/components/order_widget/order_widget.js b/addons/pos_self_order/static/src/app/components/order_widget/order_widget.js index 76ac72a640b..25dc3f8de47 100644 --- a/addons/pos_self_order/static/src/app/components/order_widget/order_widget.js +++ b/addons/pos_self_order/static/src/app/components/order_widget/order_widget.js @@ -40,19 +40,20 @@ export class OrderWidget extends Component { const currentPage = this.router.activeSlot; const payAfter = this.selfOrder.config.self_ordering_pay_after; const kioskPayment = this.selfOrder.pos_payment_methods.find((p) => !p.is_online_payment); // cannot be online payment in kiosk for instance - const isLine = this.selfOrder.currentOrder.lines.length !== 0; + const isNoLine = this.selfOrder.currentOrder.lines.length === 0; let label = ""; - let disabled = !isLine; + let disabled = false; if (currentPage === "product_list") { label = _t("Order"); + disabled = isNoLine; } else if (payAfter === "meal" && !this.selfOrder.currentOrder.isSavedOnServer) { label = _t("Order"); - disabled = disabled || !kioskPayment; + disabled = isNoLine; } else { label = kioskPayment ? _t("Pay") : _t("Pay at cashier"); - disabled = disabled || kioskPayment; + disabled = false; } return { label, disabled }; 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 db59d6f0e77..ab43eee4337 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 @@ -15,7 +15,7 @@ export class CartPage extends Component { setup() { this.selfOrder = useSelfOrder(); this.router = useService("router"); - + this.sendInProgress = false; this.state = useState({ selectTable: false, cancelConfirmation: false, @@ -49,6 +49,10 @@ export class CartPage extends Component { } async pay() { + if (this.sendInProgress) { + return; + } + const orderingMode = this.selfOrder.config.self_ordering_service_mode; const type = this.selfOrder.config.self_ordering_mode; const takeAway = this.selfOrder.currentOrder.take_away; diff --git a/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.js b/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.js deleted file mode 100644 index 4337f034579..00000000000 --- a/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.js +++ /dev/null @@ -1,7 +0,0 @@ -/** @odoo-module */ - -import { Component } from "@odoo/owl"; - -export class ClosedPage extends Component { - static template = "pos_self_order.ClosedPage"; -} diff --git a/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.xml b/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.xml deleted file mode 100644 index 00186261cf7..00000000000 --- a/addons/pos_self_order/static/src/app/pages/closed_page/closed_page.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - -
- The kiosk is closed -
-
-
diff --git a/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js b/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js index 7e871f7daad..785e71505bf 100644 --- a/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js +++ b/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js @@ -68,6 +68,10 @@ export class LandingPage extends Component { return; } + this.start(); + } + + start() { if ( this.selfOrder.config.self_ordering_takeaway && this.selfOrder.currentOrder.take_away === null diff --git a/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js b/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js index bb7a594a5d5..edef3ad0f82 100644 --- a/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js +++ b/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js @@ -23,24 +23,18 @@ export class PaymentPage extends Component { }); onWillStart(async () => { + if (this.selfOrder.currentOrder.lines.length === 0) { + this.router.navigate("default"); + return; + } + const type = this.selfOrder.config.self_ordering_mode; const paymentMethods = this.selfOrder.pos_payment_methods.filter( (p) => !p.is_online_payment ); if (paymentMethods.length === 0 && type === "kiosk") { - try { - const order = await this.rpc("/pos-self-order/process-new-order/kiosk", { - order: this.selfOrder.currentOrder, - access_token: this.selfOrder.access_token, - table_identifier: null, - }); - - this.selfOrder.updateOrderFromServer(order); - } catch (error) { - this.selfOrder.handleErrorNotification(error); - } - + this.selfOrder.sendDraftOrderToServer(); this.router.navigate("payment_success"); } else if (paymentMethods.length === 1 && type === "kiosk") { this.selectMethod(this.selfOrder.pos_payment_methods[0].id); diff --git a/addons/pos_self_order/static/src/app/self_order_bus_service.js b/addons/pos_self_order/static/src/app/self_order_bus_service.js index 4c94cc161f3..b036bfe9519 100644 --- a/addons/pos_self_order/static/src/app/self_order_bus_service.js +++ b/addons/pos_self_order/static/src/app/self_order_bus_service.js @@ -74,13 +74,12 @@ export class SelfOrderBus { if (payload.status === "closed") { this.selfOrder.pos_session = []; + this.selfOrder.ordering = false; } else { // reload to get potential new settings // more easier than RPC for now window.location.reload(); } - - this.selfOrder.isSession(); } ws_productChanged(message) { diff --git a/addons/pos_self_order/static/src/app/self_order_index.js b/addons/pos_self_order/static/src/app/self_order_index.js index 37b21ca348a..8ebd83643b5 100644 --- a/addons/pos_self_order/static/src/app/self_order_index.js +++ b/addons/pos_self_order/static/src/app/self_order_index.js @@ -1,5 +1,5 @@ /** @odoo-module */ -import { Component, whenReady, App, onMounted } from "@odoo/owl"; +import { Component, whenReady, App } from "@odoo/owl"; import { makeEnv, startServices } from "@web/env"; import { templates } from "@web/core/assets"; import { _t } from "@web/core/l10n/translation"; @@ -15,7 +15,6 @@ import { PaymentPage } from "@pos_self_order/app/pages/payment_page/payment_page import { PaymentSuccessPage } from "@pos_self_order/app/pages/payment_success_page/payment_success_page"; import { EatingLocationPage } from "@pos_self_order/app/pages/eating_location_page/eating_location_page"; import { StandNumberPage } from "@pos_self_order/app/pages/stand_number_page/stand_number_page"; -import { ClosedPage } from "@pos_self_order/app/pages/closed_page/closed_page"; import { OrdersHistoryPage } from "@pos_self_order/app/pages/order_history_page/order_history_page"; class selfOrderIndex extends Component { @@ -25,7 +24,6 @@ class selfOrderIndex extends Component { Router, CartPage, ProductPage, - ClosedPage, OrdersHistoryPage, ComboPage, PaymentPage, @@ -39,10 +37,9 @@ class selfOrderIndex extends Component { setup() { this.selfOrder = useSelfOrder(); - - onMounted(() => { - this.selfOrder.isSession(); - }); + } + get selfIsReady() { + return Object.values(this.selfOrder.productByIds).length > 0; } } diff --git a/addons/pos_self_order/static/src/app/self_order_index.xml b/addons/pos_self_order/static/src/app/self_order_index.xml index 0d8f06ac232..7ada2aee0d5 100644 --- a/addons/pos_self_order/static/src/app/self_order_index.xml +++ b/addons/pos_self_order/static/src/app/self_order_index.xml @@ -1,7 +1,10 @@ - +
+

We're currently closed.

+
+ @@ -29,13 +32,13 @@ - - - +
+ Hey, looks like you forgot to create products or add them to pos_config. Please add them before using the Self Order +
diff --git a/addons/pos_self_order/static/src/app/self_order_service.js b/addons/pos_self_order/static/src/app/self_order_service.js index 6d9203d3e12..528ffad49d8 100644 --- a/addons/pos_self_order/static/src/app/self_order_service.js +++ b/addons/pos_self_order/static/src/app/self_order_service.js @@ -106,6 +106,12 @@ export class SelfOrder extends Reactive { return product; }); + this.combos = this.combos.map((c) => { + const combo = new Combo(c); + this.comboByIds[combo.id] = combo; + return combo; + }); + this.productsGroupedByCategory = this.products.reduce((acc, product) => { product.pos_categ_ids.map((pos_categ_ids) => { acc[pos_categ_ids] = acc[pos_categ_ids] || []; @@ -121,11 +127,14 @@ export class SelfOrder extends Reactive { .sort((a, b) => categorySorter(a, b, this.config.iface_start_categ_id)) ); - this.combos = this.combos.map((c) => { - const combo = new Combo(c); - this.comboByIds[combo.id] = combo; - return combo; - }); + if (this.categoryList.size === 0) { + this.categoryList.add({ + has_image: false, + id: 0, + name: _t("Other"), + sequence: -1, + }); + } this.currentCategory = this.pos_category.length > 0 ? [...this.categoryList][0] : null; } @@ -197,6 +206,10 @@ export class SelfOrder extends Reactive { } async sendDraftOrderToServer() { + if (this.currentOrder.isSavedOnServer || this.currentOrder.lines.length === 0) { + return true; + } + try { const rpcUrl = this.currentOrder.isAlreadySent ? "/pos-self-order/update-existing-order" @@ -308,18 +321,6 @@ export class SelfOrder extends Reactive { this.currentOrder.updateDataFromServer(order); } - isSession() { - if (this.config.self_ordering_mode !== "kiosk") { - return; - } - - if (!this.pos_session || !this.pos_session.id) { - this.router.navigate("closed"); - } else if (this.router.activeSlot === "closed") { - this.router.navigate("default"); - } - } - isOrder() { if (!this.currentOrder || !this.currentOrder.lines.length) { this.router.navigate("default"); diff --git a/addons/pos_self_order/views/custom_link_views.xml b/addons/pos_self_order/views/custom_link_views.xml index 55246e0fce5..a3c920695b0 100644 --- a/addons/pos_self_order/views/custom_link_views.xml +++ b/addons/pos_self_order/views/custom_link_views.xml @@ -1,10 +1,5 @@ - - Custom Links - pos_self_order.custom_link - tree - custom.link.tree pos_self_order.custom_link diff --git a/addons/pos_self_order/views/qr_code.xml b/addons/pos_self_order/views/qr_code.xml index 70c928c2a57..8100e8d1b16 100644 --- a/addons/pos_self_order/views/qr_code.xml +++ b/addons/pos_self_order/views/qr_code.xml @@ -25,10 +25,10 @@
- Point of sale: Self Order: YesNo + Point of sale: Self Order: YesNo
-

Make it easy for your customers to explore your menu +

Make it easy for your customers to explore your menu online or order with the QR codes on your tables

Make it easy for your customers to explore your menu online with the QR codes on your tables

@@ -36,7 +36,7 @@

How to use

- +

Each table in your floor plan is assigned a unique QR code based on your configuration. For security reasons, both the point of sale and table names are encrypted in the generated URL, as shown in the example below:.

Table:
diff --git a/addons/pos_self_order/views/res_config_settings_views.xml b/addons/pos_self_order/views/res_config_settings_views.xml index 6b0f2446887..c341b7c0052 100644 --- a/addons/pos_self_order/views/res_config_settings_views.xml +++ b/addons/pos_self_order/views/res_config_settings_views.xml @@ -16,7 +16,7 @@

@@ -46,7 +46,7 @@
@@ -69,6 +69,11 @@ + + + is_kiosk_mode + + is_kiosk_mode