[REF] point_of_sale, pos_restaurant, *: stop using legacy environment

*: web

This commit removes the use of the legacy environment from the PoS, as
it is no longer needed. This will allow us to more easily use the new
unit testing infrastructure from web.

Part-of: odoo/odoo#117290
This commit is contained in:
Samuel Degueldre
2023-04-06 16:12:02 +02:00
parent 45d08db3c4
commit 8d35700aaf
19 changed files with 184 additions and 350 deletions
@@ -2,9 +2,8 @@
import { parse } from "web.field_utils";
import { barcodeService } from "@barcodes/barcode_service";
import { _t } from "web.core";
import { registry } from "@web/core/registry";
import { EventBus, onWillDestroy, useComponent, useExternalListener } from "@odoo/owl";
import { EventBus, onWillDestroy, useComponent } from "@odoo/owl";
const INPUT_KEYS = new Set(
["Delete", "Backspace", "+1", "+2", "+5", "+10", "+20", "+50"].concat(
@@ -36,7 +35,6 @@ const getDefaultConfig = () => ({
*
* Usage
* =====
* - Activate in the construction of root component. `NumberBuffer.activate()`
* - Use the buffer in a child component by calling `NumberBuffer.use(<config>)`
* in the constructor of the child component.
* - The component that `uses` the buffer has access to the following instance
@@ -58,11 +56,17 @@ const getDefaultConfig = () => ({
* - Write more integration tests. NumberPopup can be used as test component.
*/
class NumberBuffer extends EventBus {
constructor({ sound }) {
static serviceDependencies = ["sound", "localization"];
constructor() {
super();
this.setup(...arguments);
}
setup({ sound, localization }) {
this.isReset = false;
this.bufferHolderStack = [];
this.sound = sound;
this.defaultDecimalPoint = localization.decimalPoint;
window.addEventListener("keyup", this._onKeyboardInput.bind(this));
}
/**
* @returns {String} value of the buffer, e.g. '-95.79'
@@ -107,15 +111,6 @@ class NumberBuffer extends EventBus {
getFloat() {
return parse.float(this.get());
}
/**
* Add keyup listener to window via the useExternalListener hook.
* When the component calling this is unmounted, the listener is also
* removed from window.
*/
activate() {
this.defaultDecimalPoint = _t.database.parameters.decimal_point;
useExternalListener(window, "keyup", this._onKeyboardInput.bind(this));
}
/**
* @param {Object} config Use to setup the buffer
* @param {String|null} config.decimalPoint The decimal character.
@@ -324,9 +319,9 @@ class NumberBuffer extends EventBus {
}
export const numberBuffer = {
dependencies: ["sound"],
start(env, { sound }) {
return new NumberBuffer({ sound });
dependencies: NumberBuffer.serviceDependencies,
start(env, deps) {
return new NumberBuffer(deps);
},
};
@@ -1,15 +1,14 @@
/** @odoo-module */
import { PosGlobalState } from "@point_of_sale/js/models";
import legacyEnv from "web.env";
import { registry } from "@web/core/registry";
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
import { reactive, markRaw } from "@odoo/owl";
import { Reactive } from "@point_of_sale/utils";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { _t } from "@web/core/l10n/translation";
import { CashOpeningPopup } from "@point_of_sale/js/Popups/CashOpeningPopup";
import { sprintf } from "@web/core/utils/strings";
export class PosStore extends Reactive {
/** @type {'LOADING' | 'READY' | 'CLOSING'} */
@@ -18,7 +17,6 @@ export class PosStore extends Reactive {
loadingSkipButtonIsShown = false;
mainScreen = { name: null, component: null };
tempScreen = null;
legacyEnv = legacyEnv;
static serviceDependencies = [
"popup",
@@ -32,16 +30,16 @@ export class PosStore extends Reactive {
this.setup(...arguments);
}
// use setup instead of constructor because setup can be patched.
setup({ popup, orm, number_buffer, hardware_proxy, barcode_reader }) {
setup(env, { popup, orm, number_buffer, hardware_proxy, barcode_reader }) {
this.orm = orm;
this.popup = popup;
this.numberBuffer = number_buffer;
this.barcodeReader = barcode_reader;
this.globalState = new PosGlobalState({ orm, env, hardwareProxy: hardware_proxy });
this.hardwareProxy = hardware_proxy;
this.globalState = new PosGlobalState({
env: markRaw(legacyEnv),
hardwareProxy: hardware_proxy,
});
// FIXME POSREF: the hardwareProxy needs the pos and the pos needs the hardwareProxy. Maybe
// the hardware proxy should just be part of the pos service?
this.hardwareProxy.pos = this.globalState;
}
showScreen(name, props) {
@@ -77,7 +75,7 @@ export class PosStore extends Reactive {
// FIXME POSREF this looks like it's dead code.
reject({
title: _t("HTTPS connection to IoT Box failed"),
body: _.str.sprintf(
body: sprintf(
_t(
"Make sure you are using IoT Box v18.12 or higher. Navigate to %s to accept the certificate of your IoT Box."
),
@@ -100,40 +98,38 @@ export class PosStore extends Reactive {
window.location = "/web#action=point_of_sale.action_client_pos_menu";
}
if (this.globalState.db.get_orders().length) {
// If there are orders in the db left unsynced, we try to sync.
// If sync successful, close without asking.
// Otherwise, ask again saying that some orders are not yet synced.
try {
await this.globalState.push_orders();
// If there are orders in the db left unsynced, we try to sync.
// If sync successful, close without asking.
// Otherwise, ask again saying that some orders are not yet synced.
try {
await this.globalState.push_orders();
window.location = "/web#action=point_of_sale.action_client_pos_menu";
} catch (error) {
console.warn(error);
const reason = this.globalState.failed
? _t(
"Some orders could not be submitted to " +
"the server due to configuration errors. " +
"You can exit the Point of Sale, but do " +
"not close the session before the issue " +
"has been resolved."
)
: _t(
"Some orders could not be submitted to " +
"the server due to internet connection issues. " +
"You can exit the Point of Sale, but do " +
"not close the session before the issue " +
"has been resolved."
);
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: _t("Offline Orders"),
body: reason,
});
if (confirmed) {
// FIXME POSREF setting the location prevents the next render, the loading screen never shows
this.globalState.uiState = "CLOSING";
this.globalState.loadingSkipButtonIsShown = false;
window.location = "/web#action=point_of_sale.action_client_pos_menu";
} catch (error) {
console.warn(error);
const reason = this.globalState.failed
? _t(
"Some orders could not be submitted to " +
"the server due to configuration errors. " +
"You can exit the Point of Sale, but do " +
"not close the session before the issue " +
"has been resolved."
)
: _t(
"Some orders could not be submitted to " +
"the server due to internet connection issues. " +
"You can exit the Point of Sale, but do " +
"not close the session before the issue " +
"has been resolved."
);
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: _t("Offline Orders"),
body: reason,
});
if (confirmed) {
// FIXME POSREF setting the location prevents the next render, the loading screen never shows
this.globalState.uiState = "CLOSING";
this.globalState.loadingSkipButtonIsShown = false;
window.location = "/web#action=point_of_sale.action_client_pos_menu";
}
}
}
}
@@ -146,9 +142,9 @@ export class PosStore extends Reactive {
const currentPartner = currentOrder.get_partner();
if (currentPartner && currentOrder.getHasRefundLines()) {
this.popup.add(ErrorPopup, {
title: this.env._t("Can't change customer"),
body: _.str.sprintf(
this.env._t(
title: _t("Can't change customer"),
body: sprintf(
_t(
"This order already has refund lines for %s. We can't change the customer associated to it. Create a new order for the new customer."
),
currentPartner.name
@@ -218,7 +214,7 @@ export class PosStore extends Reactive {
export const posService = {
dependencies: PosStore.serviceDependencies,
start(env, deps) {
return reactive(new PosStore(deps));
return new PosStore(env, deps);
},
};
@@ -1,6 +0,0 @@
.pos .screen .content-cell{
height: 100%;
}
.pos .subwindow .subwindow-container{
height: 100%;
}
+54 -203
View File
@@ -1,242 +1,112 @@
/** @odoo-module */
import { loadCSS } from "@web/core/assets";
import { useService } from "@web/core/utils/hooks";
import { batched } from "@point_of_sale/js/utils";
import { debounce } from "@web/core/utils/timing";
import { throttleForAnimation } from "@web/core/utils/timing";
import { Transition } from "@web/core/transition";
import { MainComponentsContainer } from "@web/core/main_components_container";
import { WithEnv, ErrorHandler } from "@web/core/utils/components";
import { ErrorHandler } from "@web/core/utils/components";
import { Navbar } from "@point_of_sale/app/navbar/navbar";
// ChromeAdapter imports
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
import { registry } from "@web/core/registry";
import env from "web.env";
import { ErrorTracebackPopup } from "./Popups/ErrorTracebackPopup";
import {
onMounted,
onWillDestroy,
useExternalListener,
useSubEnv,
reactive,
onWillUnmount,
Component,
} from "@odoo/owl";
import { usePos } from "@point_of_sale/app/pos_hook";
import { useExternalListener, useSubEnv, reactive, onWillUnmount, Component } from "@odoo/owl";
/**
* Chrome is the root component of the PoS App.
*/
export class Chrome extends Component {
static template = "Chrome"; // FIXME POSREF namespace templates
static components = { Transition, MainComponentsContainer, WithEnv, ErrorHandler, Navbar };
static components = { Transition, MainComponentsContainer, ErrorHandler, Navbar };
setup() {
this.pos = usePos();
this.popup = useService("popup");
// BEGIN ChromeAdapter
ProductScreen.sortControlButtons();
const legacyActionManager = useService("legacy_action_manager");
this.batchedCustomerDisplayRender = batched(() => {
reactivePos.send_current_order_to_customer_facing_display();
});
const reactivePos = reactive(this.pos.globalState, this.batchedCustomerDisplayRender);
env.pos = reactivePos;
env.legacyActionManager = legacyActionManager;
// The proxy requires the instance of PosGlobalState to function properly.
this.hardwareProxy = useService("hardware_proxy");
this.hardwareProxy.pos = reactivePos;
const reactivePos = reactive(this.pos.globalState);
// TODO: Should we continue on exposing posmodel as global variable?
// Expose only the reactive version of `pos` when in debug mode.
window.posmodel = this.pos.globalState.debug ? reactivePos : this.pos.globalState;
this.wowlEnv = this.env;
// FIXME POSREF: make wowl services available in legacy env
Object.setPrototypeOf(env.services, this.wowlEnv.services);
this.env = env;
this.__owl__.childEnv = env;
window.posmodel = reactivePos;
// FIXME POSREF: remove
useSubEnv({
get isMobile() {
return window.innerWidth <= 768;
},
});
let currentIsMobile = this.env.isMobile;
const updateUI = debounce(() => {
if (this.env.isMobile !== currentIsMobile) {
currentIsMobile = this.env.isMobile;
this.render(true);
}
}, 15); // FIXME POSREF use throttleForAnimation?
useExternalListener(window, "resize", updateUI);
onWillUnmount(updateUI.cancel);
// END ChromeAdapter
super.setup();
useExternalListener(window, "beforeunload", this._onBeforeUnload);
useService("number_buffer").activate();
useSubEnv({
pos: reactive(
this.env.pos,
reactivePos,
batched(() => this.render(true)) // FIXME POSREF remove render(true)
),
});
onMounted(() => {
// remove default webclient handlers that induce click delay
// FIXME POSREF if these handlers shouldn't be there we should not load the files that add them.
$(document).off();
$(window).off();
$("html").off();
$("body").off();
let currentIsMobile = this.env.isMobile;
const updateUI = throttleForAnimation(() => {
if (this.env.isMobile !== currentIsMobile) {
currentIsMobile = this.env.isMobile;
// FIXME POSREF
this.render(true);
}
});
useExternalListener(window, "resize", updateUI);
onWillUnmount(updateUI.cancel);
onWillDestroy(() => {
try {
// FIXME POSREF is this needed?
this.env.pos.destroy();
} catch {
// throwing here causes loop
// prevent backspace from performing a 'back' navigation
document.addEventListener("keydown", (ev) => {
if (ev.key === "Backspace" && !ev.target.matches("input, textarea")) {
ev.preventDefault();
}
});
// This is not done in onWillStart because we want to show the loader immediately
this.start();
}
async start() {
await this.pos.globalState.load_server_data();
if (this.pos.globalState.config.use_proxy) {
await this.pos.connectToProxy();
}
this._closeOtherTabs();
this.pos.uiState = "READY";
const { name, props } = this.startScreen;
this.pos.showScreen(name, props);
this.runBackgroundTasks();
}
// GETTERS //
/**
* Startup screen can be based on pos config so the startup screen
* is only determined after pos data is completely loaded.
*
* NOTE: Wait for pos data to be completed before calling this getter.
*/
get startScreen() {
if (this.pos.uiState !== "READY") {
console.warn(
`Accessing startScreen of Chrome component before 'pos.uiState' to be 'READY' is not recommended.`
);
}
return { name: "ProductScreen" };
}
// CONTROL METHODS //
/**
* Call this function after the Chrome component is mounted.
* This will load pos and assign it to the environment.
*/
async start() {
// Little trick to avoid displaying the block ui during the POS models loading
// FIXME POSREF: use a silent RPC instead
const BlockUiFromRegistry = registry.category("main_components").get("BlockUI");
registry.category("main_components").remove("BlockUI");
try {
await this.env.pos.load_server_data();
if (this.env.pos.config.use_proxy) {
await this.pos.connectToProxy();
}
// Load the saved `env.pos.toRefundLines` from localStorage when
// the PosGlobalState is ready.
Object.assign(
this.env.pos.toRefundLines,
this.env.pos.db.load("TO_REFUND_LINES") || {}
);
this._buildChrome();
this._closeOtherTabs();
this.env.pos.selectedCategoryId =
this.env.pos.config.start_category && this.env.pos.config.iface_start_categ_id
? this.env.pos.config.iface_start_categ_id[0]
: 0;
this.pos.uiState = "READY";
const { name, props } = this.startScreen;
this.pos.showScreen(name, props);
setTimeout(() => this._runBackgroundTasks());
} catch (error) {
let title = "Unknown Error";
let body;
if (error.message && [100, 200, 404, -32098].includes(error.message.code)) {
// this is the signature of rpc error
if (error.message.code === -32098) {
title = "Network Failure (XmlHttpRequestError)";
body =
"The Point of Sale could not be loaded due to a network problem.\n" +
"Please check your internet connection.";
} else if (error.message.code === 200) {
title = error.message.data.message || this.env._t("Server Error");
body =
error.message.data.debug ||
this.env._t("The server encountered an error while receiving your order.");
}
} else if (error instanceof Error) {
title = error.message;
if (error.cause) {
body = error.cause.message;
} else {
body = error.stack;
}
}
return this.popup.add(ErrorTracebackPopup, { title, body, exitButtonIsShown: true });
}
registry.category("main_components").add("BlockUI", BlockUiFromRegistry);
// Subscribe to the changes in the models.
this.batchedCustomerDisplayRender();
}
_runBackgroundTasks() {
runBackgroundTasks() {
// push order in the background, no need to await
this.env.pos.push_orders();
this.pos.globalState.push_orders();
// Allow using the app even if not all the images are loaded.
// Basically, preload the images in the background.
this._preloadImages();
if (
this.env.pos.config.limited_partners_loading &&
this.env.pos.config.partner_load_background
) {
const {
limited_partners_loading,
partner_load_background,
limited_products_loading,
product_load_background,
} = this.pos.globalState.config;
if (limited_partners_loading && partner_load_background) {
// Wrap in fresh reactive: none of the reads during loading should subscribe to anything
reactive(this.env.pos).loadPartnersBackground();
reactive(this.pos.globalState).loadPartnersBackground();
}
if (
this.env.pos.config.limited_products_loading &&
this.env.pos.config.product_load_background
) {
if (limited_products_loading && product_load_background) {
// Wrap in fresh reactive: none of the reads during loading should subscribe to anything
reactive(this.env.pos)
.loadProductsBackground()
.then(() => {
this.render(true);
});
reactive(this.pos.globalState).loadProductsBackground();
}
}
/**
* Save `env.pos.toRefundLines` in localStorage on beforeunload - closing the
* browser, reloading or going to other page.
*/
_onBeforeUnload() {
this.env.pos.db.save("TO_REFUND_LINES", this.env.pos.toRefundLines);
}
_onSetSyncStatus({ detail: { status, pending } }) {
this.env.pos.synch.status = status;
this.env.pos.synch.pending = pending;
}
// MISC METHODS //
_preloadImages() {
for (const product of this.env.pos.db.get_product_by_category(0)) {
for (const product of this.pos.globalState.db.get_product_by_category(0)) {
const image = new Image();
image.src = `/web/image?model=product.product&field=image_128&id=${product.id}&unique=${product.write_date}`;
}
for (const category of Object.values(this.env.pos.db.category_by_id)) {
for (const category of Object.values(this.pos.globalState.db.category_by_id)) {
if (category.id == 0) {
continue;
}
@@ -249,34 +119,15 @@ export class Chrome extends Component {
image.src = `/point_of_sale/static/src/img/${imageName}`;
}
}
_buildChrome() {
if ($.browser.chrome) {
var chrome_version = $.browser.version.split(".")[0];
if (parseInt(chrome_version, 10) >= 50) {
loadCSS("/point_of_sale/static/src/css/chrome50.css");
}
}
if (this.env.pos.config.iface_big_scrollbars) {
this.pos.hasBigScrollBars = true;
}
this._disableBackspaceBack();
}
// prevent backspace from performing a 'back' navigation
_disableBackspaceBack() {
$(document).on("keydown", function (e) {
if (e.which === 8 && !$(e.target).is("input, textarea")) {
e.preventDefault();
}
});
}
/**
* Close other tabs that contain the same pos session.
*/
_closeOtherTabs() {
// FIXME POSREF use the bus?
localStorage["message"] = "";
localStorage["message"] = JSON.stringify({
message: "close_tabs",
session: this.env.pos.pos_session.id,
session: this.pos.globalState.pos_session.id,
});
window.addEventListener(
@@ -286,7 +137,7 @@ export class Chrome extends Component {
const msg = JSON.parse(event.newValue);
if (
msg.message === "close_tabs" &&
msg.session == this.env.pos.pos_session.id
msg.session == this.pos.globalState.pos_session.id
) {
console.info("POS / Session opened in another window. EXITING POS");
this.pos.closePos();
@@ -297,7 +148,7 @@ export class Chrome extends Component {
);
}
get showCashMoveButton() {
return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control;
return Boolean(this.pos.globalState?.config?.config?.cash_control);
}
/**
* Unmounts the tempScreen on error and dispatches the error in a separate
@@ -11,7 +11,8 @@
*/
export const ControlButtonsMixin = (x) => {
const controlButtonsToPosition = [];
const sortedControlButtons = [];
const orderedControlButtons = [];
let sortedControlButtons = [];
class Extended extends x {
get controlButtons() {
@@ -42,10 +43,11 @@ export const ControlButtonsMixin = (x) => {
// If no position is set, we just push it to the array.
if (!controlButton.position) {
sortedControlButtons.push(controlButton);
orderedControlButtons.push(controlButton);
} else {
controlButtonsToPosition.push(controlButton);
}
Extended.sortControlButtons();
};
/**
@@ -56,6 +58,7 @@ export const ControlButtonsMixin = (x) => {
* purposes, it is enough.
*/
Extended.sortControlButtons = function () {
sortedControlButtons = [...orderedControlButtons];
function setControlButton(locator, index, cb) {
if (locator == "replace") {
sortedControlButtons[index] = cb;
@@ -68,7 +71,7 @@ export const ControlButtonsMixin = (x) => {
function locate(cb) {
const [locator, reference] = cb.position;
const index = sortedControlButtons.findIndex((cb) => cb.name == reference);
return [locator, index, reference];
return [locator, index];
}
const cbMissingReference = [];
// 1. First pass. If the reference control button isn't there, collect it for second pass.
@@ -88,11 +91,8 @@ export const ControlButtonsMixin = (x) => {
// ii) They really have missing reference.
// Thus, we have to iterate the cb with missing reference in reverse.
for (const cb of cbMissingReference.reverse()) {
const [locator, index, reference] = locate(cb);
const [locator, index] = locate(cb);
if (index == -1) {
console.warn(
`'${cb.name}' is not properly position because '${reference}' is not found. Is '${reference}' spelled correctly?`
);
sortedControlButtons.push(cb);
} else {
setControlButton(locator, index, cb);
@@ -22,8 +22,8 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
this.pos = usePos();
this.popup = useService("popup");
this.orm = useService("orm");
this.action = useService("action");
this.hardwareProxy = useService("hardware_proxy");
this.legacyActionManager = useService("legacy_action_manager");
this.manualInputCashCount = false;
this.cashControl = this.pos.globalState.config.cash_control;
this.closeSessionClicked = false;
@@ -91,9 +91,9 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
}
}
async downloadSalesReport() {
await this.legacyActionManager.do_action("point_of_sale.sale_details_report", {
additional_context: {
active_ids: [this.pos.globalState.pos_session.id],
await this.action.doAction("point_of_sale.sale_details_report", {
additionalContext: {
active_ids: [this.env.pos.pos_session.id],
},
});
}
@@ -5,7 +5,6 @@ var _t = core._t;
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
import { useService } from "@web/core/utils/hooks";
import { useState } from "@odoo/owl";
import { localization } from "@web/core/l10n/localization";
export class NumberPopup extends AbstractAwaitablePopup {
static template = "NumberPopup";
@@ -31,6 +30,7 @@ export class NumberPopup extends AbstractAwaitablePopup {
*/
setup() {
super.setup();
this.localization = useService("localization");
let startingBuffer = "";
if (typeof this.props.startingValue === "number" && this.props.startingValue > 0) {
startingBuffer = this.props.startingValue
@@ -46,7 +46,7 @@ export class NumberPopup extends AbstractAwaitablePopup {
});
}
get decimalSeparator() {
return localization.decimalPoint;
return this.localization.decimalPoint;
}
get inputBuffer() {
if (this.state.buffer === null) {
@@ -30,6 +30,7 @@ export class PaymentScreen extends Component {
this.pos = usePos();
this.orm = useService("orm");
this.popup = useService("popup");
this.action = useService("action");
this.notification = useService("pos_notification");
this.hardwareProxy = useService("hardware_proxy");
this.payment_methods_from_config = this.env.pos.payment_methods.filter((method) =>
@@ -233,8 +234,8 @@ export class PaymentScreen extends Component {
// 2. Invoice.
if (this.currentOrder.is_to_invoice()) {
if (syncOrderResult.length) {
await this.env.legacyActionManager.do_action("account.account_invoices", {
additional_context: {
await this.action.doAction("account.account_invoices", {
additionalContext: {
active_ids: [syncOrderResult[0].account_move],
},
});
@@ -2,6 +2,7 @@
import { PSNumpadInputButton } from "./PSNumpadInputButton";
import { Component } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class PaymentScreenNumpad extends Component {
static components = { PSNumpadInputButton };
@@ -9,6 +10,6 @@ export class PaymentScreenNumpad extends Component {
setup() {
super.setup();
this.decimalPoint = this.env._t.database.parameters.decimal_point;
this.decimalPoint = useService("localization").decimalPoint;
}
}
@@ -16,6 +16,7 @@ export class NumpadWidget extends Component {
};
setup() {
this.numberBuffer = useService("number_buffer");
this.localization = useService("localization");
}
get hasPriceControlRights() {
return (
@@ -43,6 +44,6 @@ export class NumpadWidget extends Component {
this.numberBuffer.sendKey(key);
}
get decimalSeparator() {
return this.env._t.database.parameters.decimal_point;
return this.localization.decimalPoint;
}
}
@@ -15,6 +15,7 @@ export class InvoiceButton extends Component {
this.invoiceButton = useRef("invoice-button");
this.popup = useService("popup");
this.orm = useService("orm");
this.action = useService("action");
}
get isAlreadyInvoiced() {
if (!this.props.order) {
@@ -38,8 +39,8 @@ export class InvoiceButton extends Component {
{ load: false }
);
if (orderWithInvoice && orderWithInvoice.account_move) {
await this.env.legacyActionManager.do_action("account.account_invoices", {
additional_context: {
await this.action.doAction("account.account_invoices", {
additionalContext: {
active_ids: [orderWithInvoice.account_move],
},
});
+46 -47
View File
@@ -193,7 +193,7 @@ export class PosGlobalState extends PosModel {
};
}
async load_product_uom_unit() {
const uom_id = await this.env.services.orm.call("ir.model.data", "check_object_reference", [
const uom_id = await this.orm.call("ir.model.data", "check_object_reference", [
"uom",
"product_uom_unit",
]);
@@ -204,11 +204,18 @@ export class PosGlobalState extends PosModel {
await this.load_product_uom_unit();
await this.load_orders();
this.set_start_order();
Object.assign(this.toRefundLines, this.db.load("TO_REFUND_LINES") || {});
window.addEventListener("beforeunload", () =>
this.db.save("TO_REFUND_LINES", this.toRefundLines)
);
const { start_category, iface_start_categ_id } = this.config;
this.selectedCategoryId = (start_category && iface_start_categ_id?.[0]) || 0;
this.hasBigScrollBars = this.config.iface_big_scrollbars;
this.markReady();
}
async load_server_data() {
const loadedData = await this.env.services.orm.call("pos.session", "load_pos_data", [
const loadedData = await this.orm.silent.call("pos.session", "load_pos_data", [
[odoo.pos_session_id],
]);
await this._processData(loadedData);
@@ -370,32 +377,28 @@ export class PosGlobalState extends PosModel {
// updated partners, and fails if not
async load_new_partners() {
const search_params = { domain: this.prepare_new_partners_domain() };
if (this.env.pos.config.limited_partners_loading) {
if (this.config.limited_partners_loading) {
search_params["order"] = "write_date desc";
if (this.env.pos.config.partner_load_background) {
search_params["limit"] = this.env.pos.config.limited_partners_amount || 1;
if (this.config.partner_load_background) {
search_params["limit"] = this.config.limited_partners_amount || 1;
} else {
search_params["limit"] = 1;
}
}
// FIXME POSREF TIMEOUT 3000
const partners = await this.env.services.orm.silent.call(
const partners = await this.orm.silent.call(
"pos.session",
"get_pos_ui_res_partner_by_params",
[[odoo.pos_session_id], search_params]
);
if (this.env.pos.config.partner_load_background) {
if (this.config.partner_load_background) {
this.loadPartnersBackground(
search_params["domain"],
this.env.pos.config.limited_partners_amount || 1,
this.config.limited_partners_amount || 1,
"write_date desc"
);
}
if (this.addPartners(partners)) {
return true;
} else {
return false;
}
return this.addPartners(partners);
}
setSelectedCategoryId(categoryId) {
@@ -557,7 +560,7 @@ export class PosGlobalState extends PosModel {
}
}
}
const products = await this.env.services.orm.call(
const products = await this.orm.call(
"pos.session",
"get_pos_ui_product_product_by_params",
[odoo.pos_session_id, { domain: [["id", "in", [...missingProductIds]]] }]
@@ -568,7 +571,7 @@ export class PosGlobalState extends PosModel {
async _loadPartners(partnerIds) {
if (partnerIds.length > 0) {
// FIXME POSREF TIMEOUT
const fetchedPartners = await this.env.services.orm.silent.call(
const fetchedPartners = await this.orm.silent.call(
"pos.session",
"get_pos_ui_res_partner_by_params",
[[odoo.pos_session_id], { domain: [["id", "in", partnerIds]] }]
@@ -593,7 +596,7 @@ export class PosGlobalState extends PosModel {
let page = 0;
let products = [];
do {
products = await this.env.services.orm.silent.call(
products = await this.orm.silent.call(
"pos.session",
"get_pos_ui_product_product_by_params",
[
@@ -614,7 +617,7 @@ export class PosGlobalState extends PosModel {
let i = 0;
let partners = [];
do {
partners = await this.env.services.orm.silent.call(
partners = await this.orm.silent.call(
"pos.session",
"get_pos_ui_res_partner_by_params",
[
@@ -642,7 +645,7 @@ export class PosGlobalState extends PosModel {
this.set_synch("connecting", removedOrdersIds.length);
try {
const removeOrdersResponseData = await this.env.services.orm.silent.call(
const removeOrdersResponseData = await this.orm.silent.call(
"pos.order",
"remove_from_ui",
[removedOrdersIds]
@@ -736,7 +739,7 @@ export class PosGlobalState extends PosModel {
return message;
}
async _getOrdersJson() {
return await this.env.services.orm.call("pos.order", "get_draft_share_order_ids", [], {
return await this.orm.call("pos.order", "get_draft_share_order_ids", [], {
config_id: this.config.id,
});
}
@@ -762,11 +765,10 @@ export class PosGlobalState extends PosModel {
return message;
}
async _getPricelistJson(pricelistsToGet) {
return await this.env.services.orm.call(
"pos.session",
"get_pos_ui_product_pricelists_by_ids",
[[odoo.pos_session_id], pricelistsToGet]
);
return await this.orm.call("pos.session", "get_pos_ui_product_pricelists_by_ids", [
[odoo.pos_session_id],
pricelistsToGet,
]);
}
_addPosPricelists(pricelistsJson) {
if (!this.config.use_pricelist) {
@@ -805,11 +807,10 @@ export class PosGlobalState extends PosModel {
return message;
}
async _getFiscalPositionJson(fiscalPositionToGet) {
return await this.env.services.orm.call(
"pos.session",
"get_pos_ui_account_fiscal_positions_by_ids",
[[odoo.pos_session_id], fiscalPositionToGet]
);
return await this.orm.call("pos.session", "get_pos_ui_account_fiscal_positions_by_ids", [
[odoo.pos_session_id],
fiscalPositionToGet,
]);
}
_addPosFiscalPosition(fiscalPositionJson) {
this.fiscal_positions.push(...fiscalPositionJson);
@@ -830,11 +831,12 @@ export class PosGlobalState extends PosModel {
const order = this.get_order();
// check back-end method `get_product_info_pos` to see what it returns
// We do this so it's easier to override the value returned and use it in the component template later
const productInfo = await this.env.services.orm.call(
"product.product",
"get_product_info_pos",
[[product.id], product.get_price(order.pricelist, quantity), quantity, this.config.id]
);
const productInfo = await this.orm.call("product.product", "get_product_info_pos", [
[product.id],
product.get_price(order.pricelist, quantity),
quantity,
this.config.id,
]);
const priceWithoutTax = productInfo["all_prices"]["price_without_tax"];
const margin = priceWithoutTax - product.standard_price;
@@ -865,11 +867,9 @@ export class PosGlobalState extends PosModel {
};
}
async getClosePosInfo() {
const closingData = await this.env.services.orm.call(
"pos.session",
"get_closing_control_data",
[[this.pos_session.id]]
);
const closingData = await this.orm.call("pos.session", "get_closing_control_data", [
[this.pos_session.id],
]);
const ordersDetails = closingData.orders_details;
const paymentsAmount = closingData.payments_amount;
const payLaterAmount = closingData.pay_later_amount;
@@ -1057,7 +1057,7 @@ export class PosGlobalState extends PosModel {
}
// we try to send the order. silent prevents a spinner if it takes too long. (unless we are sending an invoice,
// then we want to notify the user that we are waiting on something )
const orm = options.to_invoice ? this.env.services.orm : this.env.services.orm.silent;
const orm = options.to_invoice ? this.orm : this.orm.silent;
// FIXME POSREF timeout
// const timeout = typeof options.timeout === "number" ? options.timeout : 30000 * orders.length;
return orm
@@ -1555,17 +1555,16 @@ export class PosGlobalState extends PosModel {
*/
async _addProducts(ids, setAvailable = true) {
if (setAvailable) {
await this.env.services.orm.write("product.product", ids, { available_in_pos: true });
await this.orm.write("product.product", ids, { available_in_pos: true });
}
const product = await this.env.services.orm.call(
"pos.session",
"get_pos_ui_product_product_by_params",
[odoo.pos_session_id, { domain: [["id", "in", ids]] }]
);
const product = await this.orm.call("pos.session", "get_pos_ui_product_product_by_params", [
odoo.pos_session_id,
{ domain: [["id", "in", ids]] },
]);
this._loadProductProduct(product);
}
async refreshTotalDueOfPartner(partner) {
const partnerWithUpdatedTotalDue = await this.env.services.orm.searchRead(
const partnerWithUpdatedTotalDue = await this.orm.searchRead(
"res.partner",
[["id", "=", partner.id]],
["total_due"]
+2 -2
View File
@@ -9,7 +9,7 @@
*/
export function batched(callback) {
let called = false;
return async () => {
return async (...args) => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
@@ -20,7 +20,7 @@ export function batched(callback) {
// within the callback will proceed only after resetting called to false, and have
// a chance to execute the callback again
Promise.resolve().then(() => (called = false));
callback();
callback(...args);
}
};
}
@@ -38,9 +38,7 @@
</div>
</Transition>
</div>
<WithEnv env="wowlEnv">
<MainComponentsContainer/>
</WithEnv>
<MainComponentsContainer/>
</t>
</templates>
@@ -26,7 +26,6 @@ QUnit.test("simple fast inputs with capture in between", async function (assert)
setup() {
this.state = useState({ buffer: "" });
this.numberBuffer = useService("number_buffer");
this.numberBuffer.activate();
this.numberBuffer.use({
state: this.state,
});
@@ -156,12 +156,8 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
}
}
if (payload.coupon_report) {
for (const report_entry of Object.entries(payload.coupon_report)) {
await this.env.legacyActionManager.do_action(report_entry[0], {
additional_context: {
active_ids: report_entry[1],
},
});
for (const [actionId, active_ids] of Object.entries(payload.coupon_report)) {
await this.action.doAction(actionId, { additionalContext: { active_ids } });
}
}
order.new_coupon_info = payload.new_coupon_info;
@@ -45,7 +45,7 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
}
},
isInterfacePrinter() {
return this.env.pos.config.iface_printers;
return this.config.iface_printers;
},
addSubmitOrderButton() {
return this.config.module_pos_restaurant && this.unwatched.printers.length;
@@ -87,6 +87,7 @@ export const localizationService = {
thousandsSep: userLocalization.thousands_sep,
weekStart: userLocalization.week_start,
});
return localization;
},
};
@@ -43,6 +43,7 @@ export function makeFakeLocalizationService(config = {}) {
name: "localization",
start: async (env) => {
env._t = _t;
return localization;
},
};
}