[FIX] pos_restaurant: synchronize orders in TicketScreen
Before this commit: if more than one user work on a PoS restaurant, the synced validated orders wouldn't remove from other user devices. Steps to reproduce the issue: 1. Create a PoS resturant 2. Login to the PoS with two different browser 3. Create some orders with some products in one of the browsers 4. Complete the orders in another browser => Orders will not remove from the first browser The solution is to retrieve the latest orders of every table when going to the TicketScreen opw-2925193 closes odoo/odoo#100353 Related: odoo/enterprise#32871 Signed-off-by: Trinh Jacky (trj) <trj@odoo.com>
This commit is contained in:
@@ -13,8 +13,8 @@ class PosOrder(models.Model):
|
||||
return fields
|
||||
|
||||
@api.model
|
||||
def get_table_draft_orders(self, table_id):
|
||||
table_orders = super().get_table_draft_orders(table_id)
|
||||
def get_table_draft_orders(self, table_ids):
|
||||
table_orders = super().get_table_draft_orders(table_ids)
|
||||
for order in table_orders:
|
||||
if order['employee_id']:
|
||||
order['employee_id'] = order['employee_id'][0] if order['employee_id'] else False
|
||||
|
||||
@@ -149,7 +149,7 @@ class PosOrder(models.Model):
|
||||
]
|
||||
|
||||
@api.model
|
||||
def get_table_draft_orders(self, table_id):
|
||||
def get_table_draft_orders(self, table_ids):
|
||||
"""Generate an object of all draft orders for the given table.
|
||||
|
||||
Generate and return an JSON object with all draft orders for the given table, to send to the
|
||||
@@ -160,7 +160,7 @@ class PosOrder(models.Model):
|
||||
:returns: list -- list of dict representing the table orders
|
||||
"""
|
||||
table_orders = self.search_read(
|
||||
domain=[('state', '=', 'draft'), ('table_id', '=', table_id)],
|
||||
domain=[('state', '=', 'draft'), ('table_id', 'in', table_ids)],
|
||||
fields=self._get_fields_for_draft_order())
|
||||
|
||||
self._get_order_lines(table_orders)
|
||||
|
||||
@@ -3,9 +3,35 @@ odoo.define('pos_restaurant.TicketButton', function (require) {
|
||||
|
||||
const TicketButton = require('point_of_sale.TicketButton');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const { isConnectionError } = require('point_of_sale.utils');
|
||||
|
||||
const PosResTicketButton = (TicketButton) =>
|
||||
class extends TicketButton {
|
||||
async onClick() {
|
||||
if (this.env.pos.config.iface_floorplan && !this.props.isTicketScreenShown && !this.env.pos.table) {
|
||||
try {
|
||||
this.env.pos.setLoadingOrderState(true);
|
||||
await this.env.pos._syncAllOrdersFromServer();
|
||||
} catch (error) {
|
||||
if (isConnectionError(error)) {
|
||||
await this.showPopup('OfflineErrorPopup', {
|
||||
title: this.env._t('Offline'),
|
||||
body: this.env._t('Due to a connection error, the orders are not synchronized.'),
|
||||
});
|
||||
} else {
|
||||
this.showPopup('ErrorPopup', {
|
||||
title: this.env._t('Unknown error'),
|
||||
body: error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
this.env.pos.setLoadingOrderState(false);
|
||||
this.showScreen('TicketScreen');
|
||||
}
|
||||
} else {
|
||||
super.onClick();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If no table is set to pos, which means the current main screen
|
||||
* is floor screen, then the order count should be based on all the orders.
|
||||
|
||||
@@ -105,13 +105,13 @@ const PosRestaurantPosGlobalState = (PosGlobalState) => class PosRestaurantPosGl
|
||||
}
|
||||
this.config.iface_printers = !!this.unwatched.printers.length;
|
||||
}
|
||||
async _getTableOrdersFromServer(tableId) {
|
||||
async _getTableOrdersFromServer(tableIds) {
|
||||
this.set_synch('connecting', 1);
|
||||
try {
|
||||
const orders = await this.env.services.rpc({
|
||||
model: 'pos.order',
|
||||
method: 'get_table_draft_orders',
|
||||
args: [tableId],
|
||||
args: [tableIds],
|
||||
}, {
|
||||
timeout: TIMEOUT,
|
||||
shadow: true,
|
||||
@@ -199,21 +199,31 @@ const PosRestaurantPosGlobalState = (PosGlobalState) => class PosRestaurantPosGl
|
||||
*/
|
||||
async _syncTableOrdersFromServer(tableId) {
|
||||
await this._removeOrdersFromServer(); // in case we were offline and we deleted orders in the mean time
|
||||
const ordersJsons = await this._getTableOrdersFromServer(tableId);
|
||||
const ordersJsons = await this._getTableOrdersFromServer([tableId]);
|
||||
const tableOrders = this.getTableOrders(tableId);
|
||||
|
||||
tableOrders.forEach(order => {
|
||||
this._replaceOrders(tableOrders, ordersJsons);
|
||||
}
|
||||
async _syncAllOrdersFromServer() {
|
||||
await this._removeOrdersFromServer(); // in case we were offline and we deleted orders in the mean time
|
||||
const tableIds = [].concat(...this.floors.map(floor => floor.tables.map(table => table.id)));
|
||||
const ordersJsons = await this._getTableOrdersFromServer(tableIds); // get all orders
|
||||
await this._syncTableOrdersToServer(); // to prevent losing the transferred orders
|
||||
const allOrders = [...this.get_order_list()];
|
||||
this._replaceOrders(allOrders, ordersJsons);
|
||||
}
|
||||
_replaceOrders(ordersToReplace, newOrdersJsons) {
|
||||
ordersToReplace.forEach(order => {
|
||||
// We don't remove the validated orders because we still want to see them in the ticket screen.
|
||||
// Orders in 'ReceiptScreen' or 'TipScreen' are validated orders.
|
||||
if (order.server_id && !order.finalized && !this.transferredOrdersSet.has(order)){
|
||||
this.removeOrder(order, false);
|
||||
}
|
||||
});
|
||||
ordersJsons.forEach(json => {
|
||||
newOrdersJsons.forEach(json => {
|
||||
// Because of the offline feature, some draft orders fetched from the backend will appear
|
||||
// to belong in different table, but in fact they are already moved.
|
||||
const transferredOrder = [...this.transferredOrdersSet].find(order => order.uid === json.uid)
|
||||
const isSameTable = transferredOrder && transferredOrder.tableId === tableId;
|
||||
const isSameTable = transferredOrder && transferredOrder.tableId === json.tableId;
|
||||
if (isSameTable) {
|
||||
// this means we transferred back to the original table, we'll prioritize the server state
|
||||
this.removeOrder(transferredOrder, false);
|
||||
@@ -224,6 +234,9 @@ const PosRestaurantPosGlobalState = (PosGlobalState) => class PosRestaurantPosGl
|
||||
}
|
||||
});
|
||||
}
|
||||
setLoadingOrderState(bool) {
|
||||
this.loadingOrderState = bool;
|
||||
}
|
||||
loadRestaurantFloor() {
|
||||
// we do this in the front end due to the circular/recursive reference needed
|
||||
// Ignore floorplan features if no floor specified.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('order-row')]//div[@name='delete']" position="before">
|
||||
<div t-if="env.pos.config.iface_floorplan" class="col" name="table">
|
||||
<t t-if="order.table">
|
||||
<t t-if="order.tableId">
|
||||
<div t-if="env.isMobile">Table</div>
|
||||
<div><t t-esc="getTable(order)"></t></div>
|
||||
</t>
|
||||
|
||||
@@ -56,7 +56,7 @@ odoo.define('pos_restaurant.tour.TipScreen', function (require) {
|
||||
Chrome.do.backToFloor();
|
||||
FloorScreen.check.orderCountSyncedInTableIs('T5', '1');
|
||||
Chrome.do.clickTicketButton();
|
||||
TicketScreen.check.nthRowContains('4', 'Tipping');
|
||||
TicketScreen.check.nthRowContains('3', 'Tipping');
|
||||
|
||||
// Tip 20% on order1
|
||||
TicketScreen.do.selectOrder('-0001');
|
||||
|
||||
Reference in New Issue
Block a user