[IMP] pos_self_order: add bus notification for self order
Previously, when an order was paid for at the restaurant counter, the self-orderer received no update that the order had been paid for. Now, if the customer self-order page is still open on their phone, it will receive a notification via websocket that the order has been paid for from the restaurant counter.The order will no longer be editable. In the event that they have already closed the self-order when paying at the counter, the next time they return to this page. An RPC request will be made with the access_token of the old order and the states will be aligned. closes odoo/odoo#125921 Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import timedelta
|
||||
import uuid
|
||||
|
||||
from odoo import http, fields, Command
|
||||
@@ -137,6 +138,7 @@ class PosSelfOrderController(http.Controller):
|
||||
def get_orders_by_access_token(self, access_tokens):
|
||||
orders_sudo = request.env["pos.order"].sudo().search([
|
||||
("access_token", "in", access_tokens),
|
||||
("date_order", ">=", fields.Datetime.now() - timedelta(days=7)),
|
||||
])
|
||||
|
||||
if not orders_sudo:
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import re
|
||||
from typing import Dict, Union
|
||||
from typing import Dict
|
||||
|
||||
from odoo import models, fields
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class PosOrderLine(models.Model):
|
||||
@@ -25,6 +24,29 @@ class PosOrderLine(models.Model):
|
||||
class PosOrder(models.Model):
|
||||
_inherit = "pos.order"
|
||||
|
||||
@api.model
|
||||
def create_from_ui(self, orders, draft=False):
|
||||
orders = super().create_from_ui(orders, draft)
|
||||
order_ids = self.env['pos.order'].browse([order['id'] for order in orders])
|
||||
self._send_notification(order_ids)
|
||||
return orders
|
||||
|
||||
@api.model
|
||||
def remove_from_ui(self, server_ids):
|
||||
order_ids = self.env['pos.order'].browse(server_ids)
|
||||
order_ids.state = 'cancel'
|
||||
self._send_notification(order_ids)
|
||||
|
||||
return super().remove_from_ui(server_ids)
|
||||
|
||||
def _send_notification(self, order_ids):
|
||||
for order in order_ids:
|
||||
if order.access_token and order.state != 'draft':
|
||||
self.env['bus.bus']._sendone(f'self_order-{order.access_token}', 'ORDER_STATE_CHANGED', {
|
||||
'access_token': order.access_token,
|
||||
'state': order.state
|
||||
})
|
||||
|
||||
def _export_for_self_order(self) -> Dict:
|
||||
self.ensure_one()
|
||||
|
||||
@@ -34,7 +56,7 @@ class PosOrder(models.Model):
|
||||
"pos_reference": self.pos_reference,
|
||||
"access_token": self.access_token,
|
||||
"state": self.state,
|
||||
"date": str(self.date_order),
|
||||
"date_order": str(self.date_order),
|
||||
"amount_total": self.amount_total,
|
||||
"amount_tax": self.amount_tax,
|
||||
"lines": [
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { effect } from "@web/core/utils/reactive";
|
||||
import { batched } from "@point_of_sale/utils";
|
||||
|
||||
export class SelfOrderBus {
|
||||
static serviceDependencies = ["self_order", "orm", "bus_service"];
|
||||
@@ -16,13 +17,13 @@ export class SelfOrderBus {
|
||||
this.bus = bus_service;
|
||||
|
||||
effect(
|
||||
(orders) => {
|
||||
batched((orders) => {
|
||||
for (const order of orders) {
|
||||
if (order.access_token) {
|
||||
this.bus.addChannel(`self_order-${order.access_token}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
[this.selfOrder.orders]
|
||||
);
|
||||
|
||||
@@ -34,10 +35,14 @@ export class SelfOrderBus {
|
||||
}
|
||||
|
||||
dispatch(message) {
|
||||
if (message.type === "ORDER_PAID") {
|
||||
console.log("ORDER_PAID", message.payload);
|
||||
if (message.type === "ORDER_STATE_CHANGED") {
|
||||
this.ws_changeOrderState(message.payload.access_token, message.payload.state);
|
||||
}
|
||||
}
|
||||
|
||||
ws_changeOrderState(access_token, state) {
|
||||
this.selfOrder.changeOrderState(access_token, state);
|
||||
}
|
||||
}
|
||||
|
||||
export const SelfOrderBusService = {
|
||||
|
||||
@@ -9,12 +9,14 @@ import { effect } from "@web/core/utils/reactive";
|
||||
import { Order } from "./models/order";
|
||||
import { Product } from "./models/product";
|
||||
import { ConnectionLostError, RPCError } from "@web/core/network/rpc_service";
|
||||
import { batched } from "@point_of_sale/utils";
|
||||
|
||||
export class SelfOrder {
|
||||
constructor(...args) {
|
||||
this.setup(...args);
|
||||
this.ready = this.setup(...args).then(() => this);
|
||||
}
|
||||
setup(env, rpc, notification, router) {
|
||||
|
||||
async setup(env, rpc, notification, router) {
|
||||
Object.assign(this, {
|
||||
...session.pos_self_order_data,
|
||||
});
|
||||
@@ -50,6 +52,14 @@ export class SelfOrder {
|
||||
{ type: "warning" }
|
||||
);
|
||||
}
|
||||
|
||||
if (this.self_order_mode !== "qr_code") {
|
||||
await this.getOrdersFromServer();
|
||||
effect(
|
||||
batched((state) => this.saveOrderToLocalStorage(state.orders)),
|
||||
[this]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
initData() {
|
||||
@@ -143,25 +153,39 @@ export class SelfOrder {
|
||||
}
|
||||
|
||||
async getOrdersFromServer() {
|
||||
const accessTokens = this.orders.map((order) => order.access_token);
|
||||
const accessTokens = this.orders.map((order) => order.access_token).filter(Boolean);
|
||||
|
||||
if (accessTokens.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const orderChanges = {};
|
||||
const orders = await this.rpc(`/pos-self-order/get-orders/`, {
|
||||
access_tokens: accessTokens,
|
||||
});
|
||||
|
||||
this.orders = this.orders.filter((order) => {
|
||||
orderChanges[order.access_token] = order.lastChangesSent;
|
||||
return !order.access_token;
|
||||
});
|
||||
const ordersToRecreate = {};
|
||||
for (const order of this.orders.filter((o) => o.access_token)) {
|
||||
ordersToRecreate[order.access_token] = {
|
||||
lastChangesSent: order.lastChangesSent,
|
||||
lines: order.lines.filter((l) => !l.id),
|
||||
};
|
||||
}
|
||||
|
||||
for (const index in this.orders) {
|
||||
if (this.orders[index].access_token) {
|
||||
this.orders.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.orders.push(
|
||||
...orders.map((o) => {
|
||||
const data = ordersToRecreate[o.access_token] || {};
|
||||
const newOrder = new Order({
|
||||
...o,
|
||||
lastChangesSent: orderChanges[o.access_token],
|
||||
lastChangesSent: data.lastChangesSent ?? {},
|
||||
});
|
||||
newOrder.lines.push(...(data.lines ?? []));
|
||||
return newOrder;
|
||||
})
|
||||
);
|
||||
@@ -179,12 +203,16 @@ export class SelfOrder {
|
||||
this.priceLoading = true;
|
||||
|
||||
try {
|
||||
if (!this.currentOrder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const taxes = await this.rpc(`/pos-self-order/get-orders-taxes/`, {
|
||||
order: this.currentOrder,
|
||||
pos_config_id: this.pos_config_id,
|
||||
});
|
||||
|
||||
for (const line of this.editedOrder.lines) {
|
||||
for (const line of this.currentOrder.lines) {
|
||||
const lineTaxes = taxes.lines.find((ol) => ol.uuid === line.uuid);
|
||||
line.price_subtotal = lineTaxes.price_subtotal;
|
||||
line.price_subtotal_incl = lineTaxes.price_subtotal_incl;
|
||||
@@ -218,15 +246,44 @@ export class SelfOrder {
|
||||
|
||||
if (accessToken) {
|
||||
this.editedOrder = null;
|
||||
this.orders = this.orders.filter((o) => !accessToken.includes(o.access_token));
|
||||
|
||||
for (const index in this.orders) {
|
||||
if (accessToken.includes(this.orders[index].access_token)) {
|
||||
this.orders.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeOrderState(access_token, state) {
|
||||
const order = this.orders.filter((o) => o.access_token === access_token);
|
||||
let message = _t("Your order status has been changed");
|
||||
|
||||
if (order.length !== 1) {
|
||||
throw new Error("Warning, two orders with the same access_token");
|
||||
} else {
|
||||
order[0].state = state;
|
||||
}
|
||||
|
||||
if (state === "paid") {
|
||||
this.editedOrder = null;
|
||||
message = _t("Your order has been paid");
|
||||
} else if (state === "cancel") {
|
||||
this.editedOrder = null;
|
||||
message = _t("Your order has been canceled");
|
||||
}
|
||||
|
||||
this.notification.add(message, {
|
||||
type: "success",
|
||||
});
|
||||
this.router.navigate("default");
|
||||
}
|
||||
}
|
||||
|
||||
export const selfOrderService = {
|
||||
dependencies: ["rpc", "notification", "router"],
|
||||
async start(env, { rpc, notification, router }) {
|
||||
return new SelfOrder(env, rpc, notification, router);
|
||||
return new SelfOrder(env, rpc, notification, router).ready;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ registry.category("web_tour.tours").add("self_order_after_each_cart_tour", {
|
||||
PosSelf.check.isNotNotification(),
|
||||
PosSelf.check.isPrimaryBtn("My Orders"),
|
||||
PosSelf.action.clickPrimaryBtn("My Orders"),
|
||||
PosSelf.check.isNotification("found on the server"),
|
||||
PosSelf.check.isNoOrderInHistory(),
|
||||
PosSelf.action.clickBack(),
|
||||
PosSelf.action.clickPrimaryBtn("View Menu"),
|
||||
|
||||
|
||||
@@ -71,6 +71,13 @@ export const PosSelf = {
|
||||
},
|
||||
];
|
||||
},
|
||||
isNoOrderInHistory: () => {
|
||||
return {
|
||||
content: `Verify if there is no order in history`,
|
||||
trigger: `.justify-content-center:contains('No order found')`,
|
||||
run: () => {},
|
||||
};
|
||||
},
|
||||
},
|
||||
action: {
|
||||
clickBack: () => {
|
||||
|
||||
Reference in New Issue
Block a user