[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) <adgu@odoo.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" }
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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" }
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="pos_online_payment_self_order.PaymentPage" t-inherit="pos_self_order.PaymentPage" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('payment-state-container')]" position="after">
|
||||
<div t-if="this.selectedPaymentIsOnline and !state.selection" class="d-flex justify-content-center align-items-center flex-column h-100 px-3 text-center">
|
||||
<div t-if="this.selectedPaymentIsOnline and !state.selection and selfOrder.config.self_ordering_mode === 'kiosk'" class="d-flex justify-content-center align-items-center flex-column h-100 px-3 text-center">
|
||||
<h1 class="mb-4">Scan the QR code to pay</h1>
|
||||
<div class="d-inline-flex flex-column border rounded p-4 bg-view mb-3">
|
||||
<img t-att-src="state.qrImage" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user