[REF] pos*: move show(Temp)Screen, make number_buffer and popup services
*: l10n_fr_pos_cert, point_of_sale, pos_discount, pos_hr, pos_hr_restaurant, pos_loyalty, pos_mercury, pos_restaurant, pos_sale, pos_sale_product_configurator, pos_six, pos_stripe Continuing to move things out of the Chrome god component, this commit does the following things: - moves the showScreen and showTempScreen methods and the corresponding closing methods into the pos store so that components that need them can access them through the pos service in a more explicit way. To that end, these methods have been removed from PosComponent and Gui. - introduces a popup service instead of using a bus in the env, this is mechanically very similar but closer to what's done in web and hopefully a first step to using the dialog service instead. It also removes the showPopup method from the PosComponent and Gui, to make components depending on the popup service explicit. - makes the number buffer into a service instead of being a directly imported singleton, which will enable it to be used with hooks instead at a later point, and allows easier dependency injection in tests. - removes showNotification from the PosComponent and Gui, the notification system was already converted to a service in a previous commit for the same reasons as showPopup and the end goal is to use the notification service from web to reduce duplication. closes odoo/odoo#111844 Related: odoo/enterprise#36668 Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
@@ -13,7 +13,7 @@ patch(Chrome.prototype, "l10n_fr_pos_cert.Chrome", {
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
if (limitDate.getTime() < now) {
|
||||
const info = await this.env.pos.getClosePosInfo();
|
||||
this.showPopup(ClosePosPopup, { info: info });
|
||||
this.popup.add(ClosePosPopup, { info });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { PosGlobalState, Order, Orderline } from "@point_of_sale/js/models";
|
||||
import core from "web.core";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
@@ -12,7 +11,7 @@ patch(PosGlobalState.prototype, "l10n_fr_pos_cert.PosGlobalState", {
|
||||
is_french_country() {
|
||||
var french_countries = ["FR", "MF", "MQ", "NC", "PF", "RE", "GF", "GP", "TF"];
|
||||
if (!this.company.country) {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Missing Country"),
|
||||
body: _.str.sprintf(
|
||||
_t("The company %s doesn't have a country set."),
|
||||
@@ -60,7 +59,7 @@ patch(Order.prototype, "l10n_fr_pos_cert.Order", {
|
||||
this.pos.is_french_country() &&
|
||||
this.get_orderlines().length
|
||||
) {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Fiscal Data Module error"),
|
||||
body: _t("Deleting of orders is not allowed."),
|
||||
});
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { parse } from "web.field_utils";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { Transition } from "@web/core/transition";
|
||||
import { Draggable } from "@point_of_sale/js/Misc/Draggable";
|
||||
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { OrderImportPopup } from "@point_of_sale/js/Popups/OrderImportPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { useBus, useService } from "@web/core/utils/hooks";
|
||||
|
||||
import { onMounted, onWillUnmount, useRef, useState } from "@odoo/owl";
|
||||
import { useRef, useState } from "@odoo/owl";
|
||||
|
||||
export class DebugWidget extends PosComponent {
|
||||
static components = { Transition, Draggable };
|
||||
@@ -17,6 +16,9 @@ export class DebugWidget extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.debug = useService("debug");
|
||||
this.popup = useService("popup");
|
||||
const numberBuffer = useService("number_buffer");
|
||||
useBus(numberBuffer, "buffer-update", this._onBufferUpdate);
|
||||
this.state = useState({
|
||||
barcodeInput: "",
|
||||
weightInput: "",
|
||||
@@ -49,14 +51,6 @@ export class DebugWidget extends PosComponent {
|
||||
}).bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
numberBuffer.on("buffer-update", this, this._onBufferUpdate);
|
||||
});
|
||||
|
||||
onWillUnmount(() => {
|
||||
numberBuffer.off("buffer-update", this, this._onBufferUpdate);
|
||||
});
|
||||
}
|
||||
toggleWidget() {
|
||||
this.state.isShown = !this.state.isShown;
|
||||
@@ -82,7 +76,7 @@ export class DebugWidget extends PosComponent {
|
||||
await this.env.barcode_reader.scan(ean);
|
||||
}
|
||||
async deleteOrders() {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Delete Paid Orders ?"),
|
||||
body: this.env._t(
|
||||
"This operation will permanently destroy all paid orders from the local storage. You will lose all the data. This operation cannot be undone."
|
||||
@@ -94,7 +88,7 @@ export class DebugWidget extends PosComponent {
|
||||
}
|
||||
}
|
||||
async deleteUnpaidOrders() {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Delete Unpaid Orders ?"),
|
||||
body: this.env._t(
|
||||
"This operation will destroy all unpaid orders in the browser. You will lose all the unsaved data and exit the point of sale. This operation cannot be undone."
|
||||
@@ -148,14 +142,14 @@ export class DebugWidget extends PosComponent {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const report = this.env.pos.import_orders(await file.text());
|
||||
await this.showPopup(OrderImportPopup, { report });
|
||||
await this.popup.add(OrderImportPopup, { report });
|
||||
}
|
||||
}
|
||||
refreshDisplay() {
|
||||
this.env.proxy.message("display_refresh", {});
|
||||
}
|
||||
_onBufferUpdate(buffer) {
|
||||
this.state.buffer = buffer;
|
||||
_onBufferUpdate({ detail: value }) {
|
||||
this.state.buffer = value;
|
||||
}
|
||||
get bufferRepr() {
|
||||
return `"${this.state.buffer}"`;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { registry } from "@web/core/registry";
|
||||
import { odooExceptionTitleMap } from "@web/core/errors/error_dialogs";
|
||||
import { ConnectionLostError, RPCError } from "@web/core/network/rpc_service";
|
||||
import { Gui, GuiNotReadyError } from "@point_of_sale/js/Gui";
|
||||
import { GuiNotReadyError } from "@point_of_sale/js/Gui";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { ErrorTracebackPopup } from "@point_of_sale/js/Popups/ErrorTracebackPopup";
|
||||
import { OfflineErrorPopup } from "@point_of_sale/js/Popups/OfflineErrorPopup";
|
||||
@@ -18,9 +18,9 @@ function rpcErrorHandler(env, error, originalError) {
|
||||
const { message, data } = error;
|
||||
if (odooExceptionTitleMap.has(error.exceptionName)) {
|
||||
const title = odooExceptionTitleMap.get(error.exceptionName).toString();
|
||||
Gui.showPopup(ErrorPopup, { title, body: data.message });
|
||||
env.services.popup.add(ErrorPopup, { title, body: data.message });
|
||||
} else {
|
||||
Gui.showPopup(ErrorTracebackPopup, {
|
||||
env.services.popup.add(ErrorTracebackPopup, {
|
||||
title: message,
|
||||
body: data.message + "\n" + data.debug + "\n",
|
||||
});
|
||||
@@ -33,7 +33,7 @@ registry.category("error_handlers").add("rpcErrorHandler", rpcErrorHandler);
|
||||
function offlineErrorHandler(env, error, originalError) {
|
||||
error = identifyError(originalError);
|
||||
if (error instanceof ConnectionLostError) {
|
||||
Gui.showPopup(OfflineErrorPopup, {
|
||||
env.services.popup.add(OfflineErrorPopup, {
|
||||
title: env._t("Couldn't connect to the server"),
|
||||
body: env._t(
|
||||
"The operation couldn't be completed because you are offline. Check your internet connection and try again."
|
||||
@@ -45,14 +45,14 @@ function offlineErrorHandler(env, error, originalError) {
|
||||
registry.category("error_handlers").add("offlineErrorHandler", offlineErrorHandler);
|
||||
|
||||
function defaultErrorHandler(env, error, originalError) {
|
||||
error = identifyError(originalError);
|
||||
originalError = identifyError(originalError);
|
||||
if (error instanceof Error) {
|
||||
Gui.showPopup(ErrorTracebackPopup, {
|
||||
title: `${error.name}: ${error.message}`,
|
||||
env.services.popup.add(ErrorTracebackPopup, {
|
||||
title: `${originalError.name}: ${originalError.message}`,
|
||||
body: error.traceback,
|
||||
});
|
||||
} else {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
env.services.popup.add(ErrorPopup, {
|
||||
title: env._t("Unknown Error"),
|
||||
body: env._t("Unable to show information about this error."),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
import { CashierName } from "@point_of_sale/js/ChromeWidgets/CashierName";
|
||||
|
||||
+12
-4
@@ -4,7 +4,7 @@ import { useListener } from "@web/core/utils/hooks";
|
||||
import { parse } from "web.field_utils";
|
||||
import { barcodeService } from "@barcodes/barcode_service";
|
||||
import { _t } from "web.core";
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const { EventBus, onMounted, onWillUnmount, useComponent, useExternalListener } = owl;
|
||||
const INPUT_KEYS = new Set(
|
||||
@@ -60,10 +60,11 @@ const getDefaultConfig = () => ({
|
||||
* - Write more integration tests. NumberPopup can be used as test component.
|
||||
*/
|
||||
class NumberBuffer extends EventBus {
|
||||
constructor() {
|
||||
constructor({ sound }) {
|
||||
super();
|
||||
this.isReset = false;
|
||||
this.bufferHolderStack = [];
|
||||
this.sound = sound;
|
||||
}
|
||||
/**
|
||||
* @returns {String} value of the buffer, e.g. '-95.79'
|
||||
@@ -294,7 +295,7 @@ class NumberBuffer extends EventBus {
|
||||
if (isFirstInput) {
|
||||
this.state.buffer = "" + input;
|
||||
} else if (this.state.buffer.length > 12) {
|
||||
Gui.playSound("bell");
|
||||
this.sound.play("bell");
|
||||
} else {
|
||||
this.state.buffer += input;
|
||||
}
|
||||
@@ -317,4 +318,11 @@ class NumberBuffer extends EventBus {
|
||||
}
|
||||
}
|
||||
|
||||
export const numberBuffer = new NumberBuffer();
|
||||
export const numberBuffer = {
|
||||
dependencies: ["sound"],
|
||||
start(env, { sound }) {
|
||||
return new NumberBuffer({ sound });
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("number_buffer", numberBuffer);
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.PopupContainer" owl="1">
|
||||
<div t-if="Object.keys(props.popups).length > 0" class="popups">
|
||||
<t t-foreach="props.popups" t-as="popup" t-key="popup">
|
||||
<div t-if="popup_last || popup_value.props.keepBehind" role="dialog" class="modal-dialog">
|
||||
<t t-component="popup_value.component" t-props="popup_value.props" isActive="popup_last" />
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,62 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { reactive } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
export class PopupContainer extends PosComponent {
|
||||
static template = "point_of_sale.PopupContainer";
|
||||
setup() {
|
||||
// FIXME POSREF: remove this after PosComponent is removed.
|
||||
this.__owl__.childEnv = usePos().legacyEnv;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME POSREF: probably should use the main dialog service from web long term.
|
||||
export const popupService = {
|
||||
start() {
|
||||
const popups = reactive({});
|
||||
registry.category("main_components").add("PopupContainer", {
|
||||
Component: PopupContainer,
|
||||
props: { popups },
|
||||
});
|
||||
let popupId = 0;
|
||||
return {
|
||||
/**
|
||||
* Displays a popup over the interface.
|
||||
*
|
||||
* @param {AbstractAwaitablePopup} component the popup component to
|
||||
* open
|
||||
* @param {Object} props props for the popup component
|
||||
* @returns {Promise<any>} a Promise that fulfills when the popup
|
||||
* is closed (confirmed or canceled)
|
||||
*/
|
||||
add(component, props) {
|
||||
return new Promise((resolve) => {
|
||||
const id = ++popupId;
|
||||
popups[id] = {
|
||||
component,
|
||||
props: {
|
||||
keepBehind: false,
|
||||
cancelKey: "Escape",
|
||||
confirmKey: "Enter",
|
||||
// FIXME POSREF assigning the default props by hand defeats the point of default props
|
||||
...component.defaultProps,
|
||||
...props,
|
||||
id,
|
||||
resolve,
|
||||
close(response) {
|
||||
delete popups[id];
|
||||
resolve(response);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
// exposed so that overrides can use it, should not be read from outside. Maybe we should just implement closePopupsButError directly in pos instead of pos_restaurant
|
||||
popups,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("popup", popupService);
|
||||
@@ -0,0 +1,11 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useState } from "@odoo/owl";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* @returns {import("./pos_store").PosStore}
|
||||
*/
|
||||
export function usePos() {
|
||||
return useState(useService("pos"));
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import { PosGlobalState } from "@point_of_sale/js/models";
|
||||
import { pos_env as legacyEnv } from "@point_of_sale/js/pos_env";
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { reactive, useState, markRaw } from "@odoo/owl";
|
||||
import { reactive, markRaw } from "@odoo/owl";
|
||||
import { Reactive } from "@point_of_sale/utils";
|
||||
|
||||
export class PosStore {
|
||||
export class PosStore extends Reactive {
|
||||
/** @type {'LOADING' | 'READY' | 'CLOSING'} */
|
||||
uiState = "LOADING";
|
||||
hasBigScrollBars = false;
|
||||
@@ -15,26 +15,52 @@ export class PosStore {
|
||||
mainScreen = { name: null, component: null };
|
||||
tempScreen = null;
|
||||
legacyEnv = legacyEnv;
|
||||
constructor() {
|
||||
globalState = new PosGlobalState({ env: markRaw(legacyEnv) });
|
||||
|
||||
static serviceDependencies = ["popup"];
|
||||
constructor({ popup }) {
|
||||
super();
|
||||
this.popup = popup;
|
||||
this.setup();
|
||||
}
|
||||
// use setup instead of constructor because setup can be patched.
|
||||
setup() {
|
||||
this.globalState = new PosGlobalState({ env: markRaw(legacyEnv) });
|
||||
setup() {}
|
||||
|
||||
showScreen(name, props) {
|
||||
const component = registry.category("pos_screens").get(name);
|
||||
this.mainScreen = { component, props };
|
||||
// Save the screen to the order so that it is shown again when the order is selected.
|
||||
if (component.storeOnOrder ?? true) {
|
||||
this.globalState.get_order()?.set_screen_data({ name, props });
|
||||
}
|
||||
}
|
||||
|
||||
closeScreen() {
|
||||
const { name: screenName } = this.globalState.get_order().get_screen_data();
|
||||
this.showScreen(screenName);
|
||||
}
|
||||
|
||||
// FIXME POSREF get rid of temp screens entirely?
|
||||
showTempScreen(name, props = {}) {
|
||||
return new Promise((resolve) => {
|
||||
this.tempScreen = {
|
||||
name,
|
||||
component: registry.category("pos_screens").get(name),
|
||||
props: { ...props, resolve },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
closeTempScreen() {
|
||||
this.tempScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
export const posService = {
|
||||
start() {
|
||||
return reactive(new PosStore());
|
||||
dependencies: PosStore.serviceDependencies,
|
||||
start(env, deps) {
|
||||
return reactive(new PosStore(deps));
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("pos", posService);
|
||||
|
||||
/**
|
||||
* @returns {PosStore}
|
||||
*/
|
||||
export function usePos() {
|
||||
return useState(useService("pos"));
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ import { loadCSS } from "@web/core/assets";
|
||||
import { useListener, useBus, useService } from "@web/core/utils/hooks";
|
||||
import BarcodeParser from "barcodes.BarcodeParser";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { IndependentToOrderScreen } from "@point_of_sale/js/Misc/IndependentToOrderScreen";
|
||||
import { batched } from "@point_of_sale/js/utils";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { Transition } from "@web/core/transition";
|
||||
import { MainComponentsContainer } from "@web/core/main_components_container";
|
||||
import { WithEnv } from "@web/core/utils/components";
|
||||
import { WithEnv, ErrorHandler } from "@web/core/utils/components";
|
||||
import { Navbar } from "@point_of_sale/app/navbar/navbar";
|
||||
|
||||
// ChromeAdapter imports
|
||||
@@ -31,7 +29,7 @@ import {
|
||||
reactive,
|
||||
onWillUnmount,
|
||||
} from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
/**
|
||||
* Chrome is the root component of the PoS App.
|
||||
@@ -40,6 +38,7 @@ export class Chrome extends PosComponent {
|
||||
static template = "Chrome"; // FIXME POSREF namespace templates
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
// BEGIN ChromeAdapter
|
||||
ProductScreen.sortControlButtons();
|
||||
const legacyActionManager = useService("legacy_action_manager");
|
||||
@@ -59,7 +58,14 @@ export class Chrome extends PosComponent {
|
||||
window.posmodel = this.pos.globalState.debug ? reactivePos : this.pos.globalState;
|
||||
|
||||
this.wowlEnv = this.env;
|
||||
for (const service of ["pos", "sound", "debug", "pos_notification"]) {
|
||||
for (const service of [
|
||||
"pos",
|
||||
"sound",
|
||||
"debug",
|
||||
"pos_notification",
|
||||
"number_buffer",
|
||||
"popup",
|
||||
]) {
|
||||
env.services[service] = this.wowlEnv.services[service];
|
||||
}
|
||||
|
||||
@@ -83,15 +89,12 @@ export class Chrome extends PosComponent {
|
||||
|
||||
super.setup();
|
||||
useExternalListener(window, "beforeunload", this._onBeforeUnload);
|
||||
useListener("show-main-screen", this.__showScreen);
|
||||
useListener("show-temp-screen", this.__showTempScreen);
|
||||
useListener("close-temp-screen", this.__closeTempScreen);
|
||||
useListener("close-pos", this._closePos);
|
||||
useListener("loading-skip-callback", () => this.env.proxy.stop_searching());
|
||||
useListener("set-sync-status", this._onSetSyncStatus);
|
||||
useListener("connect-to-proxy", this.connect_to_proxy);
|
||||
useBus(this.env.posbus, "start-cash-control", this.openCashControl);
|
||||
numberBuffer.activate();
|
||||
useService("number_buffer").activate();
|
||||
|
||||
useSubEnv({
|
||||
pos: reactive(
|
||||
@@ -170,7 +173,8 @@ export class Chrome extends PosComponent {
|
||||
? this.env.pos.config.iface_start_categ_id[0]
|
||||
: 0;
|
||||
this.pos.uiState = "READY";
|
||||
this._showStartScreen();
|
||||
const { name, props } = this.startScreen;
|
||||
this.pos.showScreen(name, props);
|
||||
setTimeout(() => this._runBackgroundTasks());
|
||||
} catch (error) {
|
||||
let title = "Unknown Error";
|
||||
@@ -198,7 +202,7 @@ export class Chrome extends PosComponent {
|
||||
}
|
||||
}
|
||||
|
||||
return this.showPopup(ErrorTracebackPopup, { title, body, exitButtonIsShown: true });
|
||||
return this.popup.add(ErrorTracebackPopup, { title, body, exitButtonIsShown: true });
|
||||
}
|
||||
registry.category("main_components").add("BlockUI", BlockUiFromRegistry);
|
||||
|
||||
@@ -290,7 +294,7 @@ export class Chrome extends PosComponent {
|
||||
|
||||
openCashControl() {
|
||||
if (this.shouldShowCashControl()) {
|
||||
this.showPopup(CashOpeningPopup, { keepBehind: true });
|
||||
this.popup.add(CashOpeningPopup, { keepBehind: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,52 +306,6 @@ export class Chrome extends PosComponent {
|
||||
|
||||
// EVENT HANDLERS //
|
||||
|
||||
_showStartScreen() {
|
||||
const { name, props } = this.startScreen;
|
||||
this.showScreen(name, props);
|
||||
}
|
||||
_getSavedScreen(order) {
|
||||
return order.get_screen_data();
|
||||
}
|
||||
__showTempScreen(event) {
|
||||
const { name, props, resolve } = event.detail;
|
||||
this.pos.tempScreen = {
|
||||
name,
|
||||
component: registry.category("pos_screens").get(name),
|
||||
props: { ...props, resolve },
|
||||
};
|
||||
}
|
||||
__closeTempScreen() {
|
||||
this.pos.tempScreen = null;
|
||||
}
|
||||
__showScreen({ detail: { name, props = {} } }) {
|
||||
const component = registry.category("pos_screens").get(name);
|
||||
// 1. Set the information of the screen to display.
|
||||
this.pos.mainScreen = { name, component, props };
|
||||
|
||||
// 2. Save the screen to the order.
|
||||
// - This screen is shown when the order is selected.
|
||||
if (
|
||||
!(component.prototype instanceof IndependentToOrderScreen) &&
|
||||
name !== "ReprintReceiptScreen"
|
||||
) {
|
||||
this._setScreenData(name, props);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the latest screen to the current order. This is done so that
|
||||
* when the order is selected again, the ui returns to the latest screen
|
||||
* saved in the order.
|
||||
*
|
||||
* @param {string} name Screen name
|
||||
* @param {Object} props props for the Screen component
|
||||
*/
|
||||
_setScreenData(name, props) {
|
||||
const order = this.env.pos.get_order();
|
||||
if (order) {
|
||||
order.set_screen_data({ name, props });
|
||||
}
|
||||
}
|
||||
async _closePos() {
|
||||
// If pos is not properly loaded, we just go back to /web without
|
||||
// doing anything in the order data.
|
||||
@@ -379,7 +337,7 @@ export class Chrome extends PosComponent {
|
||||
"not close the session before the issue " +
|
||||
"has been resolved."
|
||||
);
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Offline Orders"),
|
||||
body: reason,
|
||||
});
|
||||
@@ -473,6 +431,17 @@ export class Chrome extends PosComponent {
|
||||
get showCashMoveButton() {
|
||||
return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control;
|
||||
}
|
||||
/**
|
||||
* Unmounts the tempScreen on error and dispatches the error in a separate
|
||||
* stack so that it can be handled by the error service and display an error
|
||||
* popup.
|
||||
*
|
||||
* @param {any} err the error that was thrown in the temp screen.
|
||||
*/
|
||||
onTempScreenError(err) {
|
||||
this.pos.tempScreen = null;
|
||||
Promise.reject(err);
|
||||
}
|
||||
}
|
||||
Object.defineProperty(Chrome, "components", {
|
||||
get() {
|
||||
@@ -481,6 +450,7 @@ Object.defineProperty(Chrome, "components", {
|
||||
Transition,
|
||||
MainComponentsContainer,
|
||||
WithEnv,
|
||||
ErrorHandler,
|
||||
Navbar,
|
||||
},
|
||||
PosComponent.components
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { _lt } from "web.core";
|
||||
import { renderToString } from "@web/core/utils/render";
|
||||
import { CashMovePopup } from "../Popups/CashMovePopup";
|
||||
import { ErrorPopup } from "../Popups/ErrorPopup";
|
||||
import { CashMovePopup } from "@point_of_sale/js/Popups/CashMovePopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const TRANSLATED_CASH_MOVE_TYPE = {
|
||||
in: _lt("in"),
|
||||
@@ -14,8 +15,14 @@ const TRANSLATED_CASH_MOVE_TYPE = {
|
||||
export class CashMoveButton extends PosComponent {
|
||||
static template = "point_of_sale.CashMoveButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.popup = useService("popup");
|
||||
this.notification = useService("pos_notification");
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const { confirmed, payload } = await this.showPopup(CashMovePopup);
|
||||
const { confirmed, payload } = await this.popup.add(CashMovePopup);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
@@ -23,7 +30,7 @@ export class CashMoveButton extends PosComponent {
|
||||
const translatedType = TRANSLATED_CASH_MOVE_TYPE[type];
|
||||
const formattedAmount = this.env.pos.format_currency(amount);
|
||||
if (!amount) {
|
||||
return this.showNotification(
|
||||
return this.notification.add(
|
||||
_.str.sprintf(this.env._t("Cash in/out of %s is ignored."), formattedAmount),
|
||||
3000
|
||||
);
|
||||
@@ -40,13 +47,13 @@ export class CashMoveButton extends PosComponent {
|
||||
});
|
||||
const printResult = await this.env.proxy.printer.print_receipt(renderedReceipt);
|
||||
if (!printResult.successful) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: printResult.message.title,
|
||||
body: printResult.message.body,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.showNotification(
|
||||
this.notification.add(
|
||||
_.str.sprintf(this.env._t("Successfully made a cash %s of %s."), type, formattedAmount),
|
||||
3000
|
||||
);
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ClosePosPopup } from "../Popups/ClosePosPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ClosePosPopup } from "@point_of_sale/js/Popups/ClosePosPopup";
|
||||
|
||||
// Previously HeaderButtonWidget
|
||||
// This is the close session button
|
||||
export class HeaderButton extends PosComponent {
|
||||
static template = "HeaderButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const info = await this.env.pos.getClosePosInfo();
|
||||
this.showPopup(ClosePosPopup, { info: info, keepBehind: true });
|
||||
this.popup.add(ClosePosPopup, { info: info, keepBehind: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { renderToString } from "@web/core/utils/render";
|
||||
import { ErrorPopup } from "../Popups/ErrorPopup";
|
||||
|
||||
export class SaleDetailsButton extends PosComponent {
|
||||
static template = "SaleDetailsButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
// IMPROVEMENT: Perhaps put this logic in a parent component
|
||||
// so that for unit testing, we can check if this simple
|
||||
@@ -25,7 +31,7 @@ export class SaleDetailsButton extends PosComponent {
|
||||
);
|
||||
const printResult = await this.env.proxy.printer.print_receipt(report);
|
||||
if (!printResult.successful) {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: printResult.message.title,
|
||||
body: printResult.message.body,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { TicketScreen } from "@point_of_sale/js/Screens/TicketScreen/TicketScreen";
|
||||
|
||||
export class TicketButton extends PosComponent {
|
||||
static template = "TicketButton";
|
||||
@@ -13,11 +14,11 @@ export class TicketButton extends PosComponent {
|
||||
if (this.isTicketScreenShown) {
|
||||
this.env.posbus.trigger("ticket-button-clicked");
|
||||
} else {
|
||||
this.showScreen("TicketScreen");
|
||||
this.pos.showScreen("TicketScreen");
|
||||
}
|
||||
}
|
||||
get isTicketScreenShown() {
|
||||
return this.pos.mainScreen.name === "TicketScreen";
|
||||
return this.pos.mainScreen.component === TicketScreen;
|
||||
}
|
||||
get count() {
|
||||
if (this.env.pos) {
|
||||
|
||||
@@ -1,31 +1,7 @@
|
||||
/** @odoo-module */
|
||||
const { status } = owl;
|
||||
|
||||
/**
|
||||
* This module bridges the data classes (such as those defined in
|
||||
* models.js) to the view (Component) but not vice versa.
|
||||
*
|
||||
* The idea is to be able to perform side-effects to the user interface
|
||||
* during calculation. Think of console.log during times we want to see
|
||||
* the result of calculations. This is no different, except that instead
|
||||
* of printing something in the console, we access a method in the user
|
||||
* interface then the user interface reacts, e.g. calling `showPopup`.
|
||||
*
|
||||
* This however can be dangerous to the user interface as it can be possible
|
||||
* that a rendered component is destroyed during the calculation. Because of
|
||||
* this, we are going to limit external ui controls to those safe ones to
|
||||
* use such as:
|
||||
* - `showPopup`
|
||||
* - `showTempScreen`
|
||||
*
|
||||
* IMPROVEMENT: After all, this Gui layer seems to be a good abstraction because
|
||||
* there is a complete decoupling between data and view despite the data being
|
||||
* able to use selected functionalities in the view layer. More formalized
|
||||
* implementation is welcome.
|
||||
*/
|
||||
|
||||
const config = {};
|
||||
|
||||
/**
|
||||
* Call this when the user interface is ready. Provide the component
|
||||
* that will be used to control the ui.
|
||||
@@ -33,21 +9,11 @@ const config = {};
|
||||
*/
|
||||
export const configureGui = ({ component }) => {
|
||||
config.component = component;
|
||||
config.availableMethods = new Set([
|
||||
"showScreen",
|
||||
"showPopup",
|
||||
"showTempScreen",
|
||||
"playSound",
|
||||
"setSyncStatus",
|
||||
"showNotification",
|
||||
]);
|
||||
config.availableMethods = new Set(["setSyncStatus"]);
|
||||
};
|
||||
|
||||
export class GuiNotReadyError extends Error {}
|
||||
/**
|
||||
* Import this and consume like so: `Gui.showPopup(<PopupName>, <props>)`.
|
||||
* Like you would call `showPopup` in a component.
|
||||
*/
|
||||
// FIXME POSREF: remove this entirely.
|
||||
export const Gui = new Proxy(config, {
|
||||
get(target, key) {
|
||||
const { component, availableMethods } = target;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ConfirmPopup } from "../Popups/ConfirmPopup";
|
||||
import { ErrorPopup } from "../Popups/ErrorPopup";
|
||||
|
||||
@@ -16,6 +17,7 @@ export class AbstractReceiptScreen extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.orderReceipt = useRef("order-receipt");
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
async _printReceipt() {
|
||||
if (this.env.proxy.printer) {
|
||||
@@ -25,7 +27,7 @@ export class AbstractReceiptScreen extends PosComponent {
|
||||
if (printResult.successful) {
|
||||
return true;
|
||||
} else {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: printResult.message.title,
|
||||
body: "Do you want to print using the web printer?",
|
||||
});
|
||||
@@ -46,7 +48,7 @@ export class AbstractReceiptScreen extends PosComponent {
|
||||
window.print();
|
||||
return true;
|
||||
} catch {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Printing is not supported on some browsers"),
|
||||
body: this.env._t(
|
||||
"Printing is not supported on some browsers due to no default printing protocol " +
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
|
||||
export class IndependentToOrderScreen extends PosComponent {
|
||||
static storeOnOrder = false;
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.pos = usePos();
|
||||
}
|
||||
close() {
|
||||
const order = this.env.pos.get_order();
|
||||
const { name: screenName } = order.get_screen_data();
|
||||
this.showScreen(screenName);
|
||||
this.pos.showScreen(screenName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useExternalListener } from "@odoo/owl";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useBus } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* Implement this abstract class by extending it like so:
|
||||
@@ -22,7 +22,7 @@ import { useBus } from "@web/core/utils/hooks";
|
||||
* The concrete popup can now be instantiated and be awaited for
|
||||
* the user's response like so:
|
||||
* ```js
|
||||
* const { confirmed, payload } = await this.showPopup(ConcretePopup);
|
||||
* const { confirmed, payload } = await this.popup.add(ConcretePopup);
|
||||
* // based on the implementation above,
|
||||
* // if confirmed, payload = 'result'
|
||||
* // otherwise, payload = null
|
||||
@@ -30,25 +30,24 @@ import { useBus } from "@web/core/utils/hooks";
|
||||
*/
|
||||
export class AbstractAwaitablePopup extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
if (this.props.confirmKey) {
|
||||
useBus(this.env.posbus, `confirm-popup-${this.props.id}`, this.confirm);
|
||||
super.setup(...arguments);
|
||||
useExternalListener(window, "keyup", this._onWindowKeyup);
|
||||
}
|
||||
_onWindowKeyup(event) {
|
||||
if (!this.props.isActive || ["INPUT", "TEXTAREA"].includes(event.target.tagName)) {
|
||||
return;
|
||||
}
|
||||
if (this.props.cancelKey) {
|
||||
useBus(this.env.posbus, `cancel-popup-${this.props.id}`, this.cancel);
|
||||
if (event.key === this.props.cancelKey) {
|
||||
this.cancel();
|
||||
} else if (event.key === this.props.confirmKey) {
|
||||
this.confirm();
|
||||
}
|
||||
}
|
||||
async confirm() {
|
||||
this.env.posbus.trigger("close-popup", {
|
||||
popupId: this.props.id,
|
||||
response: { confirmed: true, payload: await this.getPayload() },
|
||||
});
|
||||
this.props.close({ confirmed: true, payload: await this.getPayload() });
|
||||
}
|
||||
cancel() {
|
||||
this.env.posbus.trigger("close-popup", {
|
||||
popupId: this.props.id,
|
||||
response: { confirmed: false, payload: null },
|
||||
});
|
||||
this.props.close({ confirmed: false, payload: null });
|
||||
}
|
||||
/**
|
||||
* Override this in the concrete popup implementation to set the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { MoneyDetailsPopup } from "./MoneyDetailsPopup";
|
||||
|
||||
const { useState } = owl;
|
||||
@@ -17,6 +18,7 @@ export class CashOpeningPopup extends AbstractAwaitablePopup {
|
||||
notes: "",
|
||||
openingCash: this.env.pos.pos_session.cash_register_balance_start || 0,
|
||||
});
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
//@override
|
||||
async confirm() {
|
||||
@@ -30,7 +32,7 @@ export class CashOpeningPopup extends AbstractAwaitablePopup {
|
||||
super.confirm();
|
||||
}
|
||||
async openDetailsPopup() {
|
||||
const { confirmed, payload } = await this.showPopup(MoneyDetailsPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(MoneyDetailsPopup, {
|
||||
moneyDetails: this.moneyDetails,
|
||||
total: this.manualInputCashCount ? 0 : this.state.openingCash,
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@ import { SaleDetailsButton } from "../ChromeWidgets/SaleDetailsButton";
|
||||
import { ConfirmPopup } from "./ConfirmPopup";
|
||||
import { ErrorPopup } from "./ErrorPopup";
|
||||
import { MoneyDetailsPopup } from "./MoneyDetailsPopup";
|
||||
import { AlertPopup } from "./AlertPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { useState } = owl;
|
||||
|
||||
@@ -16,6 +18,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.manualInputCashCount = false;
|
||||
this.cashControl = this.env.pos.config.cash_control;
|
||||
this.closeSessionClicked = false;
|
||||
@@ -31,7 +34,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
if (!this.cashControl || !this.hasDifference()) {
|
||||
this.closeSession();
|
||||
} else if (this.hasUserAuthority()) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Payments Difference"),
|
||||
body: this.env._t(
|
||||
"Do you want to accept payments difference and post a profit/loss journal entry?"
|
||||
@@ -41,7 +44,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
this.closeSession();
|
||||
}
|
||||
} else {
|
||||
await this.showPopup(ConfirmPopup, {
|
||||
await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Payments Difference"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t(
|
||||
@@ -61,7 +64,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
}
|
||||
}
|
||||
async openDetailsPopup() {
|
||||
const { confirmed, payload } = await this.showPopup(MoneyDetailsPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(MoneyDetailsPopup, {
|
||||
moneyDetails: this.moneyDetails,
|
||||
total: this.manualInputCashCount
|
||||
? 0
|
||||
@@ -182,7 +185,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
throw error;
|
||||
} else {
|
||||
// FIXME POSREF: why are we catching errors here but not anywhere else in this method?
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Closing session error"),
|
||||
body: this.env._t(
|
||||
"An error has occurred when trying to close the session.\n" +
|
||||
@@ -201,14 +204,14 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
|
||||
let title = "";
|
||||
|
||||
if (response.type == "alert") {
|
||||
popupType = "AlertPopup";
|
||||
popupType = AlertPopup;
|
||||
title = response.title ? response.title : "";
|
||||
} else {
|
||||
popupType = "ErrorPopup";
|
||||
popupType = ErrorPopup;
|
||||
title = "Error";
|
||||
}
|
||||
|
||||
await this.showPopup(popupType, { title: title, body: response.message });
|
||||
await this.popup.add(popupType, { title: title, body: response.message });
|
||||
if (response.redirect) {
|
||||
window.location = "/web#action=point_of_sale.action_client_pos_menu";
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const { useState } = owl;
|
||||
*
|
||||
* // supply the items to the popup and wait for user's response
|
||||
* // when user pressed `confirm` in the popup, the changes he made will be returned by the showPopup function.
|
||||
* const { confirmed, payload: newNames } = await this.showPopup(EditListPopup, {
|
||||
* const { confirmed, payload: newNames } = await this.popup.add(EditListPopup, {
|
||||
* title: "Can you confirm this item?",
|
||||
* array: names })
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { _lt } from "@web/core/l10n/translation";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
// formerly ErrorPopupWidget
|
||||
export class ErrorPopup extends AbstractAwaitablePopup {
|
||||
@@ -16,8 +17,9 @@ export class ErrorPopup extends AbstractAwaitablePopup {
|
||||
setup() {
|
||||
super.setup();
|
||||
owl.onMounted(this.onMounted);
|
||||
this.sound = useService("sound");
|
||||
}
|
||||
onMounted() {
|
||||
this.playSound("error");
|
||||
this.sound.play("error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ import core from "web.core";
|
||||
var _t = core._t;
|
||||
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
|
||||
import { Draggable } from "../Misc/Draggable";
|
||||
|
||||
@@ -44,7 +43,8 @@ export class NumberPopup extends AbstractAwaitablePopup {
|
||||
.replace(".", this.decimalSeparator);
|
||||
}
|
||||
this.state = useState({ buffer: startingBuffer, toStartOver: this.props.isInputSelected });
|
||||
numberBuffer.use({
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.use({
|
||||
nonKeyboardInputEvent: "numpad-click-input",
|
||||
triggerAtEnter: "accept-input",
|
||||
triggerAtEscape: "close-this-popup",
|
||||
@@ -65,7 +65,7 @@ export class NumberPopup extends AbstractAwaitablePopup {
|
||||
}
|
||||
}
|
||||
confirm(event) {
|
||||
if (numberBuffer.get()) {
|
||||
if (this.numberBuffer.get()) {
|
||||
super.confirm();
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,6 @@ export class NumberPopup extends AbstractAwaitablePopup {
|
||||
this.trigger("numpad-click-input", { key });
|
||||
}
|
||||
getPayload() {
|
||||
return numberBuffer.get();
|
||||
return this.numberBuffer.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ export class OfflineErrorPopup extends ErrorPopup {
|
||||
title: _lt("Offline Error"),
|
||||
body: _lt("Either the server is inaccessible or browser is not connected online."),
|
||||
};
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
if (this.constructor.dontShow) {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
dontShowAgain() {
|
||||
this.constructor.dontShow = true;
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useBus } from "@web/core/utils/hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { useExternalListener, useState } from "@odoo/owl";
|
||||
|
||||
/**
|
||||
* This component is responsible in controlling the popups. It does so
|
||||
* by coordinating with them thru the `env.posbus`. The basic steps follow:
|
||||
* 1. `showPopup` method triggers `show-popup` event resulting to the
|
||||
* mounting of the requested popup.
|
||||
* 2. When the popup is shown, the `confirm`/`cancel` method of the popup
|
||||
* will be called after the popup is used. `confirming`/`cancelling`
|
||||
* will trigger the `close-popup`, which this component also listens to,
|
||||
* resulting to closing of the popup.
|
||||
*
|
||||
* Furthermore, Pressing `confirmKey`/`cancelKey` which defaults to
|
||||
* 'Enter'/'Escape', will automatically `confirm`/`cancel` the `topPopup`.
|
||||
* This behavior is accomplished by listening to `keyup` event of the window.
|
||||
* When the `confirmKey`/`cancelKey` of the `topPopup` is pressed,
|
||||
* 'cancel-popup-{top-popup-id}'/'confirm-popup-{top-popup-id}' will be triggered
|
||||
* and since the popup is listening to that event (@see AbstractAwaitablePopup),
|
||||
* it will result to the call of `confirm`/`cancel` method.
|
||||
*
|
||||
* @typedef {{ id: number, resolve: Function, keepBehind?: boolean, cancelKey?: string, confirmKey?: string }} BasePopupProps
|
||||
* @typedef {{ name: string, component: AbstractAwaitablePopup, props: BasePopupProps, key: string }} Popup
|
||||
*/
|
||||
export class PosPopupController extends PosComponent {
|
||||
static template = "point_of_sale.PosPopupController";
|
||||
static components = {};
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
// FIXME POSREF stop overwriting env
|
||||
this.env = this.pos.legacyEnv;
|
||||
this.__owl__.childEnv = this.env;
|
||||
super.setup();
|
||||
useBus(this.env.posbus, "show-popup", this._showPopup);
|
||||
useBus(this.env.posbus, "close-popup", this._closePopup);
|
||||
useExternalListener(window, "keyup", this._onWindowKeyup);
|
||||
this.popups = useState([]);
|
||||
}
|
||||
/**
|
||||
* A popup can be cancelled/confirmed with 'Escape'/'Enter' key by default.
|
||||
* Also, if it's not the top popup, it is hidden from the view.
|
||||
* This can be overridden by the default props of the popop component
|
||||
* and the props used in requesting to show the popup.
|
||||
*
|
||||
* @param {AbstractAwaitablePopup} popupComponent
|
||||
* @param {Object} props
|
||||
* @returns {BasePopupProps}
|
||||
*/
|
||||
_showPopup(event) {
|
||||
let { id, component, props, resolve } = event.detail;
|
||||
props = Object.assign(props || {}, { id, resolve });
|
||||
if (component.dontShow) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
this.popups.push({
|
||||
component,
|
||||
props: {
|
||||
keepBehind: false,
|
||||
cancelKey: "Escape",
|
||||
confirmKey: "Enter",
|
||||
// FIXME POSREF assigning the default props by hand defeats the point of default props
|
||||
...component.defaultProps,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
}
|
||||
_closePopup(event) {
|
||||
const { popupId, response } = event.detail;
|
||||
const index = this.popups.findIndex((popup) => popup.props.id == popupId);
|
||||
if (index != -1) {
|
||||
const popup = this.popups[index];
|
||||
popup.props.resolve(response);
|
||||
this.popups.splice(index, 1);
|
||||
}
|
||||
}
|
||||
_onWindowKeyup(event) {
|
||||
const eventIsFromInputField =
|
||||
event.target.tagName === "INPUT" || event.target.tagName === "TEXTAREA";
|
||||
const shouldHandleKey = this.topPopup && !eventIsFromInputField;
|
||||
if (!shouldHandleKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === this.topPopup.props.cancelKey) {
|
||||
this.env.posbus.trigger(`cancel-popup-${this.topPopup.props.id}`);
|
||||
} else if (event.key === this.topPopup.props.confirmKey) {
|
||||
this.env.posbus.trigger(`confirm-popup-${this.topPopup.props.id}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns {boolean} Hide the element of this component when this returns false.
|
||||
*/
|
||||
isShown() {
|
||||
return this.popups.length > 0;
|
||||
}
|
||||
get topPopup() {
|
||||
return this.popups[this.popups.length - 1];
|
||||
}
|
||||
/**
|
||||
* By default, only show the top popup. But always show a popup if
|
||||
* `keepBehind` props is true. Meaning, if you have 2 popups, and
|
||||
* the bottom popup has `keepBehind = true`, then the bottom popup
|
||||
* will be visible if it's not blocked in the view by the top popup.
|
||||
*
|
||||
* @param {Popup} popup
|
||||
* @returns {boolean}
|
||||
*/
|
||||
shouldShow(popup) {
|
||||
return this.topPopup === popup || popup.props.keepBehind;
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("main_components").add("PosPopupController", {
|
||||
Component: PosPopupController,
|
||||
});
|
||||
@@ -1,54 +1,9 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { LegacyComponent } from "@web/legacy/legacy_component";
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
export class PosComponent extends LegacyComponent {
|
||||
static components = {};
|
||||
setup() {
|
||||
this.notification = useService("pos_notification");
|
||||
this.sound = useService("sound");
|
||||
}
|
||||
/**
|
||||
* This function is available to all Components that inherit this class.
|
||||
* The goal of this function is to show an awaitable dialog (popup) that
|
||||
* returns a response after user interaction. See the following for quick
|
||||
* demonstration:
|
||||
*
|
||||
* ```
|
||||
* async getUserName() {
|
||||
* const userResponse = await this.showPopup(TextInputPopup, { title: 'What is your name?' });
|
||||
* // at this point, the TextInputPopup is displayed. Depending on how the popup is defined,
|
||||
* // say the input contains the name, the result of the interaction with the user is
|
||||
* // saved in `userResponse`.
|
||||
* console.log(userResponse); // logs { confirmed: true, payload: <name> }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {String} name Name of the popup component
|
||||
* @param {Object} props Object that will be used to render to popup
|
||||
*/
|
||||
showPopup(component, props) {
|
||||
return new Promise((resolve) => {
|
||||
this.env.posbus.trigger("show-popup", { component, props, resolve, id: nextId++ });
|
||||
});
|
||||
}
|
||||
showTempScreen(name, props) {
|
||||
return new Promise((resolve) => {
|
||||
this.trigger("show-temp-screen", { name, props, resolve });
|
||||
});
|
||||
}
|
||||
showScreen(name, props) {
|
||||
this.trigger("show-main-screen", { name, props });
|
||||
}
|
||||
/**
|
||||
* @param {String} name 'bell' | 'error'
|
||||
*/
|
||||
playSound(name) {
|
||||
this.sound.play(name);
|
||||
}
|
||||
/**
|
||||
* Control the SyncNotification component.
|
||||
* @param {String} status 'connected' | 'connecting' | 'disconnected' | 'error'
|
||||
@@ -57,7 +12,4 @@ export class PosComponent extends LegacyComponent {
|
||||
setSyncStatus(status, pending) {
|
||||
this.trigger("set-sync-status", { status, pending });
|
||||
}
|
||||
showNotification(message, duration) {
|
||||
this.notification.add(message, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { _t } from "web.core";
|
||||
import { getDataURLFromFile } from "web.utils";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { onMounted, onWillUnmount } = owl;
|
||||
|
||||
@@ -12,6 +13,7 @@ export class PartnerDetailsEdit extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.intFields = ["country_id", "state_id", "property_product_pricelist"];
|
||||
const partner = this.props.partner;
|
||||
this.changes = {
|
||||
@@ -66,7 +68,7 @@ export class PartnerDetailsEdit extends PosComponent {
|
||||
}
|
||||
}
|
||||
if ((!this.props.partner.name && !processedChanges.name) || processedChanges.name === "") {
|
||||
return this.showPopup(ErrorPopup, {
|
||||
return this.popup.add(ErrorPopup, {
|
||||
title: _t("A Customer Name Is Required"),
|
||||
});
|
||||
}
|
||||
@@ -76,7 +78,7 @@ export class PartnerDetailsEdit extends PosComponent {
|
||||
async uploadImage(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file.type.match(/image.*/)) {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Unsupported File Format"),
|
||||
body: this.env._t(
|
||||
"Only web-compatible Image formats such as .png or .jpeg are supported."
|
||||
@@ -124,7 +126,7 @@ export class PartnerDetailsEdit extends PosComponent {
|
||||
const img = new Image();
|
||||
img.addEventListener("load", () => resolve(img));
|
||||
img.addEventListener("error", () => {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Loading Image Error"),
|
||||
body: this.env._t("Encountered error when loading image. Please try again."),
|
||||
});
|
||||
|
||||
@@ -4,10 +4,11 @@ import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
|
||||
import { PartnerLine } from "./PartnerLine";
|
||||
import { PartnerDetailsEdit } from "./PartnerDetailsEdit";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const { onWillUnmount, useRef } = owl;
|
||||
|
||||
@@ -32,6 +33,8 @@ export class PartnerListScreen extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.notification = useService("pos_notification");
|
||||
useListener("click-save", () => this.env.bus.trigger("save-partner"));
|
||||
useListener("save-changes", this.saveChanges);
|
||||
this.searchWordInputRef = useRef("search-word-input-partner");
|
||||
@@ -59,12 +62,12 @@ export class PartnerListScreen extends PosComponent {
|
||||
this.render(true);
|
||||
} else {
|
||||
this.props.resolve({ confirmed: false, payload: false });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
}
|
||||
}
|
||||
confirm() {
|
||||
this.props.resolve({ confirmed: true, payload: this.state.selectedPartner });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
}
|
||||
activateEditMode() {
|
||||
this.state.detailIsShown = true;
|
||||
@@ -112,7 +115,7 @@ export class PartnerListScreen extends PosComponent {
|
||||
return;
|
||||
}
|
||||
const result = await this.searchPartner();
|
||||
this.showNotification(
|
||||
this.notification.add(
|
||||
_.str.sprintf(
|
||||
this.env._t('%s customer(s) found for "%s".'),
|
||||
result.length,
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
import { parse } from "web.field_utils";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useErrorHandlers } from "@point_of_sale/js/custom_hooks";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import utils from "web.utils";
|
||||
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
@@ -16,6 +15,7 @@ import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { PaymentScreenNumpad } from "./PaymentScreenNumpad";
|
||||
import { PaymentScreenPaymentLines } from "./PaymentScreenPaymentLines";
|
||||
import { PaymentScreenStatus } from "./PaymentScreenStatus";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class PaymentScreen extends PosComponent {
|
||||
static template = "PaymentScreen";
|
||||
@@ -27,6 +27,9 @@ export class PaymentScreen extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.notification = useService("pos_notification");
|
||||
useListener("delete-payment-line", this.deletePaymentLine);
|
||||
useListener("select-payment-line", this.selectPaymentLine);
|
||||
useListener("new-payment-line", this.addNewPaymentLine);
|
||||
@@ -39,14 +42,15 @@ export class PaymentScreen extends PosComponent {
|
||||
this.payment_methods_from_config = this.env.pos.payment_methods.filter((method) =>
|
||||
this.env.pos.config.payment_method_ids.includes(method.id)
|
||||
);
|
||||
numberBuffer.use(this._getNumberBufferConfig);
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.use(this._getNumberBufferConfig);
|
||||
useErrorHandlers();
|
||||
this.payment_interface = null;
|
||||
this.error = false;
|
||||
}
|
||||
|
||||
showMaxValueError() {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Maximum value reached"),
|
||||
body: this.env._t(
|
||||
"The amount cannot be higher than the due amount if you don't have a cash payment method configured."
|
||||
@@ -87,9 +91,12 @@ export class PaymentScreen extends PosComponent {
|
||||
// IMPROVEMENT: This code snippet is repeated multiple times.
|
||||
// Maybe it's better to create a function for it.
|
||||
const currentPartner = this.currentOrder.get_partner();
|
||||
const { confirmed, payload: newPartner } = await this.showTempScreen("PartnerListScreen", {
|
||||
partner: currentPartner,
|
||||
});
|
||||
const { confirmed, payload: newPartner } = await this.pos.showTempScreen(
|
||||
"PartnerListScreen",
|
||||
{
|
||||
partner: currentPartner,
|
||||
}
|
||||
);
|
||||
if (confirmed) {
|
||||
this.currentOrder.set_partner(newPartner);
|
||||
this.currentOrder.updatePricelist(newPartner);
|
||||
@@ -99,10 +106,10 @@ export class PaymentScreen extends PosComponent {
|
||||
// original function: click_paymentmethods
|
||||
const result = this.currentOrder.add_paymentline(paymentMethod);
|
||||
if (result) {
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
return true;
|
||||
} else {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Error"),
|
||||
body: this.env._t("There is already an electronic payment in progress."),
|
||||
});
|
||||
@@ -124,10 +131,10 @@ export class PaymentScreen extends PosComponent {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (numberBuffer.get() === null) {
|
||||
if (this.numberBuffer.get() === null) {
|
||||
this.deletePaymentLine({ detail: { cid: this.selectedPaymentLine.cid } });
|
||||
} else {
|
||||
this.selectedPaymentLine.set_amount(numberBuffer.getFloat());
|
||||
this.selectedPaymentLine.set_amount(this.numberBuffer.getFloat());
|
||||
}
|
||||
}
|
||||
toggleIsToInvoice() {
|
||||
@@ -144,7 +151,7 @@ export class PaymentScreen extends PosComponent {
|
||||
const change = this.currentOrder.get_change();
|
||||
const value = tip === 0 && change > 0 ? change : tip;
|
||||
|
||||
const { confirmed, payload } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(NumberPopup, {
|
||||
title: tip ? this.env._t("Change Tip") : this.env._t("Add Tip"),
|
||||
startingValue: value,
|
||||
isInputSelected: true,
|
||||
@@ -156,7 +163,7 @@ export class PaymentScreen extends PosComponent {
|
||||
}
|
||||
async toggleShippingDatePicker() {
|
||||
if (!this.currentOrder.getShippingDate()) {
|
||||
const { confirmed, payload: shippingDate } = await this.showPopup(DatePickerPopup, {
|
||||
const { confirmed, payload: shippingDate } = await this.popup.add(DatePickerPopup, {
|
||||
title: this.env._t("Select the shipping date"),
|
||||
});
|
||||
if (confirmed) {
|
||||
@@ -180,12 +187,12 @@ export class PaymentScreen extends PosComponent {
|
||||
.send_payment_cancel(this.currentOrder, cid)
|
||||
.then(function () {
|
||||
self.currentOrder.remove_paymentline(line);
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
self.render(true);
|
||||
});
|
||||
} else if (line.get_payment_status() !== "waitingCancel") {
|
||||
this.currentOrder.remove_paymentline(line);
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
@@ -193,13 +200,13 @@ export class PaymentScreen extends PosComponent {
|
||||
const { cid } = event.detail;
|
||||
const line = this.paymentLines.find((line) => line.cid === cid);
|
||||
this.currentOrder.select_paymentline(line);
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
this.render(true);
|
||||
}
|
||||
async validateOrder(isForceValidate) {
|
||||
if (this.env.pos.config.cash_rounding) {
|
||||
if (!this.env.pos.get_order().check_paymentlines_rounding()) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Rounding error in payment lines"),
|
||||
body: this.env._t(
|
||||
"The amount of your payment lines must be rounded to validate the transaction."
|
||||
@@ -259,7 +266,7 @@ export class PaymentScreen extends PosComponent {
|
||||
syncOrderResult.map((res) => res.id)
|
||||
);
|
||||
if (!postPushResult) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Error: no internet connection."),
|
||||
body: this.env._t(
|
||||
"Some, if not all, post-processing after syncing order failed."
|
||||
@@ -284,14 +291,14 @@ export class PaymentScreen extends PosComponent {
|
||||
} finally {
|
||||
// Always show the next screen regardless of error since pos has to
|
||||
// continue working even offline.
|
||||
this.showScreen(this.nextScreen);
|
||||
this.pos.showScreen(this.nextScreen);
|
||||
// Remove the order from the local storage so that when we refresh the page, the order
|
||||
// won't be there
|
||||
this.env.pos.db.remove_unpaid_order(this.currentOrder);
|
||||
|
||||
// Ask the user to sync the remaining unsynced orders.
|
||||
if (!hasError && syncOrderResult && this.env.pos.db.get_orders().length) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Remaining unsynced orders"),
|
||||
body: this.env._t(
|
||||
"There are unsynced orders. Do you want to sync these orders?"
|
||||
@@ -322,7 +329,7 @@ export class PaymentScreen extends PosComponent {
|
||||
}
|
||||
async _isOrderValid(isForceValidate) {
|
||||
if (this.currentOrder.get_orderlines().length === 0 && this.currentOrder.is_to_invoice()) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Empty Order"),
|
||||
body: this.env._t(
|
||||
"There must be at least one product in your order before it can be validated and invoiced."
|
||||
@@ -336,7 +343,7 @@ export class PaymentScreen extends PosComponent {
|
||||
);
|
||||
if (splitPayments.length && !this.currentOrder.get_partner()) {
|
||||
const paymentMethod = splitPayments[0].payment_method;
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Customer Required"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t("Customer is required for %s payment method."),
|
||||
@@ -353,7 +360,7 @@ export class PaymentScreen extends PosComponent {
|
||||
(this.currentOrder.is_to_invoice() || this.currentOrder.getShippingDate()) &&
|
||||
!this.currentOrder.get_partner()
|
||||
) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Please select the Customer"),
|
||||
body: this.env._t(
|
||||
"You need to select the customer before you can invoice or ship an order."
|
||||
@@ -370,7 +377,7 @@ export class PaymentScreen extends PosComponent {
|
||||
this.currentOrder.getShippingDate() &&
|
||||
!(partner.name && partner.street && partner.city && partner.country_id)
|
||||
) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Incorrect address for shipping"),
|
||||
body: this.env._t("The selected customer needs an address."),
|
||||
});
|
||||
@@ -381,7 +388,7 @@ export class PaymentScreen extends PosComponent {
|
||||
this.currentOrder.get_total_with_tax() != 0 &&
|
||||
this.currentOrder.get_paymentlines().length === 0
|
||||
) {
|
||||
this.showNotification(this.env._t("Select a payment method to validate the order."));
|
||||
this.notification.add(this.env._t("Select a payment method to validate the order."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -391,7 +398,7 @@ export class PaymentScreen extends PosComponent {
|
||||
|
||||
if (this.currentOrder.has_not_valid_rounding()) {
|
||||
var line = this.currentOrder.has_not_valid_rounding();
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Incorrect rounding"),
|
||||
body: this.env._t(
|
||||
"You have to round your payments lines." + line.amount + " is not rounded."
|
||||
@@ -413,7 +420,7 @@ export class PaymentScreen extends PosComponent {
|
||||
cash = cash || this.env.pos.payment_methods[i].is_cash_count;
|
||||
}
|
||||
if (!cash) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Cannot return change without a cash payment method"),
|
||||
body: this.env._t(
|
||||
"There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration"
|
||||
@@ -429,23 +436,25 @@ export class PaymentScreen extends PosComponent {
|
||||
this.currentOrder.get_total_with_tax() > 0 &&
|
||||
this.currentOrder.get_total_with_tax() * 1000 < this.currentOrder.get_total_paid()
|
||||
) {
|
||||
this.showPopup(ConfirmPopup, {
|
||||
title: this.env._t("Please Confirm Large Amount"),
|
||||
body:
|
||||
this.env._t("Are you sure that the customer wants to pay") +
|
||||
" " +
|
||||
this.env.pos.format_currency(this.currentOrder.get_total_paid()) +
|
||||
" " +
|
||||
this.env._t("for an order of") +
|
||||
" " +
|
||||
this.env.pos.format_currency(this.currentOrder.get_total_with_tax()) +
|
||||
" " +
|
||||
this.env._t('? Clicking "Confirm" will validate the payment.'),
|
||||
}).then(({ confirmed }) => {
|
||||
if (confirmed) {
|
||||
this.validateOrder(true);
|
||||
}
|
||||
});
|
||||
this.popup
|
||||
.add(ConfirmPopup, {
|
||||
title: this.env._t("Please Confirm Large Amount"),
|
||||
body:
|
||||
this.env._t("Are you sure that the customer wants to pay") +
|
||||
" " +
|
||||
this.env.pos.format_currency(this.currentOrder.get_total_paid()) +
|
||||
" " +
|
||||
this.env._t("for an order of") +
|
||||
" " +
|
||||
this.env.pos.format_currency(this.currentOrder.get_total_with_tax()) +
|
||||
" " +
|
||||
this.env._t('? Clicking "Confirm" will validate the payment.'),
|
||||
})
|
||||
.then(({ confirmed }) => {
|
||||
if (confirmed) {
|
||||
this.validateOrder(true);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { TextAreaPopup } from "@point_of_sale/js/Popups/TextAreaPopup";
|
||||
|
||||
export class OrderlineCustomerNoteButton extends PosComponent {
|
||||
@@ -10,6 +10,7 @@ export class OrderlineCustomerNoteButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
async onClick() {
|
||||
@@ -17,8 +18,7 @@ export class OrderlineCustomerNoteButton extends PosComponent {
|
||||
if (!selectedOrderline) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { confirmed, payload: inputNote } = await this.showPopup(TextAreaPopup, {
|
||||
const { confirmed, payload: inputNote } = await this.popup.add(TextAreaPopup, {
|
||||
startingValue: selectedOrderline.get_customer_note(),
|
||||
title: this.env._t("Add Customer Note"),
|
||||
});
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { ProductInfoPopup } from "@point_of_sale/js/Popups/ProductInfoPopup";
|
||||
|
||||
export class ProductInfoButton extends PosComponent {
|
||||
@@ -11,6 +11,7 @@ export class ProductInfoButton extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
useListener("click", this.onClick);
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
async onClick() {
|
||||
const orderline = this.env.pos.get_order().get_selected_orderline();
|
||||
@@ -18,7 +19,7 @@ export class ProductInfoButton extends PosComponent {
|
||||
const product = orderline.get_product();
|
||||
const quantity = orderline.get_quantity();
|
||||
const info = await this.env.pos.getProductInfo(product, quantity);
|
||||
this.showPopup(ProductInfoPopup, { info: info, product: product });
|
||||
this.popup.add(ProductInfoPopup, { info: info, product: product });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
@@ -9,12 +10,13 @@ export class RefundButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click", this._onClick);
|
||||
}
|
||||
_onClick() {
|
||||
const partner = this.env.pos.get_order().get_partner();
|
||||
const searchDetails = partner ? { fieldName: "PARTNER", searchTerm: partner.name } : {};
|
||||
this.showScreen("TicketScreen", {
|
||||
this.pos.showScreen("TicketScreen", {
|
||||
ui: { filter: "SYNCED", searchDetails },
|
||||
destinationOrder: this.env.pos.get_order(),
|
||||
});
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
|
||||
export class SetFiscalPositionButton extends PosComponent {
|
||||
@@ -10,6 +10,7 @@ export class SetFiscalPositionButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
get currentOrder() {
|
||||
@@ -39,7 +40,7 @@ export class SetFiscalPositionButton extends PosComponent {
|
||||
item: fiscalPos,
|
||||
});
|
||||
}
|
||||
const { confirmed, payload: selectedFiscalPosition } = await this.showPopup(
|
||||
const { confirmed, payload: selectedFiscalPosition } = await this.popup.add(
|
||||
SelectionPopup,
|
||||
{
|
||||
title: this.env._t("Select Fiscal Position"),
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
|
||||
export class SetPricelistButton extends PosComponent {
|
||||
@@ -10,6 +10,7 @@ export class SetPricelistButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
get currentOrder() {
|
||||
@@ -40,7 +41,7 @@ export class SetPricelistButton extends PosComponent {
|
||||
});
|
||||
}
|
||||
|
||||
const { confirmed, payload: selectedPricelist } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload: selectedPricelist } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Select the pricelist"),
|
||||
list: selectionList,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { EditListPopup } from "@point_of_sale/js/Popups/EditListPopup";
|
||||
|
||||
@@ -15,13 +15,16 @@ export class OrderWidget extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("select-line", this._selectLine);
|
||||
useListener("edit-pack-lot-lines", this._editPackLotLines);
|
||||
this.scrollableRef = useRef("scrollable");
|
||||
useEffect(
|
||||
() => {
|
||||
const selectedLineEl = this.scrollableRef.el && this.scrollableRef.el.querySelector(".orderline.selected");
|
||||
if(selectedLineEl) {
|
||||
const selectedLineEl =
|
||||
this.scrollableRef.el &&
|
||||
this.scrollableRef.el.querySelector(".orderline.selected");
|
||||
if (selectedLineEl) {
|
||||
selectedLineEl.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
},
|
||||
@@ -48,7 +51,7 @@ export class OrderWidget extends PosComponent {
|
||||
const orderline = event.detail.orderline;
|
||||
const isAllowOnlyOneLot = orderline.product.isAllowOnlyOneLot();
|
||||
const packLotLinesToEdit = orderline.getPackLotLinesToEdit(isAllowOnlyOneLot);
|
||||
const { confirmed, payload } = await this.showPopup(EditListPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(EditListPopup, {
|
||||
title: this.env._t("Lot/Serial Number(s) Required"),
|
||||
name: orderline.product.display_name,
|
||||
isSingleItem: isAllowOnlyOneLot,
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ControlButtonsMixin } from "@point_of_sale/js/ControlButtonsMixin";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { useBarcodeReader } from "@point_of_sale/js/custom_hooks";
|
||||
import { parse } from "web.field_utils";
|
||||
|
||||
@@ -21,6 +20,7 @@ import { MobileOrderWidget } from "../../Misc/MobileOrderWidget";
|
||||
import { NumpadWidget } from "./NumpadWidget";
|
||||
import { OrderWidget } from "./OrderWidget";
|
||||
import { ProductsWidget } from "./ProductsWidget";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const { onMounted, useState } = owl;
|
||||
|
||||
@@ -36,6 +36,8 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
useListener("update-selected-orderline", this._updateSelectedOrderline);
|
||||
useListener("select-line", this._selectLine);
|
||||
useListener("set-numpad-mode", this._setNumpadMode);
|
||||
@@ -50,7 +52,8 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
discount: this._barcodeDiscountAction,
|
||||
error: this._barcodeErrorAction,
|
||||
});
|
||||
numberBuffer.use({
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.use({
|
||||
nonKeyboardInputEvent: "numpad-click-input",
|
||||
triggerAtInput: "update-selected-orderline",
|
||||
useWithBarcode: true,
|
||||
@@ -59,7 +62,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
// Call `reset` when the `onMounted` callback in `numberBuffer.use` is done.
|
||||
// We don't do this in the `mounted` lifecycle method because it is called before
|
||||
// the callbacks in `onMounted` hook.
|
||||
onMounted(() => numberBuffer.reset());
|
||||
onMounted(() => this.numberBuffer.reset());
|
||||
this.state = useState({
|
||||
mobile_pane: this.props.mobile_pane || "right",
|
||||
});
|
||||
@@ -90,7 +93,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
product.attribute_line_ids,
|
||||
(id) => this.env.pos.attributes_by_ptal_id[id]
|
||||
).filter((attr) => attr !== undefined);
|
||||
const { confirmed, payload } = await this.showPopup(ProductConfiguratorPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(ProductConfiguratorPopup, {
|
||||
product: product,
|
||||
attributes: attributes,
|
||||
});
|
||||
@@ -123,7 +126,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
packLotLinesToEdit = [];
|
||||
}
|
||||
}
|
||||
const { confirmed, payload } = await this.showPopup(EditListPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(EditListPopup, {
|
||||
title: this.env._t("Lot/Serial Number(s) Required"),
|
||||
name: product.display_name,
|
||||
isSingleItem: isAllowOnlyOneLot,
|
||||
@@ -149,7 +152,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
if (product.to_weight && this.env.pos.config.iface_electronic_scale) {
|
||||
// Show the ScaleScreen to weigh the product.
|
||||
if (this.isScaleAvailable) {
|
||||
const { confirmed, payload } = await this.showTempScreen("ScaleScreen", {
|
||||
const { confirmed, payload } = await this.pos.showTempScreen("ScaleScreen", {
|
||||
product,
|
||||
});
|
||||
if (confirmed) {
|
||||
@@ -184,16 +187,16 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
}
|
||||
// Add the product after having the extra information.
|
||||
await this._addProduct(product, options);
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
_setNumpadMode(event) {
|
||||
const { mode } = event.detail;
|
||||
numberBuffer.capture();
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.capture();
|
||||
this.numberBuffer.reset();
|
||||
this.env.pos.numpadMode = mode;
|
||||
}
|
||||
_selectLine() {
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
async _updateSelectedOrderline(event) {
|
||||
if (this.env.pos.numpadMode === "quantity" && this.env.pos.disallowLineQuantityChange()) {
|
||||
@@ -210,7 +213,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
.get_quantity();
|
||||
|
||||
if (selectedLine.noDecrease) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Invalid action"),
|
||||
body: this.env._t("You are not allowed to change this quantity"),
|
||||
});
|
||||
@@ -229,7 +232,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
const val = buffer === null ? "remove" : buffer;
|
||||
this._setValue(val);
|
||||
if (val == "remove") {
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
this.env.pos.numpadMode = "quantity";
|
||||
}
|
||||
}
|
||||
@@ -239,7 +242,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
if (this.env.pos.numpadMode === "quantity") {
|
||||
const result = this.currentOrder.get_selected_orderline().set_quantity(val);
|
||||
if (!result) {
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
} else if (this.env.pos.numpadMode === "discount") {
|
||||
this.currentOrder.get_selected_orderline().set_discount(val);
|
||||
@@ -319,7 +322,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
// Why? Because once we start declaring barcode actions in different
|
||||
// screens, these methods will also be declared over and over.
|
||||
_barcodeErrorAction(code) {
|
||||
this.showPopup(ErrorBarcodePopup, { code: this._codeRepr(code) });
|
||||
this.popup.add(ErrorBarcodePopup, { code: this._codeRepr(code) });
|
||||
}
|
||||
_codeRepr(code) {
|
||||
if (code.code.length > 32) {
|
||||
@@ -329,7 +332,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
}
|
||||
}
|
||||
async _displayAllControlPopup() {
|
||||
await this.showPopup(ControlButtonPopup, {
|
||||
await this.popup.add(ControlButtonPopup, {
|
||||
controlButtons: this.controlButtons,
|
||||
});
|
||||
}
|
||||
@@ -339,7 +342,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
*/
|
||||
async _onScaleNotAvailable() {}
|
||||
async _showDecreaseQuantityPopup() {
|
||||
const { confirmed, payload: inputNumber } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload: inputNumber } = await this.popup.add(NumberPopup, {
|
||||
startingValue: 0,
|
||||
title: this.env._t("Set the new quantity"),
|
||||
});
|
||||
@@ -370,7 +373,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
// IMPROVEMENT: This code snippet is very similar to selectPartner of PaymentScreen.
|
||||
const currentPartner = this.currentOrder.get_partner();
|
||||
if (currentPartner && this.currentOrder.getHasRefundLines()) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Can't change customer"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t(
|
||||
@@ -381,9 +384,12 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { confirmed, payload: newPartner } = await this.showTempScreen("PartnerListScreen", {
|
||||
partner: currentPartner,
|
||||
});
|
||||
const { confirmed, payload: newPartner } = await this.pos.showTempScreen(
|
||||
"PartnerListScreen",
|
||||
{
|
||||
partner: currentPartner,
|
||||
}
|
||||
);
|
||||
if (confirmed) {
|
||||
this.currentOrder.set_partner(newPartner);
|
||||
this.currentOrder.updatePricelist(newPartner);
|
||||
@@ -400,7 +406,7 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
(this.env.pos.picking_type.use_create_lots ||
|
||||
this.env.pos.picking_type.use_existing_lots)
|
||||
) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Some Serial/Lot Numbers are missing"),
|
||||
body: this.env._t(
|
||||
"You are trying to sell products with serial/lot numbers, but some of them are not set.\nWould you like to proceed anyway?"
|
||||
@@ -409,10 +415,10 @@ export class ProductScreen extends ControlButtonsMixin(PosComponent) {
|
||||
cancelText: this.env._t("No"),
|
||||
});
|
||||
if (confirmed) {
|
||||
this.showScreen("PaymentScreen");
|
||||
this.pos.showScreen("PaymentScreen");
|
||||
}
|
||||
} else {
|
||||
this.showScreen("PaymentScreen");
|
||||
this.pos.showScreen("PaymentScreen");
|
||||
}
|
||||
}
|
||||
switchPane() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
import { ProductItem } from "./ProductItem";
|
||||
import { ProductsWidgetControlPanel } from "./ProductsWidgetControlPanel";
|
||||
@@ -64,10 +64,7 @@ export class ProductsWidget extends PosComponent {
|
||||
return this.env.pos.db.get_category_childs_ids(0).length === 0;
|
||||
}
|
||||
get shouldShowButton() {
|
||||
return (
|
||||
this.productsToDisplay.length === 0 &&
|
||||
this.searchWord
|
||||
);
|
||||
return this.productsToDisplay.length === 0 && this.searchWord;
|
||||
}
|
||||
_switchCategory(event) {
|
||||
this.env.pos.setSelectedCategoryId(event.detail);
|
||||
|
||||
+4
-2
@@ -2,11 +2,12 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
import { CategoryButton } from "./CategoryButton";
|
||||
|
||||
import { onMounted, onWillUnmount, useState } from "@odoo/owl";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
export class ProductsWidgetControlPanel extends PosComponent {
|
||||
static components = { CategoryButton };
|
||||
@@ -15,6 +16,7 @@ export class ProductsWidgetControlPanel extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.notification = useService("pos_notification");
|
||||
this.updateSearch = debounce(this.updateSearch, 100);
|
||||
this.state = useState({ searchInput: "", mobileSearchBarIsShown: false });
|
||||
|
||||
@@ -50,7 +52,7 @@ export class ProductsWidgetControlPanel extends PosComponent {
|
||||
return;
|
||||
}
|
||||
const result = await this.loadProductFromDB();
|
||||
this.showNotification(
|
||||
this.notification.add(
|
||||
_.str.sprintf(
|
||||
this.env._t('%s product(s) found for "%s".'),
|
||||
result.length,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { registry } from "@web/core/registry";
|
||||
import { OrderReceipt } from "./OrderReceipt";
|
||||
|
||||
import { onMounted, useRef, status } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
static template = "ReceiptScreen";
|
||||
@@ -16,6 +17,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useErrorHandlers();
|
||||
this.orderReceipt = useRef("order-receipt");
|
||||
const order = this.currentOrder;
|
||||
@@ -57,9 +59,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
this.orderUiState.emailNotice = this.env._t("Email sent.");
|
||||
} catch {
|
||||
this.orderUiState.emailSuccessful = false;
|
||||
this.orderUiState.emailNotice = this.env._t(
|
||||
"Sending email failed. Please try again."
|
||||
);
|
||||
this.orderUiState.emailNotice = this.env._t("Sending email failed. Please try again.");
|
||||
}
|
||||
}
|
||||
get orderAmountPlusTip() {
|
||||
@@ -104,7 +104,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
this._addNewOrder();
|
||||
const { name, props } = this.nextScreen;
|
||||
this.showScreen(name, props);
|
||||
this.pos.showScreen(name, props);
|
||||
if (this.env.pos.config.iface_customer_facing_display) {
|
||||
this.env.pos.send_current_order_to_customer_facing_display();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { round_precision as round_pr } from "web.utils";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const { onMounted, onWillUnmount, useExternalListener, useState } = owl;
|
||||
|
||||
@@ -19,6 +20,7 @@ export class ScaleScreen extends PosComponent {
|
||||
this.state = useState({ weight: 0 });
|
||||
onMounted(this.onMounted);
|
||||
onWillUnmount(this.onWillUnmount);
|
||||
this.pos = usePos();
|
||||
}
|
||||
onMounted() {
|
||||
// start the scale reading
|
||||
@@ -30,14 +32,14 @@ export class ScaleScreen extends PosComponent {
|
||||
}
|
||||
back() {
|
||||
this.props.resolve({ confirmed: false, payload: null });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
}
|
||||
confirm() {
|
||||
this.props.resolve({
|
||||
confirmed: true,
|
||||
payload: { weight: this.state.weight },
|
||||
});
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
}
|
||||
_onHotkeys(event) {
|
||||
if (event.key === "Escape") {
|
||||
|
||||
+7
-4
@@ -1,15 +1,18 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class InvoiceButton extends PosComponent {
|
||||
static template = "InvoiceButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this._onClick);
|
||||
}
|
||||
get isAlreadyInvoiced() {
|
||||
@@ -45,7 +48,7 @@ export class InvoiceButton extends PosComponent {
|
||||
throw error;
|
||||
} else {
|
||||
// NOTE: error here is most probably undefined
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Network Error"),
|
||||
body: this.env._t("Unable to download invoice."),
|
||||
});
|
||||
@@ -69,7 +72,7 @@ export class InvoiceButton extends PosComponent {
|
||||
// Part 1: Handle missing partner.
|
||||
// Write to pos.order the selected partner.
|
||||
if (!order.get_partner()) {
|
||||
const { confirmed: confirmedPopup } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed: confirmedPopup } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Need customer to invoice"),
|
||||
body: this.env._t("Do you want to open the customer list to select customer?"),
|
||||
});
|
||||
@@ -78,7 +81,7 @@ export class InvoiceButton extends PosComponent {
|
||||
}
|
||||
|
||||
const { confirmed: confirmedTempScreen, payload: newPartner } =
|
||||
await this.showTempScreen("PartnerListScreen");
|
||||
await this.pos.showTempScreen("PartnerListScreen");
|
||||
if (!confirmedTempScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,18 +2,20 @@
|
||||
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class ReprintReceiptButton extends PosComponent {
|
||||
static template = "ReprintReceiptButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click", this._onClick);
|
||||
}
|
||||
async _onClick() {
|
||||
if (!this.props.order) {
|
||||
return;
|
||||
}
|
||||
this.showScreen("ReprintReceiptScreen", { order: this.props.order });
|
||||
this.pos.showScreen("ReprintReceiptScreen", { order: this.props.order });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { AbstractReceiptScreen } from "@point_of_sale/js/Misc/AbstractReceiptScreen";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { OrderReceipt } from "../ReceiptScreen/OrderReceipt";
|
||||
@@ -7,21 +8,23 @@ import { OrderReceipt } from "../ReceiptScreen/OrderReceipt";
|
||||
export class ReprintReceiptScreen extends AbstractReceiptScreen {
|
||||
static template = "ReprintReceiptScreen";
|
||||
static components = { OrderReceipt };
|
||||
static storeOnOrder = false;
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
owl.onMounted(this.onMounted);
|
||||
}
|
||||
onMounted() {
|
||||
this.printReceipt();
|
||||
}
|
||||
confirm() {
|
||||
this.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
this.pos.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
}
|
||||
async printReceipt() {
|
||||
if (this.env.proxy.printer && this.env.pos.config.iface_print_skip_screen) {
|
||||
const result = await this._printReceipt();
|
||||
if (result) {
|
||||
this.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
this.pos.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
import { Order } from "@point_of_sale/js/models";
|
||||
import { IndependentToOrderScreen } from "@point_of_sale/js/Misc/IndependentToOrderScreen";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { parse } from "web.field_utils";
|
||||
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
@@ -16,6 +15,7 @@ import { NumpadWidget } from "../ProductScreen/NumpadWidget";
|
||||
import { OrderDetails } from "../TicketScreen/OrderDetails";
|
||||
import { ReprintReceiptButton } from "./ControlButtons/ReprintReceiptButton";
|
||||
import { SearchBar } from "../../Misc/SearchBar";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const { onMounted, onWillUnmount, useState } = owl;
|
||||
|
||||
@@ -41,6 +41,8 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
useListener("close-screen", this._onCloseScreen);
|
||||
useListener("filter-selected", this._onFilterSelected);
|
||||
useListener("search", this._onSearch);
|
||||
@@ -54,7 +56,8 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
useListener("click-refund-order-uid", this._onClickRefundOrderUid);
|
||||
useListener("update-selected-orderline", this._onUpdateSelectedOrderline);
|
||||
useListener("do-refund", this._onDoRefund);
|
||||
numberBuffer.use({
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.use({
|
||||
nonKeyboardInputEvent: "numpad-click-input",
|
||||
triggerAtInput: "update-selected-orderline",
|
||||
});
|
||||
@@ -118,14 +121,14 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
this._state.ui.selectedOrderlineIds[clickedOrder.backendId] = firstLine.id;
|
||||
}
|
||||
}
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
} else {
|
||||
this._setOrder(clickedOrder);
|
||||
}
|
||||
}
|
||||
_onCreateNewOrder() {
|
||||
this.env.pos.add_new_order();
|
||||
this.showScreen("ProductScreen");
|
||||
this.pos.showScreen("ProductScreen");
|
||||
}
|
||||
_selectNextOrder(currentOrder) {
|
||||
const currentOrderIndex = this._getOrderList().indexOf(currentOrder);
|
||||
@@ -140,7 +143,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
["ProductScreen", "PaymentScreen"].includes(screen.name) &&
|
||||
order.get_orderlines().length > 0
|
||||
) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Existing orderlines"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t(
|
||||
@@ -180,7 +183,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
_onClickOrderline({ detail: orderline }) {
|
||||
const order = this.getSelectedSyncedOrder();
|
||||
this._state.ui.selectedOrderlineIds[order.backendId] = orderline.id;
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
_onClickRefundOrderUid({ detail: orderUid }) {
|
||||
// Open the refund order.
|
||||
@@ -193,24 +196,24 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
const buffer = detail.buffer;
|
||||
const order = this.getSelectedSyncedOrder();
|
||||
if (!order) {
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
|
||||
const selectedOrderlineId = this.getSelectedOrderlineId();
|
||||
const orderline = order.orderlines.find((line) => line.id == selectedOrderlineId);
|
||||
if (!orderline) {
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
|
||||
const toRefundDetail = this._getToRefundDetail(orderline);
|
||||
// When already linked to an order, do not modify the to refund quantity.
|
||||
if (toRefundDetail.destinationOrderUid) {
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
|
||||
const refundableQty = toRefundDetail.orderline.qty - toRefundDetail.orderline.refundedQty;
|
||||
if (refundableQty <= 0) {
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
|
||||
if (buffer == null || buffer == "") {
|
||||
@@ -218,8 +221,8 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
} else {
|
||||
const quantity = Math.abs(parse.float(buffer));
|
||||
if (quantity > refundableQty) {
|
||||
numberBuffer.reset();
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.numberBuffer.reset();
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Maximum Exceeded"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { OfflineErrorPopup } from "./Popups/OfflineErrorPopup";
|
||||
import { ConfirmPopup } from "./Popups/ConfirmPopup";
|
||||
import { ErrorTracebackPopup } from "./Popups/ErrorTracebackPopup";
|
||||
import { ErrorPopup } from "./Popups/ErrorPopup";
|
||||
import { useEnv } from "@odoo/owl";
|
||||
|
||||
const { onMounted, onPatched, onWillUnmount, useComponent } = owl;
|
||||
|
||||
@@ -11,14 +12,16 @@ const { onMounted, onPatched, onWillUnmount, useComponent } = owl;
|
||||
*
|
||||
* IMPROVEMENT: This is a terrible hook. There could be a better way to handle
|
||||
* the error when the order failed to sync.
|
||||
* FIXME POSREF: move this to the error_handler registry.
|
||||
*/
|
||||
export function useErrorHandlers() {
|
||||
const component = useComponent();
|
||||
const popup = useEnv().services.popup;
|
||||
|
||||
component._handlePushOrderError = async function (error) {
|
||||
// This error handler receives `error` equivalent to `error.message` of the rpc error.
|
||||
if (error.message === "Backend Invoice") {
|
||||
await this.showPopup(ConfirmPopup, {
|
||||
await popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Please print the invoice from the backend"),
|
||||
body:
|
||||
this.env._t(
|
||||
@@ -31,10 +34,10 @@ export function useErrorHandlers() {
|
||||
const body = this.env._t(
|
||||
"Check the internet connection then try to sync again by clicking on the red wifi button (upper right of the screen)."
|
||||
);
|
||||
await this.showPopup(OfflineErrorPopup, { title, body });
|
||||
await popup.add(OfflineErrorPopup, { title, body });
|
||||
} else if (error.code === 200) {
|
||||
// OpenERP Server Errors
|
||||
await this.showPopup(ErrorTracebackPopup, {
|
||||
await popup.add(ErrorTracebackPopup, {
|
||||
title: error.data.message || this.env._t("Server Error"),
|
||||
body:
|
||||
error.data.debug ||
|
||||
@@ -42,7 +45,7 @@ export function useErrorHandlers() {
|
||||
});
|
||||
} else if (error.code === 700) {
|
||||
// Sweden Fiscal module errors
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await popup.add(ErrorPopup, {
|
||||
title: this.env._t("Fiscal data module error"),
|
||||
body:
|
||||
error.data.error.status ||
|
||||
@@ -58,13 +61,13 @@ export function useErrorHandlers() {
|
||||
} else {
|
||||
bodyMessage = "Fiscal data module is not on.";
|
||||
}
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await popup.add(ErrorPopup, {
|
||||
title: this.env._t("Fiscal data module error"),
|
||||
body: bodyMessage,
|
||||
});
|
||||
} else {
|
||||
// ???
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await popup.add(ErrorPopup, {
|
||||
title: this.env._t("Unknown Error"),
|
||||
body: this.env._t(
|
||||
"The order could not be sent to the server due to an unknown error"
|
||||
|
||||
@@ -7,7 +7,6 @@ import core from "web.core";
|
||||
import field_utils from "web.field_utils";
|
||||
import time from "web.time";
|
||||
import utils from "web.utils";
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { batched, uuidv4 } from "@point_of_sale/js/utils";
|
||||
import { ErrorPopup } from "./Popups/ErrorPopup";
|
||||
|
||||
@@ -145,6 +144,10 @@ export class PosGlobalState extends PosModel {
|
||||
selectedPartner: null,
|
||||
selectedCategoryId: null,
|
||||
});
|
||||
|
||||
this.ready = new Promise((resolve) => {
|
||||
this.markReady = resolve;
|
||||
});
|
||||
}
|
||||
getDefaultSearchDetails() {
|
||||
return {
|
||||
@@ -167,6 +170,7 @@ export class PosGlobalState extends PosModel {
|
||||
await this.load_product_uom_unit();
|
||||
await this.load_orders();
|
||||
this.set_start_order();
|
||||
this.markReady();
|
||||
}
|
||||
|
||||
async load_server_data() {
|
||||
@@ -340,40 +344,37 @@ export class PosGlobalState extends PosModel {
|
||||
// reload the list of partner, returns as a promise that resolves if there were
|
||||
// updated partners, and fails if not
|
||||
async load_new_partners() {
|
||||
let search_params = { domain: this.prepare_new_partners_domain() };
|
||||
const search_params = { domain: this.prepare_new_partners_domain() };
|
||||
if (this.env.pos.config.limited_partners_loading) {
|
||||
search_params['order'] = 'write_date desc';
|
||||
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;
|
||||
}
|
||||
else {
|
||||
search_params['limit'] = 1;
|
||||
search_params["limit"] = this.env.pos.config.limited_partners_amount || 1;
|
||||
} else {
|
||||
search_params["limit"] = 1;
|
||||
}
|
||||
}
|
||||
const partners = await this.env.services
|
||||
.rpc(
|
||||
{
|
||||
model: 'pos.session',
|
||||
method: 'get_pos_ui_res_partner_by_params',
|
||||
args: [[odoo.pos_session_id], search_params],
|
||||
},
|
||||
{
|
||||
timeout: 3000,
|
||||
shadow: true,
|
||||
}
|
||||
)
|
||||
const partners = await this.env.services.rpc(
|
||||
{
|
||||
model: "pos.session",
|
||||
method: "get_pos_ui_res_partner_by_params",
|
||||
args: [[odoo.pos_session_id], search_params],
|
||||
},
|
||||
{
|
||||
timeout: 3000,
|
||||
shadow: true,
|
||||
}
|
||||
);
|
||||
if (this.env.pos.config.partner_load_background) {
|
||||
this.loadPartnersBackground(
|
||||
search_params['domain'],
|
||||
search_params["domain"],
|
||||
this.env.pos.config.limited_partners_amount || 1,
|
||||
'write_date desc'
|
||||
"write_date desc"
|
||||
);
|
||||
}
|
||||
if (this.addPartners(partners)) {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +548,7 @@ export class PosGlobalState extends PosModel {
|
||||
page += 1;
|
||||
} while (products.length == this.config.limited_products_amount);
|
||||
}
|
||||
async loadPartnersBackground(domain=[], offset=0, order=false) {
|
||||
async loadPartnersBackground(domain = [], offset = 0, order = false) {
|
||||
// Start at the first page since the first set of loaded partners are not actually in the
|
||||
// same order as this background loading procedure.
|
||||
let i = 0;
|
||||
@@ -1482,7 +1483,7 @@ export class Product extends PosModel {
|
||||
// product.pricelist.item records are loaded with a search_read
|
||||
// and were automatically sorted based on their _order by the
|
||||
// ORM. After that they are added in this order to the pricelists.
|
||||
get_price(pricelist, quantity, price_extra, recurring=false) {
|
||||
get_price(pricelist, quantity, price_extra, recurring = false) {
|
||||
var self = this;
|
||||
var date = moment();
|
||||
|
||||
@@ -1512,12 +1513,13 @@ export class Product extends PosModel {
|
||||
pricelist_items = _.filter(
|
||||
self.applicablePricelistItems[pricelist.id],
|
||||
function (item) {
|
||||
return (
|
||||
(!item.categ_id || _.contains(category_ids, item.categ_id[0])) &&
|
||||
(!item.date_start || moment.utc(item.date_start).isSameOrBefore(date)) &&
|
||||
(!item.date_end || moment.utc(item.date_end).isSameOrAfter(date))
|
||||
);
|
||||
});
|
||||
return (
|
||||
(!item.categ_id || _.contains(category_ids, item.categ_id[0])) &&
|
||||
(!item.date_start || moment.utc(item.date_start).isSameOrBefore(date)) &&
|
||||
(!item.date_end || moment.utc(item.date_end).isSameOrAfter(date))
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var price = self.lst_price;
|
||||
@@ -1650,7 +1652,7 @@ export class Orderline extends PosModel {
|
||||
var packlotline = pack_lot_lines[i][2];
|
||||
var pack_lot_line = new Packlotline(
|
||||
{},
|
||||
{ json: _.extend({...packlotline}, { order_line: this }) }
|
||||
{ json: _.extend({ ...packlotline }, { order_line: this }) }
|
||||
);
|
||||
this.pack_lot_lines.add(pack_lot_line);
|
||||
}
|
||||
@@ -1790,7 +1792,7 @@ export class Orderline extends PosModel {
|
||||
const maxQtyToRefund =
|
||||
toRefundDetail.orderline.qty - toRefundDetail.orderline.refundedQty;
|
||||
if (quant > 0) {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.pos.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Positive quantity not allowed"),
|
||||
body: _t(
|
||||
"Only a negative quantity is allowed for this refund line. Click on +/- to modify the quantity to be refunded."
|
||||
@@ -1802,7 +1804,7 @@ export class Orderline extends PosModel {
|
||||
} else if (-quant <= maxQtyToRefund) {
|
||||
toRefundDetail.qty = -quant;
|
||||
} else {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.pos.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Greater than allowed"),
|
||||
body: _.str.sprintf(
|
||||
_t(
|
||||
@@ -2243,7 +2245,7 @@ export class Orderline extends PosModel {
|
||||
};
|
||||
}
|
||||
display_discount_policy() {
|
||||
return (this.order.pricelist ? this.order.pricelist.discount_policy : "with_discount" );
|
||||
return this.order.pricelist ? this.order.pricelist.discount_policy : "with_discount";
|
||||
}
|
||||
compute_fixed_price(price) {
|
||||
var order = this.order;
|
||||
@@ -2605,8 +2607,8 @@ export class Order extends PosModel {
|
||||
}
|
||||
this.set_partner(partner);
|
||||
|
||||
this.temporary = false; // FIXME
|
||||
this.to_invoice = false; // FIXME
|
||||
this.temporary = false; // FIXME
|
||||
this.to_invoice = false; // FIXME
|
||||
this.shippingDate = json.shipping_date;
|
||||
|
||||
var orderlines = json.lines;
|
||||
@@ -2622,10 +2624,7 @@ export class Order extends PosModel {
|
||||
var paymentlines = json.statement_ids;
|
||||
for (i = 0; i < paymentlines.length; i++) {
|
||||
var paymentline = paymentlines[i][2];
|
||||
var newpaymentline = new Payment(
|
||||
{},
|
||||
{ pos: this.pos, order: this, json: paymentline }
|
||||
);
|
||||
var newpaymentline = new Payment({}, { pos: this.pos, order: this, json: paymentline });
|
||||
this.paymentlines.add(newpaymentline);
|
||||
|
||||
if (i === paymentlines.length - 1) {
|
||||
@@ -2686,7 +2685,10 @@ export class Order extends PosModel {
|
||||
_exportShippingDateForPrinting() {
|
||||
const shippingDate = new Date(this.shippingDate);
|
||||
const localeShippingDate = field_utils.format.date(
|
||||
moment(shippingDate), {}, {timezone: false});
|
||||
moment(shippingDate),
|
||||
{},
|
||||
{ timezone: false }
|
||||
);
|
||||
const exportedDate = {
|
||||
localestring: localeShippingDate,
|
||||
validationDate: shippingDate,
|
||||
@@ -3399,7 +3401,10 @@ export class Order extends PosModel {
|
||||
var rounding_method = this.pos.cash_rounding[0].rounding_method;
|
||||
var remaining = this.get_total_with_tax() - this.get_total_paid();
|
||||
var sign = this.get_total_with_tax() > 0 ? 1.0 : -1.0;
|
||||
if(this.get_total_with_tax() < 0 && remaining > 0 || this.get_total_with_tax() > 0 && remaining < 0) {
|
||||
if (
|
||||
(this.get_total_with_tax() < 0 && remaining > 0) ||
|
||||
(this.get_total_with_tax() > 0 && remaining < 0)
|
||||
) {
|
||||
rounding_method = rounding_method.endsWith("UP") ? "DOWN" : rounding_method;
|
||||
}
|
||||
|
||||
@@ -3415,16 +3420,13 @@ export class Order extends PosModel {
|
||||
Math.abs(this.get_total_with_tax()) < this.pos.cash_rounding[0].rounding
|
||||
) {
|
||||
return 0;
|
||||
} else if(rounding_method === "UP" && rounding_applied < 0 && remaining > 0) {
|
||||
} else if (rounding_method === "UP" && rounding_applied < 0 && remaining > 0) {
|
||||
rounding_applied += this.pos.cash_rounding[0].rounding;
|
||||
}
|
||||
else if(rounding_method === "UP" && rounding_applied > 0 && remaining < 0) {
|
||||
} else if (rounding_method === "UP" && rounding_applied > 0 && remaining < 0) {
|
||||
rounding_applied -= this.pos.cash_rounding[0].rounding;
|
||||
}
|
||||
else if(rounding_method === "DOWN" && rounding_applied > 0 && remaining > 0){
|
||||
} else if (rounding_method === "DOWN" && rounding_applied > 0 && remaining > 0) {
|
||||
rounding_applied -= this.pos.cash_rounding[0].rounding;
|
||||
}
|
||||
else if(rounding_method === "DOWN" && rounding_applied < 0 && remaining < 0){
|
||||
} else if (rounding_method === "DOWN" && rounding_applied < 0 && remaining < 0) {
|
||||
rounding_applied += this.pos.cash_rounding[0].rounding;
|
||||
}
|
||||
return sign * rounding_applied;
|
||||
@@ -3587,7 +3589,7 @@ export class Order extends PosModel {
|
||||
}
|
||||
/* ---- Ship later --- */
|
||||
setShippingDate(shippingDate) {
|
||||
this.shippingDate = shippingDate
|
||||
this.shippingDate = shippingDate;
|
||||
}
|
||||
getShippingDate() {
|
||||
return this.shippingDate;
|
||||
|
||||
@@ -22,6 +22,7 @@ import core from "web.core";
|
||||
* by overriding the loader_params of the models in the back end
|
||||
* in the `pos.session` model
|
||||
*/
|
||||
// FIXME POSREF convert this to a class.
|
||||
export const PaymentInterface = core.Class.extend({
|
||||
init: function (pos, payment_method) {
|
||||
this.pos = pos;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import Session from "web.Session";
|
||||
import core from "web.core";
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { ErrorPopup } from "./Popups/ErrorPopup";
|
||||
var _t = core._t;
|
||||
|
||||
@@ -115,7 +114,7 @@ export const PrinterMixin = {
|
||||
|
||||
_onIoTActionResult: function (data) {
|
||||
if (this.pos && (data === false || data.result === false)) {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.pos.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Connection to the printer failed"),
|
||||
body: _t("Please check if the printer is still connected."),
|
||||
});
|
||||
@@ -124,7 +123,7 @@ export const PrinterMixin = {
|
||||
|
||||
_onIoTActionFail: function () {
|
||||
if (this.pos) {
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.pos.env.services.popup.add(ErrorPopup, {
|
||||
title: _t("Connection to IoT Box failed"),
|
||||
body: _t("Please check if the IoT Box is still connected."),
|
||||
});
|
||||
|
||||
@@ -41,3 +41,9 @@ export function withComputedProperties(obj, sources, descriptor) {
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export class Reactive {
|
||||
constructor() {
|
||||
return reactive(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,14 @@
|
||||
<div class="subwindow">
|
||||
<div class="subwindow-container">
|
||||
<div class="subwindow-container-fix screens">
|
||||
<!-- FIXME POSREF: remove the t-key after owl 2.0.6+ is merged, see https://github.com/odoo/owl/pull/1344 -->
|
||||
<!-- FIXME POSREF: better error handling in main screens (currently, a crash in owl lifecycle of a main screen blows up the application and the error can't be displayed) -->
|
||||
<t isShown="!pos.tempScreen" t-component="pos.mainScreen.component"
|
||||
t-props="pos.mainScreen.props" t-key="pos.mainScreen.name" />
|
||||
<t t-if="pos.tempScreen" t-component="pos.tempScreen.component"
|
||||
t-props="pos.tempScreen.props" t-key="pos.tempScreen.name" />
|
||||
t-props="pos.mainScreen.props" t-key="pos.mainScreen.component.name"/>
|
||||
<ErrorHandler onError.bind="onTempScreenError">
|
||||
<t t-if="pos.tempScreen" t-component="pos.tempScreen.component"
|
||||
t-props="pos.tempScreen.props" t-key="pos.tempScreen.name" />
|
||||
</ErrorHandler>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.PosPopupController" owl="1">
|
||||
<div class="popups" t-att-hidden="!isShown()">
|
||||
<t t-foreach="popups" t-as="popup" t-key="popup.props.id">
|
||||
<div role="dialog" class="modal-dialog" t-att-class="{ oe_hidden: !shouldShow(popup) }">
|
||||
<t t-component="popup.component" t-props="popup.props" />
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -7,7 +7,7 @@
|
||||
<t t-if="!env.isMobile">
|
||||
<div class="top-content">
|
||||
<div class="button back"
|
||||
t-on-click="() => this.showScreen('ProductScreen')">
|
||||
t-on-click="() => pos.showScreen('ProductScreen')">
|
||||
<i class="fa fa-angle-double-left fa-fw"></i>
|
||||
<span class="back_text">Back</span>
|
||||
</div>
|
||||
@@ -108,7 +108,7 @@
|
||||
t-on-click="() => this.trigger('validate-order')">
|
||||
<h1>Validate</h1>
|
||||
</button>
|
||||
<button class="btn-switchpane secondary" t-on-click="() => this.showScreen('ProductScreen', {mobile_pane: 'left'})">
|
||||
<button class="btn-switchpane secondary" t-on-click="() => pos.showScreen('ProductScreen', {mobile_pane: 'left'})">
|
||||
<h1>Review</h1>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+16
-9
@@ -1,34 +1,41 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import makeTestEnvironment from "web.test_env";
|
||||
import testUtils from "web.test_utils";
|
||||
import { mount } from "@web/../tests/helpers/utils";
|
||||
import { LegacyComponent } from "@web/legacy/legacy_component";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { numberBuffer } from "@point_of_sale/app/number_buffer_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const { useState, xml } = owl;
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
|
||||
import { useState, xml } from "@odoo/owl";
|
||||
|
||||
QUnit.module("unit tests for NumberBuffer", {
|
||||
before() {},
|
||||
async beforeEach() {
|
||||
registry.category("services").add("number_buffer", numberBuffer);
|
||||
registry.category("services").add("sound", { start: () => ({ play() {} }) });
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test("simple fast inputs with capture in between", async function (assert) {
|
||||
assert.expect(3);
|
||||
const target = testUtils.prepareTarget();
|
||||
const env = makeTestEnvironment();
|
||||
const env = await makeTestEnv();
|
||||
|
||||
class Root extends LegacyComponent {
|
||||
setup() {
|
||||
this.state = useState({ buffer: "" });
|
||||
numberBuffer.activate();
|
||||
numberBuffer.use({
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.activate();
|
||||
this.numberBuffer.use({
|
||||
nonKeyboardInputEvent: "numpad-click-input",
|
||||
state: this.state,
|
||||
});
|
||||
}
|
||||
resetBuffer() {
|
||||
numberBuffer.capture();
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.capture();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
onClickOne() {
|
||||
this.trigger("numpad-click-input", { key: "1" });
|
||||
@@ -0,0 +1,152 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import makeTestEnvironment from "web.test_env";
|
||||
import { click, getFixture, mount, nextTick, triggerEvent } from "@web/../tests/helpers/utils";
|
||||
import { clearRegistryWithCleanup, makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { popupService } from "@point_of_sale/app/popup/popup_service";
|
||||
import { MainComponentsContainer } from "@web/core/main_components_container";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { xml } = owl;
|
||||
|
||||
// Note that we are creating new popups here to decouple this test from the pos app.
|
||||
class CustomPopup1 extends AbstractAwaitablePopup {}
|
||||
CustomPopup1.template = xml/* html */ `
|
||||
<div class="popup custom-popup-1">
|
||||
<footer>
|
||||
<div class="confirm" t-on-click="confirm">
|
||||
Yes
|
||||
</div>
|
||||
<div class="cancel" t-on-click="cancel">
|
||||
No
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class CustomPopup2 extends AbstractAwaitablePopup {}
|
||||
CustomPopup2.template = xml/* html */ `
|
||||
<div class="popup custom-popup-2">
|
||||
<footer>
|
||||
<div class="confirm" t-on-click="confirm">
|
||||
Okay
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class Root extends PosComponent {
|
||||
static components = { MainComponentsContainer };
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
}
|
||||
Root.template = xml`<MainComponentsContainer/>`;
|
||||
|
||||
let env;
|
||||
QUnit.module("unit tests for PopupContainer", {
|
||||
async beforeEach() {
|
||||
const makeService = (obj) => ({
|
||||
start() {
|
||||
return obj;
|
||||
},
|
||||
});
|
||||
const legacyEnv = makeTestEnvironment();
|
||||
registry.category("services").add("popup", popupService);
|
||||
registry.category("services").add("pos_notification", makeService({ add() {} }));
|
||||
registry.category("services").add("sound", makeService({ play() {} }));
|
||||
registry.category("services").add("pos", makeService({ legacyEnv }));
|
||||
clearRegistryWithCleanup(registry.category("main_components"));
|
||||
env = await makeTestEnv();
|
||||
for (const service of ["pos_notification", "sound", "popup"]) {
|
||||
legacyEnv.services[service] = env.services[service];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test("allow multiple popups at the same time", async function (assert) {
|
||||
const fixture = getFixture();
|
||||
const root = await mount(Root, fixture, { env });
|
||||
|
||||
// Single popup
|
||||
let popup1Promise = root.popup.add(CustomPopup1, {});
|
||||
await nextTick();
|
||||
assert.containsOnce(fixture, ".popup", "popup is open");
|
||||
await click(fixture.querySelector(".modal-dialog .custom-popup-1 .confirm"));
|
||||
let result1 = await popup1Promise;
|
||||
assert.strictEqual(result1.confirmed, true, "popup is confirmed");
|
||||
assert.containsNone(fixture, ".popup", 0, "popup is closed");
|
||||
|
||||
// Multiple popups
|
||||
popup1Promise = root.popup.add(CustomPopup1, {});
|
||||
await nextTick();
|
||||
|
||||
assert.containsOnce(fixture, ".popup", 1, "first popup is shown");
|
||||
|
||||
const popup2Promise = root.popup.add(CustomPopup2, {});
|
||||
await nextTick();
|
||||
|
||||
assert.containsOnce(fixture, ".popup", "only one popup is displayed");
|
||||
assert.containsOnce(fixture, ".custom-popup-2", "second popup is displayed");
|
||||
assert.containsNone(fixture, ".custom-popup-1", "first popup is not displayed");
|
||||
|
||||
await click(fixture.querySelector(".modal-dialog .custom-popup-2 .confirm"));
|
||||
|
||||
assert.containsOnce(fixture, ".popup", "after confirming on popup 2, only 1 should remain");
|
||||
assert.containsOnce(fixture, ".custom-popup-1", "first popup is displayed");
|
||||
assert.containsNone(fixture, ".custom-popup-2", "second popup is closed");
|
||||
|
||||
await click(fixture.querySelector(".modal-dialog .custom-popup-1 .cancel"));
|
||||
|
||||
assert.containsNone(fixture, ".popup", "after cancelling popup 1, no popup should remain.");
|
||||
|
||||
result1 = await popup1Promise;
|
||||
const result2 = await popup2Promise;
|
||||
assert.strictEqual(result1.confirmed, false, "popup 1 is not confirmed");
|
||||
assert.strictEqual(result2.confirmed, true, "popup 2 is confirmed");
|
||||
});
|
||||
|
||||
QUnit.test("pressing cancel/confirm key should only close the top popup", async function (assert) {
|
||||
const fixture = getFixture();
|
||||
const root = await mount(Root, fixture, { env });
|
||||
|
||||
const popup1Promise = root.popup.add(CustomPopup1, {
|
||||
confirmKey: "Enter",
|
||||
cancelKey: "Escape",
|
||||
});
|
||||
await nextTick();
|
||||
assert.containsOnce(fixture, ".custom-popup-1", "first popup is open");
|
||||
|
||||
const popup2Promise = root.popup.add(CustomPopup2, {
|
||||
confirmKey: "Enter",
|
||||
cancelKey: "Escape",
|
||||
});
|
||||
await nextTick();
|
||||
assert.containsNone(fixture, ".custom-popup-1", "first popup no longer displayed");
|
||||
assert.containsOnce(fixture, ".custom-popup-2", "second popup is open");
|
||||
|
||||
await triggerEvent(fixture, ".popup", "keyup", { key: "Escape" });
|
||||
assert.containsNone(fixture, ".custom-popup-2", "second popup no longer displayed");
|
||||
assert.containsOnce(fixture, ".custom-popup-1", "first popup is displayed again");
|
||||
|
||||
const result2 = await popup2Promise;
|
||||
assert.strictEqual(
|
||||
result2.confirmed,
|
||||
false,
|
||||
"canceling the popup with escape resolved the promise with confirmed: false"
|
||||
);
|
||||
|
||||
await triggerEvent(fixture, ".popup", "keyup", { key: "Enter" });
|
||||
assert.containsNone(fixture, ".popup", "no popups remain");
|
||||
|
||||
const result1 = await popup1Promise;
|
||||
assert.strictEqual(
|
||||
result1.confirmed,
|
||||
true,
|
||||
"confirming the popup with enter resolved the promise with confirmed: true"
|
||||
);
|
||||
});
|
||||
@@ -1,181 +0,0 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { PosPopupController } from "@point_of_sale/js/Popups/PosPopupController";
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import makeTestEnvironment from "web.test_env";
|
||||
import testUtils from "web.test_utils";
|
||||
import { mount } from "@web/../tests/helpers/utils";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const { EventBus, useSubEnv, xml } = owl;
|
||||
|
||||
// Note that we are creating new popups here to decouple this test from the pos app.
|
||||
class CustomPopup1 extends AbstractAwaitablePopup {}
|
||||
CustomPopup1.template = xml/* html */ `
|
||||
<div class="popup custom-popup-1">
|
||||
<footer>
|
||||
<div class="confirm" t-on-click="confirm">
|
||||
Yes
|
||||
</div>
|
||||
<div class="cancel" t-on-click="cancel">
|
||||
No
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class CustomPopup2 extends AbstractAwaitablePopup {}
|
||||
CustomPopup2.template = xml/* html */ `
|
||||
<div class="popup custom-popup-2">
|
||||
<footer>
|
||||
<div class="confirm" t-on-click="confirm">
|
||||
Okay
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let env;
|
||||
QUnit.module("unit tests for PosPopupController", {
|
||||
async beforeEach() {
|
||||
const posbus = new EventBus();
|
||||
const makeService = (obj) => ({
|
||||
start() {
|
||||
return obj;
|
||||
},
|
||||
});
|
||||
const legacyEnv = makeTestEnvironment();
|
||||
registry.category("services").add("pos_notification", makeService({ add() {} }));
|
||||
registry.category("services").add("sound", makeService({ play() {} }));
|
||||
registry.category("services").add("pos", makeService({ legacyEnv }));
|
||||
env = await makeTestEnv();
|
||||
for (const service of ["pos", "pos_notification", "sound"]) {
|
||||
legacyEnv.services[service] = env.services[service];
|
||||
}
|
||||
env.posbus = posbus;
|
||||
legacyEnv.posbus = posbus;
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test("allow multiple popups at the same time", async function (assert) {
|
||||
assert.expect(12);
|
||||
class Root extends PosComponent {
|
||||
static components = { PosPopupController };
|
||||
setup() {
|
||||
super.setup();
|
||||
useSubEnv({
|
||||
isDebug: () => false,
|
||||
});
|
||||
}
|
||||
}
|
||||
Root.template = xml/* html */ `
|
||||
<div>
|
||||
<PosPopupController />
|
||||
</div>
|
||||
`;
|
||||
|
||||
const root = await mount(Root, testUtils.prepareTarget(), { env });
|
||||
|
||||
// Check 1 popup
|
||||
let popup1Promise = root.showPopup(CustomPopup1, {});
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 1);
|
||||
testUtils.dom.click(root.el.querySelector(".modal-dialog .custom-popup-1 .confirm"));
|
||||
let result1 = await popup1Promise;
|
||||
assert.strictEqual(result1.confirmed, true);
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 0);
|
||||
|
||||
// Check multiple popups
|
||||
popup1Promise = root.showPopup(CustomPopup1, {});
|
||||
await testUtils.nextTick();
|
||||
|
||||
// Check if the first popup is shown.
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 1);
|
||||
|
||||
const popup2Promise = root.showPopup(CustomPopup2, {});
|
||||
await testUtils.nextTick();
|
||||
|
||||
// Check for the second popup.
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 2);
|
||||
|
||||
// popup 1 should be hidden
|
||||
assert.strictEqual(root.el.querySelectorAll(".modal-dialog.oe_hidden").length, 1);
|
||||
|
||||
// click confirm on popup 2
|
||||
testUtils.dom.click(root.el.querySelector(".modal-dialog .custom-popup-2 .confirm"));
|
||||
await testUtils.nextTick();
|
||||
|
||||
// after confirming on popup 2, only 1 should remain.
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 1);
|
||||
assert.strictEqual(root.el.querySelectorAll(".modal-dialog .custom-popup-2").length, 0);
|
||||
|
||||
// popup 1 should not be hidden
|
||||
const customPopup1 = root.el.querySelector(".modal-dialog");
|
||||
assert.strictEqual(![...customPopup1.classList].includes("oe_hidden"), true);
|
||||
testUtils.dom.click(root.el.querySelector(".modal-dialog .custom-popup-1 .cancel"));
|
||||
await testUtils.nextTick();
|
||||
|
||||
// after cancelling popup 1, no popup should remain.
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 0);
|
||||
|
||||
result1 = await popup1Promise;
|
||||
const result2 = await popup2Promise;
|
||||
assert.strictEqual(result1.confirmed, false); // false because it's cancelled.
|
||||
assert.strictEqual(result2.confirmed, true); // true because it's confirmed.
|
||||
});
|
||||
|
||||
QUnit.test("pressing cancel/confirm key should only close the top popup", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
class Root extends PosComponent {
|
||||
static components = { PosPopupController };
|
||||
setup() {
|
||||
super.setup();
|
||||
useSubEnv({
|
||||
isDebug: () => false,
|
||||
});
|
||||
}
|
||||
}
|
||||
Root.template = xml/* html */ `
|
||||
<div>
|
||||
<PosPopupController />
|
||||
</div>
|
||||
`;
|
||||
|
||||
const root = await mount(Root, testUtils.prepareTarget(), { env });
|
||||
|
||||
const popup1Promise = root.showPopup(CustomPopup1, {
|
||||
confirmKey: "Enter",
|
||||
cancelKey: "Escape",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 1);
|
||||
|
||||
const popup2Promise = root.showPopup(CustomPopup2, {
|
||||
confirmKey: "Enter",
|
||||
cancelKey: "Escape",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 2);
|
||||
|
||||
// Pressing 'Escape' should cancel the top popup which is the CustomPopup2.
|
||||
testUtils.dom.triggerEvent(window, "keyup", { key: "Escape" });
|
||||
await testUtils.nextTick();
|
||||
|
||||
// Therefore, the popup2Promise has now resolved with `confirmed` value = false.
|
||||
const result2 = await popup2Promise;
|
||||
assert.strictEqual(result2.confirmed, false);
|
||||
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 1);
|
||||
|
||||
testUtils.dom.triggerEvent(window, "keyup", { key: "Enter" });
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.strictEqual(root.el.querySelectorAll(".popup").length, 0);
|
||||
|
||||
const result1 = await popup1Promise;
|
||||
assert.strictEqual(result1.confirmed, true);
|
||||
});
|
||||
@@ -3,7 +3,6 @@
|
||||
import core from "web.core";
|
||||
import rpc from "web.rpc";
|
||||
import { PaymentInterface } from "@point_of_sale/js/payment";
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
|
||||
var _t = core._t;
|
||||
@@ -366,7 +365,7 @@ export const PaymentAdyen = PaymentInterface.extend({
|
||||
if (!title) {
|
||||
title = _t("Adyen Error");
|
||||
}
|
||||
Gui.showPopup(ErrorPopup, {
|
||||
this.pos.env.services.popup.add(ErrorPopup, {
|
||||
title: title,
|
||||
body: msg,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
|
||||
@@ -11,11 +11,12 @@ export class DiscountButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
async onClick() {
|
||||
var self = this;
|
||||
const { confirmed, payload } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(NumberPopup, {
|
||||
title: this.env._t("Discount Percentage"),
|
||||
startingValue: this.env.pos.config.discount_pc,
|
||||
isInputSelected: true,
|
||||
@@ -31,7 +32,7 @@ export class DiscountButton extends PosComponent {
|
||||
var lines = order.get_orderlines();
|
||||
var product = this.env.pos.db.get_product_by_id(this.env.pos.config.discount_product_id[0]);
|
||||
if (product === undefined) {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("No discount product found"),
|
||||
body: this.env._t(
|
||||
"The discount product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'."
|
||||
|
||||
@@ -5,8 +5,8 @@ import { patch } from "@web/core/utils/patch";
|
||||
import { SelectCashierMixin } from "@pos_hr/js/SelectCashierMixin";
|
||||
import { useBarcodeReader } from "@point_of_sale/js/custom_hooks";
|
||||
|
||||
patch(CashierName.prototype, "pos_hr.CashierName", {
|
||||
...SelectCashierMixin,
|
||||
patch(CashierName.prototype, "pos_hr.CashierName SelectCashierMixin", SelectCashierMixin);
|
||||
patch(CashierName.prototype, "pos_hr.CashierName methods", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
useBarcodeReader({ cashier: this.barcodeCashierAction });
|
||||
|
||||
@@ -7,7 +7,7 @@ patch(Chrome.prototype, "pos_hr.Chrome", {
|
||||
async start() {
|
||||
await this._super(...arguments);
|
||||
if (this.env.pos.config.module_pos_hr) {
|
||||
this.showTempScreen("LoginScreen");
|
||||
this.pos.showTempScreen("LoginScreen");
|
||||
}
|
||||
},
|
||||
get showCashMoveButton() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
|
||||
const { useState } = owl;
|
||||
@@ -10,10 +11,11 @@ export class HeaderLockButton extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.state = useState({ isUnlockIcon: true, title: "Unlocked" });
|
||||
this.pos = usePos();
|
||||
}
|
||||
async showLoginScreen() {
|
||||
this.env.pos.reset_cashier();
|
||||
await this.showTempScreen("LoginScreen");
|
||||
await this.pos.showTempScreen("LoginScreen");
|
||||
}
|
||||
onMouseOver(isMouseOver) {
|
||||
this.state.isUnlockIcon = !isMouseOver;
|
||||
|
||||
@@ -5,16 +5,18 @@ import { SelectCashierMixin } from "@pos_hr/js/SelectCashierMixin";
|
||||
import { useBarcodeReader } from "@point_of_sale/js/custom_hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class LoginScreen extends PosComponent {
|
||||
static template = "LoginScreen";
|
||||
}
|
||||
// FIXME stop this double patch once the mixin is converted to a hook.
|
||||
// FIXME POSREF stop this double patch once the mixin is converted to a hook.
|
||||
patch(LoginScreen.prototype, "pos_hr.LoginScreen SelectCashierMixin", SelectCashierMixin);
|
||||
patch(LoginScreen.prototype, "pos_hr.LoginScreen methods", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
useBarcodeReader({ cashier: this.barcodeCashierAction }, true);
|
||||
this.pos = usePos();
|
||||
},
|
||||
async selectCashier() {
|
||||
if (await this._super(...arguments)) {
|
||||
@@ -22,19 +24,19 @@ patch(LoginScreen.prototype, "pos_hr.LoginScreen methods", {
|
||||
}
|
||||
},
|
||||
async barcodeCashierAction(code) {
|
||||
if (await this._super(code) && this.env.pos.get_cashier().id) {
|
||||
if ((await this._super(code)) && this.env.pos.get_cashier().id) {
|
||||
this.back();
|
||||
}
|
||||
},
|
||||
back() {
|
||||
this.props.resolve({ confirmed: false, payload: false });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
this.env.pos.hasLoggedIn = true;
|
||||
this.env.posbus.trigger("start-cash-control");
|
||||
},
|
||||
confirm() {
|
||||
this.props.resolve({ confirmed: true, payload: true });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
},
|
||||
get shopName() {
|
||||
return this.env.pos.config.name;
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
// FIXME POSREF make this into a hook or something
|
||||
export const SelectCashierMixin = {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.popup = useService("popup");
|
||||
},
|
||||
async askPin(employee) {
|
||||
const { confirmed, payload: inputPin } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload: inputPin } = await this.popup.add(NumberPopup, {
|
||||
isPassword: true,
|
||||
title: this.env._t("Password ?"),
|
||||
startingValue: null,
|
||||
@@ -21,7 +26,7 @@ export const SelectCashierMixin = {
|
||||
if (employee.pin === Sha1.hash(inputPin)) {
|
||||
return employee;
|
||||
} else {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Incorrect Password"),
|
||||
});
|
||||
return;
|
||||
@@ -43,7 +48,7 @@ export const SelectCashierMixin = {
|
||||
isSelected: false,
|
||||
};
|
||||
});
|
||||
let { confirmed, payload: employee } = await this.showPopup(SelectionPopup, {
|
||||
let { confirmed, payload: employee } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Change Cashier"),
|
||||
list: employeesList,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ This module adapts the behavior of the PoS when the pos_hr and pos_restaurant ar
|
||||
'auto_install': True,
|
||||
'assets': {
|
||||
'point_of_sale.assets': [
|
||||
'pos_hr_restaurant/static/src/js/**/*.js',
|
||||
'pos_hr_restaurant/static/src/**/*',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* @odoo-module alias=pos_restaurant_hr.chrome */
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import "@pos_restaurant/app/pos_store";
|
||||
import { PosStore } from "@point_of_sale/app/pos_store";
|
||||
|
||||
patch(PosStore.prototype, "pos_hr_restaurant.PosStore", {
|
||||
shouldResetIdleTimer() {
|
||||
return this.tempScreen?.name !== "LoginScreen" && this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/* @odoo-module alias=pos_restaurant_hr.chrome */
|
||||
|
||||
import { Chrome } from "@point_of_sale/js/Chrome";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(Chrome.prototype, "pos_hr_restaurant.Chrome", {
|
||||
//@override
|
||||
_shouldResetIdleTimer() {
|
||||
return this._super() && this.pos.tempScreen?.name !== "LoginScreen";
|
||||
},
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { TextInputPopup } from "@point_of_sale/js/Popups/TextInputPopup";
|
||||
|
||||
export class PromoCodeButton extends PosComponent {
|
||||
@@ -10,11 +10,12 @@ export class PromoCodeButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
let { confirmed, payload: code } = await this.showPopup(TextInputPopup, {
|
||||
let { confirmed, payload: code } = await this.popup.add(TextInputPopup, {
|
||||
title: this.env._t("Enter Code"),
|
||||
startingValue: "",
|
||||
placeholder: this.env._t("Gift card or Discount code"),
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
|
||||
export class RewardButton extends PosComponent {
|
||||
@@ -11,6 +10,8 @@ export class RewardButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.notification = useService("pos_notification");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ export class RewardButton extends PosComponent {
|
||||
label: this.env.pos.db.get_product_by_id(product_id).display_name,
|
||||
item: product_id,
|
||||
}));
|
||||
const { confirmed, payload: selectedProduct } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload: selectedProduct } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Please select a product for this reward"),
|
||||
list: productsList,
|
||||
});
|
||||
@@ -93,7 +94,7 @@ export class RewardButton extends PosComponent {
|
||||
const result = order._applyReward(reward, coupon_id, args);
|
||||
if (result !== true) {
|
||||
// Returned an error
|
||||
Gui.showNotification(result);
|
||||
this.notification.add(result);
|
||||
}
|
||||
order._updateRewards();
|
||||
return result;
|
||||
@@ -114,7 +115,7 @@ export class RewardButton extends PosComponent {
|
||||
label: reward.reward.description,
|
||||
item: reward,
|
||||
}));
|
||||
const { confirmed, payload: selectedReward } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload: selectedReward } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Please select a reward"),
|
||||
list: rewardsList,
|
||||
});
|
||||
|
||||
@@ -4,10 +4,16 @@ import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
export class eWalletButton extends PosComponent {
|
||||
static template = "point_of_sale.eWalletButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
|
||||
_getEWalletRewards(order) {
|
||||
const claimableRewards = order.getClaimableRewards();
|
||||
const eWalletRewards = claimableRewards.filter(
|
||||
@@ -28,7 +34,7 @@ export class eWalletButton extends PosComponent {
|
||||
if (eWalletPrograms.length == 1) {
|
||||
selectedProgram = eWalletPrograms[0];
|
||||
} else {
|
||||
const { confirmed, payload } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Refund with eWallet"),
|
||||
list: eWalletPrograms.map((program) => ({
|
||||
id: program.id,
|
||||
@@ -55,7 +61,7 @@ export class eWalletButton extends PosComponent {
|
||||
if (eWalletRewards.length == 1) {
|
||||
eWalletReward = eWalletRewards[0];
|
||||
} else {
|
||||
const { confirmed, payload } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Use eWallet to pay"),
|
||||
list: eWalletRewards.map(({ reward, coupon_id }) => ({
|
||||
id: reward.id,
|
||||
@@ -75,7 +81,7 @@ export class eWalletButton extends PosComponent {
|
||||
);
|
||||
if (result !== true) {
|
||||
// Returned an error
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Error"),
|
||||
body: result,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { Order, Orderline, PosGlobalState } from "@point_of_sale/js/models";
|
||||
import session from "web.session";
|
||||
import concurrency from "web.concurrency";
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
import { round_decimals, round_precision } from "web.utils";
|
||||
import core from "web.core";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
@@ -1627,7 +1626,7 @@ patch(Order.prototype, "pos_loyalty.Order", {
|
||||
// Allow rejecting a gift card that is not yet paid.
|
||||
const program = this.pos.program_by_id[payload.program_id];
|
||||
if (program && program.program_type === "gift_card" && !payload.has_source_order) {
|
||||
const { confirmed } = await Gui.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.pos.env.services.popup.add(ConfirmPopup, {
|
||||
title: _t("Unpaid gift card"),
|
||||
body: _t(
|
||||
"This gift card is not linked to any order. Do you really want to apply its reward?"
|
||||
@@ -1666,7 +1665,7 @@ patch(Order.prototype, "pos_loyalty.Order", {
|
||||
async activateCode(code) {
|
||||
const res = await this._activateCode(code);
|
||||
if (res !== true) {
|
||||
Gui.showNotification(res);
|
||||
this.pos.env.services.pos_notification.add(res);
|
||||
}
|
||||
},
|
||||
isProgramsResettable() {
|
||||
|
||||
@@ -62,7 +62,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
|
||||
);
|
||||
}
|
||||
if (!successful) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Error validating rewards"),
|
||||
body: payload.message,
|
||||
});
|
||||
|
||||
@@ -7,10 +7,12 @@ import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { TextInputPopup } from "@point_of_sale/js/Popups/TextInputPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.notification = useService("pos_notification");
|
||||
useBarcodeReader({
|
||||
coupon: this._onCouponScan,
|
||||
});
|
||||
@@ -21,12 +23,12 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
.get_orderlines()
|
||||
.find((line) => line.getEWalletGiftCardProgramType() === "ewallet");
|
||||
if (eWalletLine && !order.get_partner()) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Customer needed"),
|
||||
body: this.env._t("eWallet requires a customer to be selected"),
|
||||
});
|
||||
if (confirmed) {
|
||||
const { confirmed, payload: newPartner } = await this.showTempScreen(
|
||||
const { confirmed, payload: newPartner } = await this.pos.showTempScreen(
|
||||
"PartnerListScreen",
|
||||
{ partner: null }
|
||||
);
|
||||
@@ -52,7 +54,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
|
||||
// If gift card program setting is 'scan_use', ask for the code.
|
||||
if (this.env.pos.config.gift_card_settings == "scan_use") {
|
||||
const { confirmed, payload: code } = await this.showPopup(TextInputPopup, {
|
||||
const { confirmed, payload: code } = await this.popup.add(TextInputPopup, {
|
||||
title: this.env._t("Generate a Gift Card"),
|
||||
startingValue: "",
|
||||
placeholder: this.env._t("Enter the gift card code"),
|
||||
@@ -78,7 +80,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
// There should be maximum one gift card for a given code.
|
||||
const giftCard = fetchedGiftCard[0];
|
||||
if (giftCard && giftCard.source_pos_order_id) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("This gift card has already been sold"),
|
||||
body: this.env._t(
|
||||
"You cannot sell a gift card that has already been sold."
|
||||
@@ -95,7 +97,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
options.giftCardId = giftCard.id;
|
||||
}
|
||||
} else {
|
||||
this.showNotification("Please enter a valid gift card code.");
|
||||
this.notification.add("Please enter a valid gift card code.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -117,7 +119,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
const linkedPrograms = linkedProgramIds.map((id) => this.env.pos.program_by_id[id]);
|
||||
let selectedProgram = null;
|
||||
if (linkedPrograms.length > 1) {
|
||||
const { confirmed, payload: program } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload: program } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("Select program"),
|
||||
list: linkedPrograms.map((program) => ({
|
||||
id: program.id,
|
||||
@@ -173,7 +175,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
if (selectedLine.eWalletGiftCardProgram) {
|
||||
// Do not allow negative quantity or price in a gift card or ewallet orderline.
|
||||
// Refunding gift card or ewallet is not supported.
|
||||
this.showNotification(
|
||||
this.notification.add(
|
||||
this.env._t(
|
||||
"You cannot set negative quantity or price to gift card or ewallet."
|
||||
),
|
||||
@@ -189,7 +191,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
(event.detail.key === "Backspace" || event.detail.key === "Delete")
|
||||
) {
|
||||
const reward = this.env.pos.reward_by_id[selectedLine.reward_id];
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Deactivating reward"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t(
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { TicketScreen } from "@point_of_sale/js/Screens/TicketScreen/TicketScreen";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
/**
|
||||
* Prevent refunding ewallet/gift card lines.
|
||||
*/
|
||||
patch(TicketScreen.prototype, "pos_loyalty.TicketScreen", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.notification = useService("pos_notification");
|
||||
},
|
||||
_onUpdateSelectedOrderline() {
|
||||
const order = this.getSelectedSyncedOrder();
|
||||
if (!order) {
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
const selectedOrderlineId = this.getSelectedOrderlineId();
|
||||
const orderline = order.orderlines.find((line) => line.id == selectedOrderlineId);
|
||||
if (orderline && this._isEWalletGiftCard(orderline)) {
|
||||
this._showNotAllowedRefundNotification();
|
||||
return numberBuffer.reset();
|
||||
return this.numberBuffer.reset();
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
@@ -31,7 +35,7 @@ patch(TicketScreen.prototype, "pos_loyalty.TicketScreen", {
|
||||
return this._super(...arguments);
|
||||
},
|
||||
_showNotAllowedRefundNotification() {
|
||||
this.showNotification(
|
||||
this.notification.add(
|
||||
this.env._t(
|
||||
"Refunding a top up or reward product for an eWallet or gift card program is not allowed."
|
||||
),
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { _t } from "web.core";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { PaymentScreen } from "@point_of_sale/js/Screens/PaymentScreen/PaymentScreen";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { useBarcodeReader } from "@point_of_sale/js/custom_hooks";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { PaymentTransactionPopup } from "./PaymentTransactionPopup";
|
||||
@@ -128,7 +127,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
* @override
|
||||
*/
|
||||
async validateOrder(isForceValidate) {
|
||||
numberBuffer.capture();
|
||||
this.numberBuffer.capture();
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
@@ -137,7 +136,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
* @override
|
||||
*/
|
||||
async _sendPaymentRequest({ detail: line }) {
|
||||
numberBuffer.capture();
|
||||
this.numberBuffer.capture();
|
||||
return this._super(...arguments);
|
||||
},
|
||||
_get_swipe_pending_line() {
|
||||
@@ -212,7 +211,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
credit_code_transaction(parsed_result, old_deferred, retry_nr) {
|
||||
var order = this.env.pos.get_order();
|
||||
if (order.get_due(order.selected_paymentline) < 0) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Refunds not supported"),
|
||||
body: this.env._t(
|
||||
"Credit card refunds are not supported. Instead select your credit card payment method, click 'Validate' and refund the original charge manually through the Vantiv backend."
|
||||
@@ -229,7 +228,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
var decodedMagtek = self.env.pos.decodeMagtek(parsed_result.code);
|
||||
|
||||
if (!decodedMagtek) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Could not read card"),
|
||||
body: this.env._t(
|
||||
"This can be caused by a badly executed swipe or by not having your keyboard layout set to US QWERTY (not US International)."
|
||||
@@ -354,7 +353,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
order.selected_paymentline.mercury_data = response; // used to reverse transactions
|
||||
order.selected_paymentline.set_credit_card_name();
|
||||
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
order.trigger("change", order); // needed so that export_to_JSON gets triggered
|
||||
self.render();
|
||||
|
||||
@@ -421,22 +420,24 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
isSelected: false,
|
||||
item: paymentMethod.item,
|
||||
}));
|
||||
this.showPopup(SelectionPopup, {
|
||||
title: this.env._t("Pay with: "),
|
||||
list: selectionList,
|
||||
}).then(({ confirmed, payload: selectedPaymentMethod }) => {
|
||||
if (confirmed) {
|
||||
parsed_result.payment_method_id = selectedPaymentMethod;
|
||||
this.credit_code_transaction(parsed_result);
|
||||
} else {
|
||||
this.credit_code_cancel();
|
||||
}
|
||||
});
|
||||
this.popup
|
||||
.add(SelectionPopup, {
|
||||
title: this.env._t("Pay with: "),
|
||||
list: selectionList,
|
||||
})
|
||||
.then(({ confirmed, payload: selectedPaymentMethod }) => {
|
||||
if (confirmed) {
|
||||
parsed_result.payment_method_id = selectedPaymentMethod;
|
||||
this.credit_code_transaction(parsed_result);
|
||||
} else {
|
||||
this.credit_code_cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
remove_paymentline_by_ref(line) {
|
||||
this.env.pos.get_order().remove_paymentline(line);
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
this.render();
|
||||
},
|
||||
do_reversal(line, is_voidsale, old_deferred, retry_nr) {
|
||||
@@ -446,7 +447,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
|
||||
// show the transaction popup.
|
||||
// the transaction deferred is used to update transaction status
|
||||
this.showPopup(PaymentTransactionPopup, {
|
||||
this.popup.add(PaymentTransactionPopup, {
|
||||
transaction: def,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { PosPopupController } from "@point_of_sale/js/Popups/PosPopupController";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { PaymentTransactionPopup } from "./PaymentTransactionPopup";
|
||||
|
||||
patch(PosPopupController, "pos_mercury.PosPopupController", {
|
||||
components: { ...PosPopupController.components, PaymentTransactionPopup },
|
||||
});
|
||||
@@ -13,7 +13,7 @@ patch(ProductScreen.prototype, "pos_mercury.ProductScreen", {
|
||||
});
|
||||
},
|
||||
credit_error_action() {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
body: this.env._t("Go to payment screen to use cards"),
|
||||
});
|
||||
},
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
|
||||
export class BackToFloorButton extends PosComponent {
|
||||
@@ -20,6 +20,6 @@ export class BackToFloorButton extends PosComponent {
|
||||
return this.table != null;
|
||||
}
|
||||
backToFloorScreen() {
|
||||
this.showScreen("FloorScreen", { floor: this.floor });
|
||||
this.pos.showScreen("FloorScreen", { floor: this.floor });
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Navbar } from "@point_of_sale/app/navbar/navbar";
|
||||
import { BackToFloorButton } from "../js/ChromeWidgets/BackToFloorButton";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { BackToFloorButton } from "./BackToFloorButton";
|
||||
|
||||
patch(Navbar, "pos_restaurant.Navbar", {
|
||||
components: { ...Navbar.components, BackToFloorButton },
|
||||
@@ -0,0 +1,24 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { popupService } from "@point_of_sale/app/popup/popup_service";
|
||||
|
||||
patch(popupService, "pos_restaurant.popupService", {
|
||||
start() {
|
||||
return Object.assign(this._super(...arguments), {
|
||||
closePopupsButError() {
|
||||
const popups = Object.values(this.popups);
|
||||
const isErrorPopupOpen = popups.some((popup) =>
|
||||
// FIXME POSREF: this seems very brittle.
|
||||
popup.component.name.toLowerCase().includes("error")
|
||||
);
|
||||
if (!isErrorPopupOpen) {
|
||||
for (const popup of popups) {
|
||||
popup.props.close(false);
|
||||
}
|
||||
}
|
||||
return !isErrorPopupOpen;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { PosStore } from "@point_of_sale/app/pos_store";
|
||||
import { PaymentScreen } from "@point_of_sale/js/Screens/PaymentScreen/PaymentScreen";
|
||||
import { FloorScreen } from "@pos_restaurant/js/Screens/FloorScreen/FloorScreen";
|
||||
|
||||
const NON_IDLE_EVENTS = [
|
||||
"mousemove",
|
||||
"mousedown",
|
||||
"touchstart",
|
||||
"touchend",
|
||||
"touchmove",
|
||||
"click",
|
||||
"scroll",
|
||||
"keypress",
|
||||
];
|
||||
let IDLE_TIMER_SETTER;
|
||||
|
||||
/**
|
||||
* Go back to the floor screen if the user has been idle for a while.
|
||||
*/
|
||||
patch(PosStore.prototype, "pos_restaurant.PosStore", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.globalState.ready.then(() => {
|
||||
if (this.globalState.config.iface_floorplan) {
|
||||
this.setActivityListeners();
|
||||
}
|
||||
});
|
||||
},
|
||||
setActivityListeners() {
|
||||
IDLE_TIMER_SETTER = this.setIdleTimer.bind(this);
|
||||
for (const event of NON_IDLE_EVENTS) {
|
||||
window.addEventListener(event, IDLE_TIMER_SETTER);
|
||||
}
|
||||
},
|
||||
setIdleTimer() {
|
||||
clearTimeout(this.idleTimer);
|
||||
if (this.shouldResetIdleTimer()) {
|
||||
this.idleTimer = setTimeout(() => this.actionAfterIdle(), 60000);
|
||||
}
|
||||
},
|
||||
async actionAfterIdle() {
|
||||
const isPopupClosed = this.popup.closePopupsButError();
|
||||
if (isPopupClosed) {
|
||||
this.closeTempScreen();
|
||||
const { table } = this.globalState;
|
||||
const order = this.globalState.get_order();
|
||||
if (order && order.get_screen_data().name === "ReceiptScreen") {
|
||||
// When the order is finalized, we can safely remove it from the memory
|
||||
// We check that it's in ReceiptScreen because we want to keep the order if it's in a tipping state
|
||||
this.globalState.removeOrder(order);
|
||||
}
|
||||
this.showScreen("FloorScreen", { floor: table?.floor });
|
||||
}
|
||||
},
|
||||
shouldResetIdleTimer() {
|
||||
const stayPaymentScreen =
|
||||
this.mainScreen.component === PaymentScreen &&
|
||||
this.globalState.get_order().paymentlines.length > 0;
|
||||
return (
|
||||
this.globalState.config.iface_floorplan &&
|
||||
!stayPaymentScreen &&
|
||||
this.mainScreen.component !== FloorScreen
|
||||
);
|
||||
},
|
||||
showScreen() {
|
||||
this._super(...arguments);
|
||||
this.setIdleTimer();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* FIXME POSREF this is now dead code, check if we still want this
|
||||
* Before closing pos, we remove the event listeners set on window
|
||||
* for detecting activities outside FloorScreen.
|
||||
*/
|
||||
async _closePos() {
|
||||
if (IDLE_TIMER_SETTER) {
|
||||
for (const event of NON_IDLE_EVENTS) {
|
||||
window.removeEventListener(event, IDLE_TIMER_SETTER);
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -1,34 +1,12 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Chrome } from "@point_of_sale/js/Chrome";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
const NON_IDLE_EVENTS =
|
||||
"mousemove mousedown touchstart touchend touchmove click scroll keypress".split(/\s+/);
|
||||
let IDLE_TIMER_SETTER;
|
||||
import { Chrome } from "@point_of_sale/js/Chrome";
|
||||
|
||||
patch(Chrome.prototype, "pos_restaurant.Chrome", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async start() {
|
||||
await this._super(...arguments);
|
||||
if (this.env.pos.config.iface_floorplan) {
|
||||
this._setActivityListeners();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* Do not set `FloorScreen` to the order.
|
||||
*/
|
||||
_setScreenData(name) {
|
||||
if (name === "FloorScreen") {
|
||||
return;
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* FIXME POSREF move to pos store
|
||||
* `FloorScreen` is the start screen if there are floors.
|
||||
*/
|
||||
get startScreen() {
|
||||
@@ -39,63 +17,4 @@ patch(Chrome.prototype, "pos_restaurant.Chrome", {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
},
|
||||
_setActivityListeners() {
|
||||
IDLE_TIMER_SETTER = this._setIdleTimer.bind(this);
|
||||
for (const event of NON_IDLE_EVENTS) {
|
||||
window.addEventListener(event, IDLE_TIMER_SETTER);
|
||||
}
|
||||
},
|
||||
_setIdleTimer() {
|
||||
clearTimeout(this.idleTimer);
|
||||
if (this._shouldResetIdleTimer()) {
|
||||
this.idleTimer = setTimeout(() => {
|
||||
this._actionAfterIdle();
|
||||
}, 60000);
|
||||
}
|
||||
},
|
||||
async _actionAfterIdle() {
|
||||
const isPopupClosed = await new Promise((resolve) =>
|
||||
this.env.posbus.trigger("close-popups-but-error", { resolve })
|
||||
);
|
||||
if (isPopupClosed) {
|
||||
if (this.pos.tempScreen) {
|
||||
this.trigger("close-temp-screen");
|
||||
}
|
||||
const table = this.env.pos.table;
|
||||
const order = this.env.pos.get_order();
|
||||
if (order && order.get_screen_data().name === "ReceiptScreen") {
|
||||
// When the order is finalized, we can safely remove it from the memory
|
||||
// We check that it's in ReceiptScreen because we want to keep the order if it's in a tipping state
|
||||
this.env.pos.removeOrder(order);
|
||||
}
|
||||
this.showScreen("FloorScreen", { floor: table ? table.floor : null });
|
||||
}
|
||||
},
|
||||
_shouldResetIdleTimer() {
|
||||
const stayPaymentScreen =
|
||||
this.pos.mainScreen.name === "PaymentScreen" &&
|
||||
this.env.pos.get_order().paymentlines.length > 0;
|
||||
return (
|
||||
this.env.pos.config.iface_floorplan &&
|
||||
!stayPaymentScreen &&
|
||||
this.pos.mainScreen.name !== "FloorScreen"
|
||||
);
|
||||
},
|
||||
__showScreen() {
|
||||
this._super(...arguments);
|
||||
this._setIdleTimer();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* Before closing pos, we remove the event listeners set on window
|
||||
* for detecting activities outside FloorScreen.
|
||||
*/
|
||||
async _closePos() {
|
||||
if (IDLE_TIMER_SETTER) {
|
||||
for (const event of NON_IDLE_EVENTS) {
|
||||
window.removeEventListener(event, IDLE_TIMER_SETTER);
|
||||
}
|
||||
}
|
||||
await this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ patch(TicketButton.prototype, "pos_restaurant.TicketButton", {
|
||||
await this.env.pos._syncAllOrdersFromServer();
|
||||
} finally {
|
||||
this.env.pos.setLoadingOrderState(false);
|
||||
this.showScreen("TicketScreen");
|
||||
this.pos.showScreen("TicketScreen");
|
||||
}
|
||||
} else {
|
||||
this._super(...arguments);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { PosPopupController } from "@point_of_sale/js/Popups/PosPopupController";
|
||||
import { useBus } from "@web/core/utils/hooks";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(PosPopupController.prototype, "pos_restaurant.PosPopupController", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
useBus(this.env.posbus, "close-popups-but-error", this._closePopupsButError);
|
||||
},
|
||||
_closePopupsButError(event) {
|
||||
const { resolve } = event.detail;
|
||||
const isErrorPopupOpen = this.popups.some((popup) =>
|
||||
popup.name.toLowerCase().includes("error")
|
||||
);
|
||||
if (!isErrorPopupOpen) {
|
||||
for (const popup of this.popups) {
|
||||
popup.props.resolve(false);
|
||||
}
|
||||
this.popups.length = 0; // clearing the array but keep the useState
|
||||
}
|
||||
resolve(!isErrorPopupOpen);
|
||||
},
|
||||
});
|
||||
@@ -4,10 +4,10 @@ import { ReceiptScreen } from "@point_of_sale/js/Screens/ReceiptScreen/ReceiptSc
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
export class BillScreen extends ReceiptScreen {
|
||||
static template = "BillScreen";
|
||||
static template = "pos_restaurant.BillScreen";
|
||||
confirm() {
|
||||
this.props.resolve({ confirmed: true, payload: null });
|
||||
this.trigger("close-temp-screen");
|
||||
this.pos.closeTempScreen();
|
||||
}
|
||||
whenClosing() {
|
||||
this.confirm();
|
||||
|
||||
@@ -11,13 +11,15 @@ import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { EditableTable } from "./EditableTable";
|
||||
import { EditBar } from "./EditBar";
|
||||
import { TableWidget } from "./TableWidget";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { onPatched, onMounted, onWillUnmount, useRef, useState } = owl;
|
||||
|
||||
export class FloorScreen extends PosComponent {
|
||||
static components = { EditableTable, EditBar, TableWidget };
|
||||
static template = "FloorScreen";
|
||||
static hideOrderSelector = true;
|
||||
static storeOnOrder = false;
|
||||
|
||||
/**
|
||||
* @param {Object} props
|
||||
@@ -25,6 +27,8 @@ export class FloorScreen extends PosComponent {
|
||||
*/
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
const floor = this.props.floor ? this.props.floor : this.env.pos.floors[0];
|
||||
this.state = useState({
|
||||
selectedFloorId: floor.id,
|
||||
@@ -205,7 +209,7 @@ export class FloorScreen extends PosComponent {
|
||||
await this.env.pos.setTable(table);
|
||||
}
|
||||
const order = this.env.pos.get_order();
|
||||
this.showScreen(order.get_screen_data().name);
|
||||
this.pos.showScreen(order.get_screen_data().name);
|
||||
}
|
||||
}
|
||||
async onSaveTable(table) {
|
||||
@@ -231,7 +235,7 @@ export class FloorScreen extends PosComponent {
|
||||
if (!selectedTable) {
|
||||
return;
|
||||
}
|
||||
const { confirmed, payload: newName } = await this.showPopup(TextInputPopup, {
|
||||
const { confirmed, payload: newName } = await this.popup.add(TextInputPopup, {
|
||||
startingValue: selectedTable.name,
|
||||
title: this.env._t("Table Name ?"),
|
||||
});
|
||||
@@ -248,7 +252,7 @@ export class FloorScreen extends PosComponent {
|
||||
if (!selectedTable) {
|
||||
return;
|
||||
}
|
||||
const { confirmed, payload: inputNumber } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload: inputNumber } = await this.popup.add(NumberPopup, {
|
||||
startingValue: selectedTable.seats,
|
||||
cheap: true,
|
||||
title: this.env._t("Number of Seats ?"),
|
||||
@@ -289,7 +293,7 @@ export class FloorScreen extends PosComponent {
|
||||
if (!this.selectedTable) {
|
||||
return;
|
||||
}
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Are you sure ?"),
|
||||
body: this.env._t("Removing a table cannot be undone"),
|
||||
});
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { TextAreaPopup } from "@point_of_sale/js/Popups/TextAreaPopup";
|
||||
|
||||
export class OrderlineNoteButton extends PosComponent {
|
||||
@@ -10,6 +10,7 @@ export class OrderlineNoteButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
get selectedOrderline() {
|
||||
@@ -20,7 +21,7 @@ export class OrderlineNoteButton extends PosComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const { confirmed, payload: inputNote } = await this.showPopup(TextAreaPopup, {
|
||||
const { confirmed, payload: inputNote } = await this.popup.add(TextAreaPopup, {
|
||||
startingValue: this.selectedOrderline.get_note(),
|
||||
title: this.env._t("Add Internal Note"),
|
||||
});
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
@@ -9,6 +10,7 @@ export class PrintBillButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
_isDisabled() {
|
||||
@@ -16,7 +18,7 @@ export class PrintBillButton extends PosComponent {
|
||||
return order.get_orderlines().length === 0;
|
||||
}
|
||||
onClick() {
|
||||
this.showTempScreen("BillScreen");
|
||||
this.pos.showTempScreen("BillScreen");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
@@ -9,6 +10,7 @@ export class SplitBillButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
_isDisabled() {
|
||||
@@ -20,7 +22,7 @@ export class SplitBillButton extends PosComponent {
|
||||
);
|
||||
}
|
||||
async onClick() {
|
||||
this.showScreen("SplitBillScreen");
|
||||
this.pos.showScreen("SplitBillScreen");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -3,6 +3,7 @@
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* IMPROVEMENT: Perhaps this class is quite complicated for its worth.
|
||||
@@ -15,6 +16,7 @@ export class SubmitOrderButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.clicked = false; //mutex, we don't want to be able to spam the printers
|
||||
}
|
||||
async _onClick() {
|
||||
@@ -27,7 +29,7 @@ export class SubmitOrderButton extends PosComponent {
|
||||
if (isPrintSuccessful) {
|
||||
order.updatePrintedResume();
|
||||
} else {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Printing failed"),
|
||||
body: this.env._t("Failed in printing the changes in the order"),
|
||||
});
|
||||
|
||||
+4
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
|
||||
@@ -11,6 +11,7 @@ export class TableGuestsButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
get currentOrder() {
|
||||
@@ -20,7 +21,7 @@ export class TableGuestsButton extends PosComponent {
|
||||
return this.currentOrder ? this.currentOrder.getCustomerCount() : 0;
|
||||
}
|
||||
async onClick() {
|
||||
const { confirmed, payload: inputNumber } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload: inputNumber } = await this.popup.add(NumberPopup, {
|
||||
startingValue: this.nGuests,
|
||||
cheap: true,
|
||||
title: this.env._t("Guests ?"),
|
||||
@@ -32,7 +33,7 @@ export class TableGuestsButton extends PosComponent {
|
||||
// Set the maximum number possible for an integer
|
||||
const max_capacity = 2 ** 31 - 1;
|
||||
if (guestCount > max_capacity) {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Blocked action"),
|
||||
body: _.str.sprintf(
|
||||
this.env._t("You cannot put a number that exceeds %s "),
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PosComponent } from "@point_of_sale/js/PosComponent";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
@@ -9,11 +10,12 @@ export class TransferOrderButton extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click", this.onClick);
|
||||
}
|
||||
async onClick() {
|
||||
this.env.pos.setCurrentOrderToTransfer();
|
||||
this.showScreen("FloorScreen");
|
||||
this.pos.showScreen("FloorScreen");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ReceiptScreen } from "@point_of_sale/js/Screens/ReceiptScreen/ReceiptScreen";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { onWillUnmount } from "@odoo/owl";
|
||||
import { FloorScreen } from "@pos_restaurant/js/Screens/FloorScreen/FloorScreen";
|
||||
|
||||
patch(ReceiptScreen, "pos_restaurant.ReceiptScreen", {
|
||||
showBackToFloorButton: true,
|
||||
@@ -15,7 +16,7 @@ patch(ReceiptScreen.prototype, "pos_restaurant.ReceiptScreen", {
|
||||
this.pos = usePos();
|
||||
onWillUnmount(() => {
|
||||
// When leaving the receipt screen to the floor screen the order is paid and can be removed
|
||||
if (this.pos.mainScreen.name === "FloorScreen") {
|
||||
if (this.pos.mainScreen.component === FloorScreen) {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Order } from "@point_of_sale/js/models";
|
||||
|
||||
import { SplitOrderline } from "./SplitOrderline";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const { useState, onMounted } = owl;
|
||||
|
||||
@@ -15,6 +16,7 @@ export class SplitBillScreen extends PosComponent {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useListener("click-line", this.onClickLine);
|
||||
this.splitlines = useState(this._initSplitLines(this.env.pos.get_order()));
|
||||
this.newOrderLines = {};
|
||||
@@ -46,7 +48,7 @@ export class SplitBillScreen extends PosComponent {
|
||||
this._updateNewOrder(line);
|
||||
}
|
||||
back() {
|
||||
this.showScreen("ProductScreen");
|
||||
this.pos.showScreen("ProductScreen");
|
||||
}
|
||||
proceed() {
|
||||
if (_.isEmpty(this.splitlines)) {
|
||||
@@ -82,7 +84,7 @@ export class SplitBillScreen extends PosComponent {
|
||||
this.env.pos.orders.add(reactiveNewOrder);
|
||||
this.env.pos.selectedOrder = reactiveNewOrder;
|
||||
}
|
||||
this.showScreen("PaymentScreen");
|
||||
this.pos.showScreen("PaymentScreen");
|
||||
}
|
||||
/**
|
||||
* @param {models.Order} order
|
||||
|
||||
@@ -16,9 +16,9 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
const order = this.env.pos.get_order();
|
||||
if (order) {
|
||||
const { name: screenName } = order.get_screen_data();
|
||||
this.showScreen(screenName);
|
||||
this.pos.showScreen(screenName);
|
||||
} else {
|
||||
this.showScreen("FloorScreen");
|
||||
this.pos.showScreen("FloorScreen");
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -101,7 +101,7 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
? this.env.pos.getTableOrders(this.env.pos.table.id)
|
||||
: this.env.pos.orders;
|
||||
if (orderList.length == 0) {
|
||||
this.showScreen("FloorScreen");
|
||||
this.pos.showScreen("FloorScreen");
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -135,7 +135,7 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
this.env.pos.removeOrder(order);
|
||||
return true;
|
||||
} catch {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: "Failed to set tip",
|
||||
body: `Failed to set tip to ${order.name}. Do you want to proceed on setting the tips of the remaining?`,
|
||||
});
|
||||
|
||||
@@ -6,6 +6,8 @@ import { renderToString } from "@web/core/utils/render";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const { onMounted } = owl;
|
||||
|
||||
@@ -14,6 +16,8 @@ export class TipScreen extends PosComponent {
|
||||
static showBackToFloorButton = true;
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.state = this.currentOrder.uiState.TipScreen;
|
||||
this._totalAmount = this.currentOrder.get_total_with_tax();
|
||||
|
||||
@@ -47,7 +51,7 @@ export class TipScreen extends PosComponent {
|
||||
const serverId = this.env.pos.validated_orders_name_server_id_map[order.name];
|
||||
|
||||
if (!serverId) {
|
||||
this.showPopup(ErrorPopup, {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Unsynced order"),
|
||||
body: this.env._t(
|
||||
"This order is not yet synced to server. Make sure it is synced then try again."
|
||||
@@ -67,7 +71,7 @@ export class TipScreen extends PosComponent {
|
||||
}
|
||||
|
||||
if (amount > 0.25 * this.totalAmount) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: "Are you sure?",
|
||||
body: `${this.env.pos.format_currency(
|
||||
amount
|
||||
@@ -104,7 +108,7 @@ export class TipScreen extends PosComponent {
|
||||
this.env.pos.add_new_order();
|
||||
}
|
||||
const { name, props } = this.nextScreen;
|
||||
this.showScreen(name, props);
|
||||
this.pos.showScreen(name, props);
|
||||
}
|
||||
get nextScreen() {
|
||||
if (this.env.pos.config.module_pos_restaurant && this.env.pos.config.iface_floorplan) {
|
||||
@@ -139,7 +143,7 @@ export class TipScreen extends PosComponent {
|
||||
async _printIoT(receipt) {
|
||||
const printResult = await this.env.proxy.printer.print_receipt(receipt);
|
||||
if (!printResult.successful) {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: printResult.message.title,
|
||||
body: printResult.message.body,
|
||||
});
|
||||
@@ -151,7 +155,7 @@ export class TipScreen extends PosComponent {
|
||||
$(this.el).find(".pos-receipt-container").html(receipt);
|
||||
window.print();
|
||||
} catch {
|
||||
await this.showPopup(ErrorPopup, {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Printing is not supported on some browsers"),
|
||||
body: this.env._t(
|
||||
"Printing is not supported on some browsers due to no default printing protocol " +
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="BillScreen" owl="1">
|
||||
<t t-name="pos_restaurant.BillScreen" owl="1">
|
||||
<div class="receipt-screen screen">
|
||||
<div class="screen-content">
|
||||
<div class="top-content">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="pos-receipt-container"/>
|
||||
<div class="screen-content">
|
||||
<div class="top-content">
|
||||
<span class="button back" t-on-click="() => this.showScreen('FloorScreen')">
|
||||
<span class="button back" t-on-click="() => this.pos.showScreen('FloorScreen')">
|
||||
<i class="fa fa-angle-double-left"></i>
|
||||
<span> </span>
|
||||
<span>Back</span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Gui } from "@point_of_sale/js/Gui";
|
||||
|
||||
const { EventBus } = owl;
|
||||
|
||||
// FIXME POSREF: should probably be a service instead of a singleton.
|
||||
class SaleOrderFetcher extends EventBus {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
import { sprintf } from "web.utils";
|
||||
import { parse } from "web.field_utils";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import { useListener, useService } from "@web/core/utils/hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ControlButtonsMixin } from "@point_of_sale/js/ControlButtonsMixin";
|
||||
import { numberBuffer } from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import { saleOrderFetcher } from "@pos_sale/js/OrderManagementScreen/SaleOrderFetcher";
|
||||
import { IndependentToOrderScreen } from "@point_of_sale/js/Misc/IndependentToOrderScreen";
|
||||
import { orderManagement } from "@point_of_sale/js/PosContext";
|
||||
@@ -24,10 +23,11 @@ const { onMounted, onWillUnmount, useState } = owl;
|
||||
export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentToOrderScreen) {
|
||||
static components = { SaleOrderList, SaleOrderManagementControlPanel };
|
||||
static template = "SaleOrderManagementScreen";
|
||||
static hideOrderSelector = true;
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
useListener("close-screen", this.close);
|
||||
useListener("click-sale-order", this._onClickSaleOrder);
|
||||
useListener("next-page", this._onNextPage);
|
||||
@@ -72,7 +72,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
async _setNumpadMode(event) {
|
||||
const { mode } = event.detail;
|
||||
this.numpadMode = mode;
|
||||
numberBuffer.reset();
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
_onNextPage() {
|
||||
saleOrderFetcher.nextPage();
|
||||
@@ -87,7 +87,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
}
|
||||
async _onClickSaleOrder(event) {
|
||||
const clickedOrder = event.detail;
|
||||
const { confirmed, payload: selectedOption } = await this.showPopup(SelectionPopup, {
|
||||
const { confirmed, payload: selectedOption } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("What do you want to do?"),
|
||||
list: [
|
||||
{ id: "0", label: this.env._t("Apply a down payment"), item: false },
|
||||
@@ -116,7 +116,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
this.env._t("There was a problem in loading the %s customer."),
|
||||
sale_order.partner_id[1]
|
||||
);
|
||||
await this.showPopup(ErrorPopup, { title, body });
|
||||
await this.popup.add(ErrorPopup, { title, body });
|
||||
}
|
||||
currentPOSOrder.set_partner(
|
||||
this.env.pos.db.get_partner_by_id(sale_order.partner_id[0])
|
||||
@@ -146,7 +146,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
.filter((line) => !this.env.pos.db.get_product_by_id(line.product_id[0]))
|
||||
.map((line) => line.product_id[0]);
|
||||
if (product_to_add_in_pos.length) {
|
||||
const { confirmed } = await this.showPopup(ConfirmPopup, {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Products not available in POS"),
|
||||
body: this.env._t(
|
||||
"Some of the products in your Sale Order are not available in POS, do you want to import them?"
|
||||
@@ -202,7 +202,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
// Ask once when `useLoadedLots` is undefined, then reuse it's value on the succeeding lines.
|
||||
const { confirmed } =
|
||||
useLoadedLots === undefined
|
||||
? await this.showPopup(ConfirmPopup, {
|
||||
? await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("SN/Lots Loading"),
|
||||
body: this.env._t(
|
||||
"Do you want to load the SN/Lots linked to the Sales Order?"
|
||||
@@ -244,7 +244,9 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
this.env.pos.config.down_payment_product_id[0]
|
||||
);
|
||||
if (!down_payment_product) {
|
||||
await this.env.pos._addProducts([this.env.pos.config.down_payment_product_id[0]]);
|
||||
await this.env.pos._addProducts([
|
||||
this.env.pos.config.down_payment_product_id[0],
|
||||
]);
|
||||
down_payment_product = this.env.pos.db.get_product_by_id(
|
||||
this.env.pos.config.down_payment_product_id[0]
|
||||
);
|
||||
@@ -260,7 +262,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
down_payment = sale_order.amount_total;
|
||||
}
|
||||
|
||||
const { confirmed, payload } = await this.showPopup(NumberPopup, {
|
||||
const { confirmed, payload } = await this.popup.add(NumberPopup, {
|
||||
title: sprintf(
|
||||
this.env._t("Percentage of %s"),
|
||||
this.env.pos.format_currency(sale_order.amount_total)
|
||||
@@ -291,7 +293,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
"It seems that you didn't configure a down payment product in your point of sale.\
|
||||
You can go to your point of sale configuration to choose one."
|
||||
);
|
||||
await this.showPopup(ErrorPopup, { title, body });
|
||||
await this.popup.add(ErrorPopup, { title, body });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user