diff --git a/addons/pos_self_order/controllers/orders.py b/addons/pos_self_order/controllers/orders.py index e5144c947e3..4303472ff52 100644 --- a/addons/pos_self_order/controllers/orders.py +++ b/addons/pos_self_order/controllers/orders.py @@ -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: diff --git a/addons/pos_self_order/models/pos_order.py b/addons/pos_self_order/models/pos_order.py index 1ded0c55ea9..2d76dda0a44 100644 --- a/addons/pos_self_order/models/pos_order.py +++ b/addons/pos_self_order/models/pos_order.py @@ -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": [ diff --git a/addons/pos_self_order/static/src/self_order_bus_service.js b/addons/pos_self_order/static/src/self_order_bus_service.js index f42006eb7ae..1516fd8beee 100644 --- a/addons/pos_self_order/static/src/self_order_bus_service.js +++ b/addons/pos_self_order/static/src/self_order_bus_service.js @@ -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 = { diff --git a/addons/pos_self_order/static/src/self_order_service.js b/addons/pos_self_order/static/src/self_order_service.js index 96cd306d86d..c1720bb2685 100644 --- a/addons/pos_self_order/static/src/self_order_service.js +++ b/addons/pos_self_order/static/src/self_order_service.js @@ -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; }, }; diff --git a/addons/pos_self_order/static/tests/tours/self_order_after_each_cart_tour.js b/addons/pos_self_order/static/tests/tours/self_order_after_each_cart_tour.js index 6010271449d..346de06a17c 100644 --- a/addons/pos_self_order/static/tests/tours/self_order_after_each_cart_tour.js +++ b/addons/pos_self_order/static/tests/tours/self_order_after_each_cart_tour.js @@ -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"), diff --git a/addons/pos_self_order/static/tests/tours/tour_utils.js b/addons/pos_self_order/static/tests/tours/tour_utils.js index 24001d23148..3b0df2accd1 100644 --- a/addons/pos_self_order/static/tests/tours/tour_utils.js +++ b/addons/pos_self_order/static/tests/tours/tour_utils.js @@ -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: () => {