From e9ce5608ddd06df19cba8ec37273e6675feb629c Mon Sep 17 00:00:00 2001 From: "David Monnom (moda)" Date: Thu, 26 Oct 2023 15:11:56 +0200 Subject: [PATCH] [IMP] pos_self_order, pos_*: overal improvements & fixes *:pos_self_order_adyen,pos_adyen,pos_online_payment_self_order Some overral improvements and fixes closes odoo/odoo#140041 Related: odoo/enterprise#49707 Signed-off-by: Adrien Guilliams (adgu) --- addons/pos_adyen/models/pos_payment_method.py | 2 +- .../models/pos_config.py | 6 ++ .../static/src/pages/cart_page/cart_page.js | 84 ------------------- .../src/pages/payment_page/payment_page.js | 27 ++++-- .../src/pages/payment_page/payment_page.xml | 2 +- .../static/src/self_order_service.js | 5 -- addons/pos_self_order/controllers/orders.py | 2 +- addons/pos_self_order/models/pos_config.py | 9 +- addons/pos_self_order/models/pos_order.py | 13 +++ .../src/app/pages/cart_page/cart_page.js | 33 +------- .../app/pages/landing_page/landing_page.js | 7 ++ .../app/pages/payment_page/payment_page.js | 11 +-- .../stand_number_page/stand_number_page.js | 2 +- .../static/src/app/self_order_service.js | 59 ++++++++++++- .../static/src/overrides/models/pos_store.js | 6 +- .../pos_self_order_adyen/controllers/main.py | 17 ++-- 16 files changed, 135 insertions(+), 150 deletions(-) delete mode 100644 addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js diff --git a/addons/pos_adyen/models/pos_payment_method.py b/addons/pos_adyen/models/pos_payment_method.py index 9746fb6fefe..56144756a06 100644 --- a/addons/pos_adyen/models/pos_payment_method.py +++ b/addons/pos_adyen/models/pos_payment_method.py @@ -82,7 +82,7 @@ class PosPaymentMethod(models.Model): def proxy_adyen_request(self, data, operation=False): ''' Necessary because Adyen's endpoints don't have CORS enabled ''' - if data['SaleToPOIRequest']['MessageHeader']['MessageCategory'] == 'Payment': # Clear only if it is a payment request + if data.get('SaleToPOIRequest') and data['SaleToPOIRequest']['MessageHeader']['MessageCategory'] == 'Payment': # Clear only if it is a payment request self.sudo().adyen_latest_response = '' # avoid handling old responses multiple times if not operation: diff --git a/addons/pos_online_payment_self_order/models/pos_config.py b/addons/pos_online_payment_self_order/models/pos_config.py index 40b79f37d0e..4e3acb01db5 100644 --- a/addons/pos_online_payment_self_order/models/pos_config.py +++ b/addons/pos_online_payment_self_order/models/pos_config.py @@ -22,3 +22,9 @@ class PosConfig(models.Model): payment_methods = self.self_order_online_payment_method_id.read(payment_search_params['search_params']['fields']) res['pos_payment_methods'] += payment_methods return res + + def _get_allowed_payment_methods(self): + if self.self_ordering_mode == 'kiosk': + return self.payment_method_ids.filtered(lambda p: p.use_payment_terminal in self._ALLOWED_PAYMENT_METHODS or p.is_online_payment) + else: + return self.payment_method_ids.filtered(lambda p: p.is_online_payment) 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 deleted file mode 100644 index a1bfc1650ae..00000000000 --- a/addons/pos_online_payment_self_order/static/src/pages/cart_page/cart_page.js +++ /dev/null @@ -1,84 +0,0 @@ -/** @odoo-module */ -import { patch } from "@web/core/utils/patch"; -import { CartPage } from "@pos_self_order/app/pages/cart_page/cart_page"; -import { _t } from "@web/core/l10n/translation"; - -patch(CartPage.prototype, { - async pay() { - const isOnlinePayment = this.selfOrder.pos_payment_methods.find((p) => p.is_online_payment); - - if (!isOnlinePayment) { - return super.pay(...arguments); - } - - if (this.sendInProgress) { - return; - } - - const order = this.selfOrder.currentOrder; - const mode = this.selfOrder.config.self_ordering_pay_after; - const service = this.selfOrder.config.self_ordering_service_mode; - const takeAway = this.selfOrder.currentOrder.take_away; - - if (!this.selfOrder.table && service === "table" && !takeAway) { - this.state.selectTable = true; - return; - } - - if (mode === "meal" && order.isSavedOnServer) { - this.checkAndOpenPaymentPage(order); - } else if (mode === "meal" && !order.isSavedOnServer) { - this.sendInProgress = true; - await this.selfOrder.sendDraftOrderToServer(); - - this.router.navigate("confirmation", { - orderAccessToken: order.access_token, - screenMode: "order", - }); - } else if (mode === "each") { - this.checkAndOpenPaymentPage(order); - } else { - return super.pay(...arguments); - } - }, - async checkAndOpenPaymentPage(order) { - const isOnlinePayment = this.selfOrder.pos_payment_methods.find((p) => p.is_online_payment); - - if (order) { - if (order.state === "draft") { - if (!isOnlinePayment) { - // if no payment method is available -> pay at cashier - this.router.navigate("confirmation", { - orderAccessToken: order.access_token, - screenMode: "pay", - }); - return; - } - - if ( - !order.isSavedOnServer && - this.selfOrder.config.self_ordering_mode !== "kiosk" - ) { - await this.selfOrder.sendDraftOrderToServer(); - } - - if (this.selfOrder.config.self_ordering_mode === "mobile") { - const onlinePaymentUrl = this.selfOrder.getOnlinePaymentUrl(order, true); - window.open(onlinePaymentUrl, "_self"); - } else { - this.router.navigate("payment"); - } - } else { - this.selfOrder.notification.add( - _t("The current order cannot be paid (maybe it is already paid)."), - { type: "danger" } - ); - } - } else { - this.selfOrder.notification.add( - _t("The order could not be saved, therefore its payment is unavailable."), - { type: "danger" } - ); - } - }, -}); diff --git a/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.js b/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.js index 7722b24bc84..cabe3f2c770 100644 --- a/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.js +++ b/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.js @@ -1,28 +1,32 @@ /** @odoo-module */ import { patch } from "@web/core/utils/patch"; import { PaymentPage } from "@pos_self_order/app/pages/payment_page/payment_page"; +import { _t } from "@web/core/l10n/translation"; patch(PaymentPage.prototype, { async startPayment() { let order = this.selfOrder.currentOrder; - const selfMode = this.selfOrder.config.self_ordering_mode; const pm = this.selectedPaymentMethod; + const device = this.selfOrder.config.self_ordering_mode; - if (selfMode !== "kiosk" || !pm || !pm.is_online_payment) { + if (!pm || !pm.is_online_payment) { return super.startPayment(...arguments); } else { order = await this.selfOrder.sendDraftOrderToServer(); } - const url = this.selfOrder.getOnlinePaymentUrl(order, false); - this.generateQrcodeImg(url); + if (device === "kiosk") { + const url = this.selfOrder.getOnlinePaymentUrl(order, false); + this.generateQrcodeImg(url); + } else { + this.checkAndOpenPaymentPage(order); + } }, get selectedPaymentIsOnline() { const paymentMethods = this.selectedPaymentMethod; return ( paymentMethods && - paymentMethods.is_online_payment && - this.selfOrder.config.self_ordering_mode === "kiosk" + paymentMethods.is_online_payment ); }, generateQrcodeImg(url) { @@ -30,4 +34,15 @@ patch(PaymentPage.prototype, { const qr_code_svg = new XMLSerializer().serializeToString(codeWriter.write(url, 150, 150)); this.state.qrImage = "data:image/svg+xml;base64," + window.btoa(qr_code_svg); }, + async checkAndOpenPaymentPage(order) { + if (order.state === "draft") { + const onlinePaymentUrl = this.selfOrder.getOnlinePaymentUrl(order, true); + window.open(onlinePaymentUrl, "_self"); + } else { + this.selfOrder.notification.add( + _t("The current order cannot be paid (maybe it is already paid)."), + { type: "danger" } + ); + } + }, }); diff --git a/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.xml b/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.xml index 91102563965..89cd766fab8 100644 --- a/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.xml +++ b/addons/pos_online_payment_self_order/static/src/pages/payment_page/payment_page.xml @@ -2,7 +2,7 @@ -
+

Scan the QR code to pay

diff --git a/addons/pos_online_payment_self_order/static/src/self_order_service.js b/addons/pos_online_payment_self_order/static/src/self_order_service.js index c46e1af47a2..f71496bbb1d 100644 --- a/addons/pos_online_payment_self_order/static/src/self_order_service.js +++ b/addons/pos_online_payment_self_order/static/src/self_order_service.js @@ -23,7 +23,6 @@ patch(SelfOrder.prototype, { ) { const baseUrl = session.base_url; const order = this.currentOrder; - const tableIdentifier = this.table?.identifier; let exitRouteUrl = baseUrl; if (exitRoute) { @@ -34,10 +33,6 @@ patch(SelfOrder.prototype, { } exitRouteUrl += `?access_token=${this.access_token}`; - - if (tableIdentifier) { - exitRouteUrl += `&table_identifier=${tableIdentifier}`; - } } const exit = encodeURIComponent(exitRouteUrl); diff --git a/addons/pos_self_order/controllers/orders.py b/addons/pos_self_order/controllers/orders.py index 36f05b868ba..7eaa68e7f3e 100644 --- a/addons/pos_self_order/controllers/orders.py +++ b/addons/pos_self_order/controllers/orders.py @@ -79,7 +79,7 @@ class PosSelfOrderController(http.Controller): order.write({ 'lines': lines, - 'state': 'paid' if amount_total == 0 and pos_config.self_ordering_pay_after == 'each' else 'draft', + 'state': 'paid' if amount_total == 0 else 'draft', 'amount_tax': amount_total - amount_untaxed, 'amount_total': amount_total, }) diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py index fca890f5ed9..397a94eda38 100644 --- a/addons/pos_self_order/models/pos_config.py +++ b/addons/pos_self_order/models/pos_config.py @@ -18,6 +18,8 @@ from odoo.tools import file_open, split_every class PosConfig(models.Model): _inherit = "pos.config" + _ALLOWED_PAYMENT_METHODS = ['adyen', 'stripe'] + def _self_order_kiosk_default_languages(self): return self.env["res.lang"].get_installed() @@ -145,6 +147,11 @@ class PosConfig(models.Model): return pos_config_ids + def _get_allowed_payment_methods(self): + if self.self_ordering_mode == 'kiosk': + return self.payment_method_ids.filtered(lambda p: p.use_payment_terminal in self._ALLOWED_PAYMENT_METHODS) + return [] + def write(self, vals): for record in self: if vals.get('self_ordering_mode') == 'kiosk' or (vals.get('pos_self_ordering_mode') == 'mobile' and vals.get('pos_self_ordering_service_mode') == 'counter'): @@ -333,7 +340,7 @@ class PosConfig(models.Model): def _get_self_ordering_data(self): self.ensure_one() payment_search_params = self.current_session_id._loader_params_pos_payment_method() - payment_methods = self.payment_method_ids.filtered(lambda p: p.use_payment_terminal in ['adyen', 'stripe'] or p.is_online_payment).read(payment_search_params['search_params']['fields']) + payment_methods = self._get_allowed_payment_methods().read(payment_search_params['search_params']['fields']) default_language = self.self_ordering_default_language_id.read(["code", "name", "iso_code", "flag_image_url"]) return { diff --git a/addons/pos_self_order/models/pos_order.py b/addons/pos_self_order/models/pos_order.py index 005dbe0919f..b639b185db4 100644 --- a/addons/pos_self_order/models/pos_order.py +++ b/addons/pos_self_order/models/pos_order.py @@ -69,9 +69,22 @@ class PosOrder(models.Model): @api.model def create_from_ui(self, orders, draft=False): + old_order_ids = [] + + for order in orders: + if order['data'].get('server_id'): + server_id = order['data'].get('server_id') + old_order = self.env['pos.order'].browse(server_id).read(['id', 'take_away']) + old_order_ids.append(old_order[0]) + orders = super().create_from_ui(orders, draft) order_ids = self.env['pos.order'].browse([order['id'] for order in orders]) + if old_order_ids: + for order in old_order_ids: + if order['take_away']: + order_ids.filtered(lambda o: o.id == order['id']).write({'take_away': True}) + if self.env.context.get('from_self') is not True: self._send_notification(order_ids) 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 bf8d8f2ce4f..be9dcd5b387 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 @@ -51,9 +51,6 @@ export class CartPage extends Component { const orderingMode = this.selfOrder.config.self_ordering_service_mode; const type = this.selfOrder.config.self_ordering_mode; const takeAway = this.selfOrder.currentOrder.take_away; - const mode = this.selfOrder.config.self_ordering_pay_after; - const order = this.selfOrder.currentOrder; - const isPaymentMethod = this.selfOrder.pos_payment_methods.length > 0; if (this.sendInProgress || !this.selfOrder.verifyCart()) { return; @@ -64,33 +61,9 @@ export class CartPage extends Component { return; } - // in case of no payment methods available -> pay at cashier - if (!isPaymentMethod) { - let screenMode = "pay"; - - if (!order.isSavedOnServer) { - this.sendInProgress = true; - await this.selfOrder.sendDraftOrderToServer(); - this.sendInProgress = false; - screenMode = mode === "meal" ? "order" : "pay"; - } - - this.router.navigate("confirmation", { - orderAccessToken: order.access_token, - screenMode: screenMode, - }); - return; - } - - if (orderingMode === "table" && !takeAway) { - if (type === "kiosk") { - this.router.navigate("stand_number"); - } else { - this.router.navigate("payment"); - } - } else { - this.router.navigate("payment"); - } + this.sendInProgress = true; + await this.selfOrder.confirmOrder(); + this.sendInProgress = false; } selectTable(table) { 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 b241873761b..27ba4b5cbd1 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 @@ -91,6 +91,13 @@ export class LandingPage extends Component { } start() { + if ( + this.draftOrder.length > 0 && + this.selfOrder.config.self_ordering_pay_after === "each" + ) { + return; + } + 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 53dbfec326b..cc1187d72dc 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,16 +23,9 @@ export class PaymentPage extends Component { }); onWillStart(async () => { - const type = this.selfOrder.config.self_ordering_mode; const paymentMethods = this.selfOrder.pos_payment_methods; - if (paymentMethods.length === 0 && type === "kiosk") { - await this.selfOrder.sendDraftOrderToServer(); - this.router.navigate("confirmation", { - orderAccessToken: this.selfOrder.currentOrder.access_token, - screenMode: "pay", - }); - } else if (paymentMethods.length === 1 && type === "kiosk") { + if (paymentMethods.length === 1) { this.selectMethod(this.selfOrder.pos_payment_methods[0].id); } }); @@ -52,6 +45,8 @@ export class PaymentPage extends Component { return this.selfOrder.pos_payment_methods.find((p) => p.id === this.state.paymentMethodId); } + // this function will be override by pos_online_payment_self_order module + // in mobile is the only available payment method async startPayment() { this.selfOrder.paymentError = false; try { diff --git a/addons/pos_self_order/static/src/app/pages/stand_number_page/stand_number_page.js b/addons/pos_self_order/static/src/app/pages/stand_number_page/stand_number_page.js index 327bf478760..0b807414370 100644 --- a/addons/pos_self_order/static/src/app/pages/stand_number_page/stand_number_page.js +++ b/addons/pos_self_order/static/src/app/pages/stand_number_page/stand_number_page.js @@ -35,7 +35,7 @@ export class StandNumberPage extends Component { confirm() { if (this.state.standNumber.length > 0) { this.selfOrder.currentOrder.table_stand_number = this.state.standNumber; - this.router.navigate("payment"); + this.selfOrder.confirmOrder(); } } } 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 db33bb46c77..a6fae140b85 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 @@ -42,7 +42,6 @@ export class SelfOrder extends Reactive { this.productsGroupedByCategory = {}; this.lastEditedProductId = null; this.attributeValueById = {}; - this.eatingLocation = "in"; // (in, out) in by default because out can be disabled in the config this.currentProduct = 0; this.attributeById = {}; this.priceLoading = false; @@ -62,13 +61,67 @@ export class SelfOrder extends Reactive { } } + async confirmOrder() { + const payAfter = this.config.self_ordering_pay_after; // each, meal + const device = this.config.self_ordering_mode; // kiosk, mobile + const service = this.config.self_ordering_service_mode; // table, counter + const paymentMethods = this.pos_payment_methods; // Stripe, Adyen, Online + const order = this.currentOrder; + + // Stand number page will recall this function after the stand number is set + if (service === "table" && !order.take_away && device === "kiosk" && !order.table_stand_number) { + this.router.navigate("stand_number"); + return; + } + + // if the amount is 0, we don't need to go to the payment page + // this directive works for both mode each and meal + if (order.amount_total === 0 && order.lines.length > 0) { + await this.sendDraftOrderToServer(); + this.router.navigate("confirmation", { + orderAccessToken: order.access_token, + screenMode: "order", + }); + return; + } + + // When no payment methods redirect to confirmation page + // the client will be able to pay at counter + if (paymentMethods.length === 0) { + let screenMode = "pay"; + + if (!order.isSavedOnServer) { + await this.sendDraftOrderToServer(); + screenMode = payAfter === "meal" ? "order" : "pay"; + } + + this.router.navigate("confirmation", { + orderAccessToken: order.access_token, + screenMode: screenMode, + }); + } else { + // In meal mode, first time the customer validate his order, we send it to the server + // and we redirect him to the confirmation page, the next time he validate his order + // if the order is already saved on the server, we redirect him to the payment page + // In each mode, we redirect the customer to the payment page directly + if (payAfter === "meal" && !order.isSavedOnServer) { + await this.sendDraftOrderToServer(); + this.router.navigate("confirmation", { + orderAccessToken: order.access_token, + screenMode: "order", + }); + } else { + this.router.navigate("payment"); + } + } + } + get currentOrder() { - if (this.editedOrder) { + if (this.editedOrder && this.editedOrder.state === "draft") { return this.editedOrder; } const existingOrder = this.orders.find((o) => o.state === "draft"); - if (!existingOrder) { const newOrder = new Order({ pos_config_id: this.pos_config_id, diff --git a/addons/pos_self_order/static/src/overrides/models/pos_store.js b/addons/pos_self_order/static/src/overrides/models/pos_store.js index 9e5f4cad24a..bbc18180836 100644 --- a/addons/pos_self_order/static/src/overrides/models/pos_store.js +++ b/addons/pos_self_order/static/src/overrides/models/pos_store.js @@ -14,6 +14,10 @@ patch(PosStore.prototype, { patch(Order.prototype, { defaultTableNeeded(options) { - return super.defaultTableNeeded(...arguments) && !this.name.includes("Kiosk") && !this.name.includes("Self-Order"); + return ( + super.defaultTableNeeded(...arguments) && + !this.name.includes("Kiosk") && + !this.name.includes("Self-Order") + ); }, }); diff --git a/addons/pos_self_order_adyen/controllers/main.py b/addons/pos_self_order_adyen/controllers/main.py index 4f901a4040e..fe445effb95 100644 --- a/addons/pos_self_order_adyen/controllers/main.py +++ b/addons/pos_self_order_adyen/controllers/main.py @@ -14,14 +14,15 @@ class PosSelfAdyenController(PosAdyenController): order_reference = data['SaleToPOIResponse']['PaymentResponse']['SaleData']['SaleTransactionID']['TransactionID'] payment_result = data['SaleToPOIResponse']['PaymentResponse']['Response']['Result'] order_sudo = request.env['pos.order'].sudo().search([('pos_reference', '=', order_reference)], limit=1) + order = order_sudo.sudo(False).with_user(order_sudo.session_id.config_id.self_ordering_default_user_id).with_company(order_sudo.session_id.config_id.company_id) terminal_identifier = data['SaleToPOIResponse']['MessageHeader']['POIID'] payment_method = request.env['pos.payment.method'].sudo().search([('adyen_terminal_identifier', '=', terminal_identifier)], limit=1) - if payment_result == 'Success' and order_sudo.config_id.self_ordering_mode == 'kiosk': + if payment_result == 'Success' and order.config_id.self_ordering_mode == 'kiosk': payment_amount = data['SaleToPOIResponse']['PaymentResponse']['PaymentResult']['AmountsResp']['AuthorizedAmount'] card_type = data['SaleToPOIResponse']['PaymentResponse']['PaymentResult']['PaymentInstrumentData']['CardData']['PaymentBrand'] transaction_id = data['SaleToPOIResponse']['PaymentResponse']['SaleData']['SaleTransactionID']['TransactionID'] - order_sudo.add_payment({ + order.add_payment({ 'amount': payment_amount, 'payment_date': fields.Datetime.now(), 'payment_method_id': payment_method.id, @@ -30,13 +31,13 @@ class PosSelfAdyenController(PosAdyenController): 'transaction_id': transaction_id, 'payment_status': payment_result, 'ticket': '', - 'pos_order_id': order_sudo.id + 'pos_order_id': order.id }) - order_sudo.action_pos_order_paid() - order_sudo._send_order() + order.action_pos_order_paid() + order._send_order() - if order_sudo.config_id.self_ordering_mode == 'kiosk': - request.env['bus.bus']._sendone(f'pos_config-{order_sudo.config_id.access_token}', 'PAYMENT_STATUS', { + if order.config_id.self_ordering_mode == 'kiosk': + order.env['bus.bus']._sendone(f'pos_config-{order.config_id.access_token}', 'PAYMENT_STATUS', { 'payment_result': payment_result, - 'order': order_sudo._export_for_self_order(), + 'order': order._export_for_self_order(), })