[REF] point_of_sale, *: extract navbar and remove ChromeAdapter
*: pos_hr, pos_restaurant, pos_six The Chrome component a bloated "god component" that coordinates entirely too many things which makes it hard to reason about, navigate, and debug. This commit extracts the navbar section of the chrome component into its own component, and makes the sound player into its own self-contained service. This commit also merges the ChromeAdapter component into the Chrome component, there is no reason for these to be two separate components. Part-of: odoo/odoo#108891
This commit is contained in:
@@ -125,11 +125,11 @@
|
||||
'point_of_sale/static/src/js/**/*.js',
|
||||
'web/static/lib/zxing-library/zxing-library.js',
|
||||
'point_of_sale/static/src/xml/**/*.xml',
|
||||
'point_of_sale/static/src/app/**/*',
|
||||
],
|
||||
# This bundle contains the code responsible for starting the POS UI.
|
||||
# It is practically the entry point.
|
||||
'point_of_sale.assets_backend_prod_only': [
|
||||
'point_of_sale/static/src/entry/chrome_adapter.js',
|
||||
'point_of_sale/static/src/entry/main.js',
|
||||
'web/static/src/start.js',
|
||||
'web/static/src/legacy/legacy_setup.js',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
|
||||
export class Navbar extends PosComponent {
|
||||
static template = "point_of_sale.Navbar";
|
||||
static props = {
|
||||
showCashMoveButton: Boolean,
|
||||
};
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get customerFacingDisplayButtonIsShown() {
|
||||
return this.env.pos.config.iface_customer_facing_display;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.Navbar" owl="1">
|
||||
<div class="pos-topheader">
|
||||
<div class="pos-branding">
|
||||
<img t-if="!env.isMobile" class="pos-logo" t-on-click="() => pos.toggleDebugWidget()" src="/point_of_sale/static/src/img/logo.png" alt="Logo" />
|
||||
<CashMoveButton t-if="props.showCashMoveButton" />
|
||||
<TicketButton />
|
||||
</div>
|
||||
<div class="pos-rightheader">
|
||||
<div class="status-buttons">
|
||||
<CashierName />
|
||||
<SaleDetailsButton t-if="env.proxy.printer" isHeaderButton="true"/>
|
||||
<ProxyStatus t-if="env.pos.config.use_proxy" />
|
||||
<CustomerFacingDisplayButton t-if="customerFacingDisplayButtonIsShown" />
|
||||
<SyncNotification />
|
||||
<HeaderButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,41 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { reactive, useState, toRaw } from "@odoo/owl";
|
||||
|
||||
export class PosStore {
|
||||
/** @type {'LOADING' | 'READY' | 'CLOSING'} */
|
||||
uiState = "LOADING";
|
||||
debugWidgetIsShown = true;
|
||||
hasBigScrollBars = false;
|
||||
notification = {
|
||||
isShown: false,
|
||||
message: "",
|
||||
duration: 2000,
|
||||
};
|
||||
loadingSkipButtonIsShown = false;
|
||||
// not using an arrow function here because we need the correct `this`
|
||||
toggleDebugWidget = debounce(function toggleDebugWidget() {
|
||||
this.debugWidgetIsShown = !toRaw(this).debugWidgetIsShown;
|
||||
}, 100);
|
||||
mainScreen = { name: null, component: null };
|
||||
constructor() {
|
||||
this.setup();
|
||||
}
|
||||
// to allow other modules to add things to the store.
|
||||
setup() {}
|
||||
}
|
||||
|
||||
export const posService = {
|
||||
start() {
|
||||
return reactive(new PosStore());
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("pos", posService);
|
||||
|
||||
export function usePos() {
|
||||
return useState(useService("pos"));
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component, reactive, xml } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
registry
|
||||
.category("sounds")
|
||||
.add("error", "/point_of_sale/static/src/sounds/error.wav")
|
||||
.add("bell", "/point_of_sale/static/src/sounds/bell.wav");
|
||||
|
||||
class SoundContainer extends Component {
|
||||
static template = xml`<t t-foreach="props.sounds" t-as="sound" t-key="sound">
|
||||
<audio autoplay="true" t-att-src="sound_value.src" t-on-ended="sound_value.cleanup" t-on-error="() => onError(sound_value)"/>
|
||||
</t>`;
|
||||
static props = {
|
||||
sounds: Object,
|
||||
};
|
||||
onError(sound) {
|
||||
sound.cleanup();
|
||||
const err = new Error(`Couldn't load sound at "${sound.src}"`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export const soundService = {
|
||||
start() {
|
||||
let soundId = 0;
|
||||
const sounds = reactive({});
|
||||
registry.category("main_components").add("SoundContainer", {
|
||||
Component: SoundContainer,
|
||||
props: { sounds },
|
||||
});
|
||||
return {
|
||||
play(name) {
|
||||
const id = soundId++;
|
||||
sounds[id] = {
|
||||
src: registry.category("sounds").get(name),
|
||||
cleanup: () => delete sounds[id],
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("sound", soundService);
|
||||
@@ -1,82 +0,0 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
import Chrome from "@point_of_sale/js/Chrome";
|
||||
import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { PosGlobalState } from "@point_of_sale/js/models";
|
||||
import { configureGui } from "@point_of_sale/js/Gui";
|
||||
import { registry } from "@web/core/registry";
|
||||
import env from "@point_of_sale/js/pos_env";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { batched } from "@point_of_sale/js/utils";
|
||||
|
||||
const { Component, reactive, markRaw, useExternalListener, useSubEnv, onWillUnmount, xml } = owl;
|
||||
|
||||
export class ChromeAdapter extends Component {
|
||||
setup() {
|
||||
this.PosChrome = Registries.Component.get(Chrome);
|
||||
ProductScreen.sortControlButtons();
|
||||
const legacyActionManager = useService("legacy_action_manager");
|
||||
|
||||
// Instantiate PosGlobalState here to ensure that every extension
|
||||
// (or class overloads) is taken into consideration.
|
||||
const pos = PosGlobalState.create({ env: markRaw(env) });
|
||||
|
||||
this.batchedCustomerDisplayRender = batched(() => {
|
||||
reactivePos.send_current_order_to_customer_facing_display();
|
||||
});
|
||||
const reactivePos = reactive(pos, this.batchedCustomerDisplayRender);
|
||||
env.pos = reactivePos;
|
||||
env.legacyActionManager = legacyActionManager;
|
||||
|
||||
// The proxy requires the instance of PosGlobalState to function properly.
|
||||
env.proxy.set_pos(reactivePos);
|
||||
|
||||
// TODO: Should we continue on exposing posmodel as global variable?
|
||||
// Expose only the reactive version of `pos` when in debug mode.
|
||||
window.posmodel = pos.debug ? reactivePos : pos;
|
||||
|
||||
this.env = env;
|
||||
this.__owl__.childEnv = env;
|
||||
useSubEnv({
|
||||
get isMobile() {
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
});
|
||||
let currentIsMobile = this.env.isMobile;
|
||||
const updateUI = debounce(() => {
|
||||
if (this.env.isMobile !== currentIsMobile) {
|
||||
currentIsMobile = this.env.isMobile;
|
||||
this.render(true);
|
||||
}
|
||||
}, 15);
|
||||
useExternalListener(window, "resize", updateUI);
|
||||
onWillUnmount(updateUI.cancel);
|
||||
}
|
||||
|
||||
async configureAndStart(chrome) {
|
||||
// Add the pos error handler when the chrome component is available.
|
||||
registry.category("error_handlers").add(
|
||||
"posErrorHandler",
|
||||
(env, ...noEnvArgs) => {
|
||||
if (chrome) {
|
||||
return chrome.errorHandler(this.env, ...noEnvArgs);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{ sequence: 0 }
|
||||
);
|
||||
// Little trick to avoid displaying the block ui during the POS models loading
|
||||
const BlockUiFromRegistry = registry.category("main_components").get("BlockUI");
|
||||
registry.category("main_components").remove("BlockUI");
|
||||
configureGui({ component: chrome });
|
||||
await chrome.start();
|
||||
registry.category("main_components").add("BlockUI", BlockUiFromRegistry);
|
||||
|
||||
// Subscribe to the changes in the models.
|
||||
this.batchedCustomerDisplayRender();
|
||||
}
|
||||
}
|
||||
ChromeAdapter.template = xml`<t t-component="PosChrome" setupIsDone.bind="configureAndStart"/>`;
|
||||
@@ -2,35 +2,15 @@
|
||||
|
||||
import { startWebClient } from "@web/start";
|
||||
|
||||
import { ChromeAdapter } from "@point_of_sale/entry/chrome_adapter";
|
||||
import { Chrome } from "@point_of_sale/js/Chrome";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const { Component, xml } = owl;
|
||||
|
||||
// For consistency's sake, we should trigger"WEB_CLIENT_READY" on the bus when PosApp is mounted
|
||||
// But we can't since mail and some other poll react on that cue, and we don't want those services started
|
||||
class PosApp extends Component {
|
||||
setup() {
|
||||
this.Components = registry.category("main_components").getEntries();
|
||||
}
|
||||
}
|
||||
PosApp.template = xml`
|
||||
<body>
|
||||
<ChromeAdapter />
|
||||
<div>
|
||||
<t t-foreach="Components" t-as="C" t-key="C[0]">
|
||||
<t t-component="C[1].Component" t-props="C[1].props"/>
|
||||
</t>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
PosApp.components = { ChromeAdapter };
|
||||
|
||||
function startPosApp() {
|
||||
Registries.Component.freeze();
|
||||
Registries.Model.freeze();
|
||||
startWebClient(PosApp);
|
||||
// For consistency's sake, we should trigger"WEB_CLIENT_READY" on the bus when PosApp is mounted
|
||||
// But we can't since mail and some other poll react on that cue, and we don't want those services started
|
||||
startWebClient(Registries.Component.get(Chrome));
|
||||
}
|
||||
|
||||
startPosApp();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { loadCSS } from "@web/core/assets";
|
||||
import { useListener, useBus } from "@web/core/utils/hooks";
|
||||
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";
|
||||
@@ -16,33 +16,87 @@ import {
|
||||
} from "@web/core/network/rpc_service";
|
||||
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 { Navbar } from "@point_of_sale/app/navbar/navbar";
|
||||
|
||||
const {
|
||||
onError,
|
||||
// ChromeAdapter imports
|
||||
import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { PosGlobalState } from "@point_of_sale/js/models";
|
||||
import { configureGui } from "@point_of_sale/js/Gui";
|
||||
import { registry } from "@web/core/registry";
|
||||
import env from "@point_of_sale/js/pos_env";
|
||||
|
||||
import {
|
||||
onMounted,
|
||||
onWillDestroy,
|
||||
useExternalListener,
|
||||
useRef,
|
||||
useState,
|
||||
useSubEnv,
|
||||
reactive,
|
||||
} = owl;
|
||||
markRaw,
|
||||
onWillUnmount,
|
||||
} from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
|
||||
/**
|
||||
* Chrome is the root component of the PoS App.
|
||||
*/
|
||||
class Chrome extends PosComponent {
|
||||
export class Chrome extends PosComponent {
|
||||
setup() {
|
||||
// BEGIN ChromeAdapter
|
||||
ProductScreen.sortControlButtons();
|
||||
const legacyActionManager = useService("legacy_action_manager");
|
||||
|
||||
// Instantiate PosGlobalState here to ensure that every extension
|
||||
// (or class overloads) is taken into consideration.
|
||||
const pos = PosGlobalState.create({ env: markRaw(env) });
|
||||
|
||||
this.batchedCustomerDisplayRender = batched(() => {
|
||||
reactivePos.send_current_order_to_customer_facing_display();
|
||||
});
|
||||
const reactivePos = reactive(pos, this.batchedCustomerDisplayRender);
|
||||
env.pos = reactivePos;
|
||||
env.legacyActionManager = legacyActionManager;
|
||||
|
||||
// The proxy requires the instance of PosGlobalState to function properly.
|
||||
env.proxy.set_pos(reactivePos);
|
||||
|
||||
// TODO: Should we continue on exposing posmodel as global variable?
|
||||
// Expose only the reactive version of `pos` when in debug mode.
|
||||
window.posmodel = pos.debug ? reactivePos : pos;
|
||||
|
||||
this.wowlEnv = this.env;
|
||||
env.services.pos = this.wowlEnv.services.pos;
|
||||
env.services.sound = this.wowlEnv.services.sound;
|
||||
window.sound = env.services.sound;
|
||||
this.env = env;
|
||||
this.__owl__.childEnv = env;
|
||||
useSubEnv({
|
||||
get isMobile() {
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
});
|
||||
let currentIsMobile = this.env.isMobile;
|
||||
const updateUI = debounce(() => {
|
||||
if (this.env.isMobile !== currentIsMobile) {
|
||||
currentIsMobile = this.env.isMobile;
|
||||
this.render(true);
|
||||
}
|
||||
}, 15); // FIXME POSREF use throttleForAnimation?
|
||||
useExternalListener(window, "resize", updateUI);
|
||||
onWillUnmount(updateUI.cancel);
|
||||
// END ChromeAdapter
|
||||
|
||||
super.setup();
|
||||
useExternalListener(window, "beforeunload", this._onBeforeUnload);
|
||||
useListener("show-main-screen", this.__showScreen);
|
||||
useListener("toggle-debug-widget", debounce(this._toggleDebugWidget, 100));
|
||||
useListener("toggle-mobile-searchbar", this._toggleMobileSearchBar);
|
||||
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("play-sound", this._onPlaySound);
|
||||
const sound = useService("sound");
|
||||
useListener("play-sound", ({ detail: name }) => sound.play(name));
|
||||
useListener("set-sync-status", this._onSetSyncStatus);
|
||||
useListener("show-notification", this._onShowNotification);
|
||||
useListener("close-notification", this._onCloseNotification);
|
||||
@@ -50,38 +104,24 @@ class Chrome extends PosComponent {
|
||||
useBus(this.env.posbus, "start-cash-control", this.openCashControl);
|
||||
NumberBuffer.activate();
|
||||
|
||||
this.state = useState({
|
||||
uiState: "LOADING", // 'LOADING' | 'READY' | 'CLOSING'
|
||||
debugWidgetIsShown: true,
|
||||
mobileSearchBarIsShown: false,
|
||||
hasBigScrollBars: false,
|
||||
sound: { src: null },
|
||||
notification: {
|
||||
isShown: false,
|
||||
message: "",
|
||||
duration: 2000,
|
||||
},
|
||||
loadingSkipButtonIsShown: false,
|
||||
});
|
||||
this.state = usePos();
|
||||
|
||||
this.mainScreen = useState({ name: null, component: null });
|
||||
this.mainScreen = this.state.mainScreen;
|
||||
this.mainScreenProps = {};
|
||||
|
||||
this.tempScreen = useState({ isShown: false, name: null, component: null });
|
||||
this.tempScreenProps = {};
|
||||
|
||||
this.progressbar = useRef("progressbar");
|
||||
|
||||
this.previous_touch_y_coordinate = -1;
|
||||
|
||||
const pos = reactive(
|
||||
this.env.pos,
|
||||
batched(() => this.render(true))
|
||||
);
|
||||
useSubEnv({ pos });
|
||||
useSubEnv({
|
||||
pos: reactive(
|
||||
this.env.pos,
|
||||
batched(() => this.render(true)) // FIXME POSREF remove render(true)
|
||||
),
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// remove default webclient handlers that induce click delay
|
||||
// FIXME POSREF if these handlers shouldn't be there we should not load the files that add them.
|
||||
$(document).off();
|
||||
$(window).off();
|
||||
$("html").off();
|
||||
@@ -89,32 +129,19 @@ class Chrome extends PosComponent {
|
||||
});
|
||||
|
||||
onWillDestroy(() => {
|
||||
this.env.pos.destroy();
|
||||
try {
|
||||
// FIXME POSREF is this needed?
|
||||
this.env.pos.destroy();
|
||||
} catch {
|
||||
// throwing here causes loop
|
||||
}
|
||||
});
|
||||
|
||||
onError((error) => {
|
||||
// error is an OwlError object.
|
||||
console.error(error.cause);
|
||||
});
|
||||
|
||||
this.props.setupIsDone(this);
|
||||
this.start();
|
||||
}
|
||||
|
||||
// GETTERS //
|
||||
|
||||
get customerFacingDisplayButtonIsShown() {
|
||||
return this.env.pos.config.iface_customer_facing_display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to give the `state.mobileSearchBarIsShown` value to main screen props
|
||||
*/
|
||||
get mainScreenPropsFielded() {
|
||||
return Object.assign({}, this.mainScreenProps, {
|
||||
mobileSearchBarIsShown: this.state.mobileSearchBarIsShown,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Startup screen can be based on pos config so the startup screen
|
||||
* is only determined after pos data is completely loaded.
|
||||
@@ -137,6 +164,18 @@ class Chrome extends PosComponent {
|
||||
* This will load pos and assign it to the environment.
|
||||
*/
|
||||
async start() {
|
||||
registry.category("error_handlers").add(
|
||||
"posErrorHandler",
|
||||
(env, ...noEnvArgs) => {
|
||||
return this.errorHandler(this.env, ...noEnvArgs);
|
||||
},
|
||||
{ sequence: 0 }
|
||||
);
|
||||
// Little trick to avoid displaying the block ui during the POS models loading
|
||||
const BlockUiFromRegistry = registry.category("main_components").get("BlockUI");
|
||||
registry.category("main_components").remove("BlockUI");
|
||||
configureGui({ component: this }); // FIXME POSREF
|
||||
|
||||
try {
|
||||
await this.env.pos.load_server_data();
|
||||
await this.setupBarcodeParser();
|
||||
@@ -186,6 +225,10 @@ class Chrome extends PosComponent {
|
||||
exitButtonIsShown: true,
|
||||
});
|
||||
}
|
||||
registry.category("main_components").add("BlockUI", BlockUiFromRegistry);
|
||||
|
||||
// Subscribe to the changes in the models.
|
||||
this.batchedCustomerDisplayRender();
|
||||
}
|
||||
|
||||
_runBackgroundTasks() {
|
||||
@@ -228,7 +271,7 @@ class Chrome extends PosComponent {
|
||||
this.env.proxy
|
||||
.autoconnect({
|
||||
force_ip: this.env.pos.config.proxy_ip || undefined,
|
||||
progress: function (prog) {},
|
||||
progress: function (prog) { },
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
@@ -340,19 +383,19 @@ class Chrome extends PosComponent {
|
||||
console.warn(error);
|
||||
const reason = this.env.pos.failed
|
||||
? this.env._t(
|
||||
"Some orders could not be submitted to " +
|
||||
"the server due to configuration errors. " +
|
||||
"You can exit the Point of Sale, but do " +
|
||||
"not close the session before the issue " +
|
||||
"has been resolved."
|
||||
)
|
||||
"Some orders could not be submitted to " +
|
||||
"the server due to configuration errors. " +
|
||||
"You can exit the Point of Sale, but do " +
|
||||
"not close the session before the issue " +
|
||||
"has been resolved."
|
||||
)
|
||||
: this.env._t(
|
||||
"Some orders could not be submitted to " +
|
||||
"the server due to internet connection issues. " +
|
||||
"You can exit the Point of Sale, but do " +
|
||||
"not close the session before the issue " +
|
||||
"has been resolved."
|
||||
);
|
||||
"Some orders could not be submitted to " +
|
||||
"the server due to internet connection issues. " +
|
||||
"You can exit the Point of Sale, but do " +
|
||||
"not close the session before the issue " +
|
||||
"has been resolved."
|
||||
);
|
||||
const { confirmed } = await this.showPopup("ConfirmPopup", {
|
||||
title: this.env._t("Offline Orders"),
|
||||
body: reason,
|
||||
@@ -365,25 +408,6 @@ class Chrome extends PosComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
_toggleDebugWidget() {
|
||||
this.state.debugWidgetIsShown = !this.state.debugWidgetIsShown;
|
||||
}
|
||||
_toggleMobileSearchBar({ detail: isSearchBarEnabled }) {
|
||||
if (isSearchBarEnabled !== null) {
|
||||
this.state.mobileSearchBarIsShown = isSearchBarEnabled;
|
||||
} else {
|
||||
this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown;
|
||||
}
|
||||
}
|
||||
_onPlaySound({ detail: name }) {
|
||||
let src;
|
||||
if (name === "error") {
|
||||
src = "/point_of_sale/static/src/sounds/error.wav";
|
||||
} else if (name === "bell") {
|
||||
src = "/point_of_sale/static/src/sounds/bell.wav";
|
||||
}
|
||||
this.state.sound.src = src;
|
||||
}
|
||||
_onSetSyncStatus({ detail: { status, pending } }) {
|
||||
this.env.pos.synch.status = status;
|
||||
this.env.pos.synch.pending = pending;
|
||||
@@ -404,10 +428,6 @@ class Chrome extends PosComponent {
|
||||
this.env.pos.db.save("TO_REFUND_LINES", this.env.pos.toRefundLines);
|
||||
}
|
||||
|
||||
get isTicketScreenShown() {
|
||||
return this.mainScreen.name === "TicketScreen";
|
||||
}
|
||||
|
||||
// MISC METHODS //
|
||||
_preloadImages() {
|
||||
for (const product of this.env.pos.db.get_product_by_category(0)) {
|
||||
@@ -474,7 +494,7 @@ class Chrome extends PosComponent {
|
||||
false
|
||||
);
|
||||
}
|
||||
showCashMoveButton() {
|
||||
get showCashMoveButton() {
|
||||
return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control;
|
||||
}
|
||||
|
||||
@@ -550,7 +570,15 @@ class Chrome extends PosComponent {
|
||||
Chrome.template = "Chrome";
|
||||
Object.defineProperty(Chrome, "components", {
|
||||
get() {
|
||||
return Object.assign({ Transition }, PosComponent.components);
|
||||
return Object.assign(
|
||||
{
|
||||
Transition,
|
||||
MainComponentsContainer,
|
||||
WithEnv,
|
||||
Navbar,
|
||||
},
|
||||
PosComponent.components
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@ import { parse } from "web.field_utils";
|
||||
import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer";
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
|
||||
const { onMounted, onWillUnmount, useRef, useState } = owl;
|
||||
|
||||
class DebugWidget extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.state = useState({
|
||||
barcodeInput: "",
|
||||
weightInput: "",
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
|
||||
class TicketButton extends PosComponent {
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
onClick() {
|
||||
if (this.props.isTicketScreenShown) {
|
||||
if (this.isTicketScreenShown) {
|
||||
this.env.posbus.trigger("ticket-button-clicked");
|
||||
} else {
|
||||
this.showScreen("TicketScreen");
|
||||
}
|
||||
}
|
||||
get isTicketScreenShown() {
|
||||
return this.pos.mainScreen.name === "TicketScreen";
|
||||
}
|
||||
get count() {
|
||||
if (this.env.pos) {
|
||||
return this.env.pos.get_order_list().length;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
|
||||
class NotificationSound extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
useListener("ended", () => (this.props.sound.src = null));
|
||||
}
|
||||
}
|
||||
NotificationSound.template = "NotificationSound";
|
||||
|
||||
Registries.Component.add(NotificationSound);
|
||||
|
||||
export default NotificationSound;
|
||||
@@ -1,18 +1,10 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { LegacyComponent } from "@web/legacy/legacy_component";
|
||||
const { onRendered } = owl;
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
class PosComponent extends LegacyComponent {
|
||||
setup() {
|
||||
onRendered(() => {
|
||||
if (this.env.isDebug()) {
|
||||
console.log("Rendered:", this.constructor.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* This function is available to all Components that inherit this class.
|
||||
* The goal of this function is to show an awaitable dialog (popup) that
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import { useListener } from "@web/core/utils/hooks";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
|
||||
const { onWillUnmount, useState } = owl;
|
||||
const { useState } = owl;
|
||||
|
||||
class ProductsWidget extends PosComponent {
|
||||
/**
|
||||
@@ -18,10 +19,7 @@ class ProductsWidget extends PosComponent {
|
||||
useListener("clear-search", this._clearSearch);
|
||||
useListener("update-product-list", this._updateProductList);
|
||||
this.state = useState({ searchWord: "" });
|
||||
onWillUnmount(this.onWillUnmount);
|
||||
}
|
||||
onWillUnmount() {
|
||||
this.trigger("toggle-mobile-searchbar", false);
|
||||
this.pos = usePos();
|
||||
}
|
||||
get selectedCategoryId() {
|
||||
return this.env.pos.selectedCategoryId;
|
||||
|
||||
+7
-5
@@ -5,14 +5,16 @@ import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/r
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { debounce } from "@web/core/utils/timing";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
|
||||
const { onMounted, onWillUnmount } = owl;
|
||||
import { onMounted, onWillUnmount, useState } from "@odoo/owl";
|
||||
|
||||
class ProductsWidgetControlPanel extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.updateSearch = debounce(this.updateSearch, 100);
|
||||
this.state = { searchInput: "" };
|
||||
this.state = useState({ searchInput: "", mobileSearchBarIsShown: false });
|
||||
|
||||
onMounted(() => {
|
||||
this.env.posbus.on("search-product-from-info-popup", this, this.searchProductFromInfo);
|
||||
@@ -25,6 +27,9 @@ class ProductsWidgetControlPanel extends PosComponent {
|
||||
this.env.posbus.off("search-product-from-info-popup", this);
|
||||
});
|
||||
}
|
||||
toggleMobileSearchBar() {
|
||||
this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown;
|
||||
}
|
||||
_clearSearch() {
|
||||
this.state.searchInput = "";
|
||||
this.trigger("clear-search");
|
||||
@@ -65,9 +70,6 @@ class ProductsWidgetControlPanel extends PosComponent {
|
||||
this.trigger("switch-category", 0);
|
||||
this.trigger("update-search", productName);
|
||||
}
|
||||
_toggleMobileSearchbar() {
|
||||
this.trigger("toggle-mobile-searchbar");
|
||||
}
|
||||
async loadProductFromDB() {
|
||||
if (!this.state.searchInput) {
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useErrorHandlers } from "@point_of_sale/js/custom_hooks";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import AbstractReceiptScreen from "@point_of_sale/js/Misc/AbstractReceiptScreen";
|
||||
|
||||
const { onMounted, useRef, status } = owl;
|
||||
import { onMounted, useRef, status } from "@odoo/owl";
|
||||
|
||||
const ReceiptScreen = (AbstractReceiptScreen) => {
|
||||
class ReceiptScreen extends AbstractReceiptScreen {
|
||||
|
||||
@@ -4,33 +4,16 @@
|
||||
<t t-name="Chrome" owl="1">
|
||||
<div class="pos" t-att-class="{ 'big-scrollbars': state.hasBigScrollBars }">
|
||||
<div class="pos-receipt-print"></div>
|
||||
<div class="pos-topheader" t-att-class="{ oe_hidden: state.uiState !== 'READY' }">
|
||||
<div t-if="tempScreen.isShown" class="block-top-header" />
|
||||
<div class="pos-branding">
|
||||
<img t-if="!env.isMobile" class="pos-logo" t-on-click="() => this.trigger('toggle-debug-widget')"
|
||||
src="/point_of_sale/static/src/img/logo.png" alt="Logo" />
|
||||
<CashMoveButton t-if="showCashMoveButton()" />
|
||||
<TicketButton isTicketScreenShown="isTicketScreenShown" />
|
||||
</div>
|
||||
<div class="pos-rightheader">
|
||||
<div class="status-buttons" t-if="state.uiState === 'READY'">
|
||||
<CashierName />
|
||||
<SaleDetailsButton t-if="env.proxy.printer" isHeaderButton="true"/>
|
||||
<ProxyStatus t-if="env.pos.config.use_proxy" />
|
||||
<CustomerFacingDisplayButton t-if="customerFacingDisplayButtonIsShown" />
|
||||
<SyncNotification />
|
||||
<HeaderButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="tempScreen.isShown" class="block-top-header" />
|
||||
<t t-if="state.uiState === 'READY'">
|
||||
<Navbar showCashMoveButton="showCashMoveButton"/>
|
||||
<div class="pos-content">
|
||||
<div class="window">
|
||||
<div class="subwindow">
|
||||
<div class="subwindow-container">
|
||||
<div class="subwindow-container-fix screens">
|
||||
<t isShown="!tempScreen.isShown" t-component="mainScreen.component"
|
||||
t-props="mainScreenPropsFielded" t-key="mainScreen.name" />
|
||||
t-props="mainScreenProps" t-key="mainScreen.name" />
|
||||
<t t-if="tempScreen.isShown" t-component="tempScreen.component"
|
||||
t-props="tempScreenProps" t-key="tempScreen.name" />
|
||||
</div>
|
||||
@@ -65,8 +48,10 @@
|
||||
<Notification message="state.notification.message" duration="state.notification.duration" className="transition.className"/>
|
||||
</Transition>
|
||||
|
||||
<NotificationSound t-if="state.sound.src" sound="state.sound" />
|
||||
</div>
|
||||
<WithEnv env="wowlEnv">
|
||||
<MainComponentsContainer/>
|
||||
</WithEnv>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<header class="drag-handle">
|
||||
<h1>Debug Window</h1>
|
||||
</header>
|
||||
<div class="toggle" t-on-click="() => this.trigger('toggle-debug-widget')" title="Dismiss"
|
||||
<div class="toggle" t-on-click="() => pos.toggleDebugWidget()" title="Dismiss"
|
||||
role="img" aria-label="Dismiss"><i class="fa fa-times" /></div>
|
||||
<div class="content">
|
||||
<p class="category">Electronic Scale</p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="TicketButton" owl="1">
|
||||
<div class="ticket-button" t-att-class="{ highlight: props.isTicketScreenShown }" t-on-click="onClick">
|
||||
<div class="ticket-button" t-att-class="{ highlight: isTicketScreenShown }" t-on-click="onClick">
|
||||
<div class="with-badge" t-att-badge="count">
|
||||
<i class="fa fa-ticket" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="NotificationSound" owl="1">
|
||||
<audio t-att-src="props.sound.src" autoplay="true"></audio>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightpane" t-if="!env.isMobile || state.mobile_pane === 'right'">
|
||||
<ProductsWidget mobileSearchBarIsShown="props.mobileSearchBarIsShown"/>
|
||||
<ProductsWidget/>
|
||||
<MobileOrderWidget t-if="env.isMobile" pane="state.mobile_pane" onSwitchPane.bind="switchPane"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
<t t-name="ProductsWidget" owl="1">
|
||||
<div class="products-widget">
|
||||
<ProductsWidgetControlPanel mobileSearchBarIsShown="props.mobileSearchBarIsShown" breadcrumbs="breadcrumbs"
|
||||
subcategories="subcategories" hasNoCategories="hasNoCategories" />
|
||||
<ProductsWidgetControlPanel breadcrumbs="breadcrumbs" subcategories="subcategories" hasNoCategories="hasNoCategories" />
|
||||
<div class="product-list-container">
|
||||
<div t-if="productsToDisplay.length != 0" class="product-list">
|
||||
<t t-foreach="productsToDisplay" t-as="product" t-key="product.id">
|
||||
|
||||
+4
-4
@@ -5,13 +5,13 @@
|
||||
<div class="products-widget-control">
|
||||
<div class="rightpane-header">
|
||||
<div class="search-bar-container sb-product">
|
||||
<span t-if="env.isMobile" class="oe_status oe_icon icon" t-on-click="_toggleMobileSearchbar">
|
||||
<i t-attf-class="fa {{props.mobileSearchBarIsShown ? 'fa-arrow-left' : 'fa-search'}}"/>
|
||||
<span t-if="env.isMobile" class="oe_status oe_icon icon" t-on-click="toggleMobileSearchBar">
|
||||
<i t-attf-class="fa {{state.mobileSearchBarIsShown ? 'fa-arrow-left' : 'fa-search'}}"/>
|
||||
</span>
|
||||
<t t-if="!env.isMobile || props.mobileSearchBarIsShown">
|
||||
<t t-if="!env.isMobile || state.mobileSearchBarIsShown">
|
||||
<div class="pos-search-bar">
|
||||
<i class="fa fa-search" t-on-click="_onPressEnterKey"/>
|
||||
<input t-model="state.searchInput" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
|
||||
<input t-model="this.state.searchInput" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
|
||||
<i t-if="state.searchInput" class="fa fa-times" t-on-click="_clearSearch"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -32,6 +32,7 @@ The actual till still requires one user but an unlimited number of employees can
|
||||
'pos_hr/static/src/js/LoginScreen.js',
|
||||
'pos_hr/static/src/js/PaymentScreen.js',
|
||||
'pos_hr/static/src/xml/**/*',
|
||||
'pos_hr/static/src/app/**/*',
|
||||
],
|
||||
'web.assets_tests': [
|
||||
'pos_hr/static/tests/**/*',
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_hr.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//HeaderButton" position="replace">
|
||||
<HeaderLockButton t-if="env.pos.config.module_pos_hr" />
|
||||
<HeaderButton t-if="!env.pos.config.module_pos_hr || env.pos.get_cashier().role === 'manager' || env.pos.get_cashier_user_id() === env.pos.user.id" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -11,16 +11,9 @@ const PosHrChrome = (Chrome) =>
|
||||
this.showTempScreen("LoginScreen");
|
||||
}
|
||||
}
|
||||
get headerButtonIsShown() {
|
||||
get showCashMoveButton() {
|
||||
return (
|
||||
!this.env.pos.config.module_pos_hr ||
|
||||
this.env.pos.get_cashier().role == "manager" ||
|
||||
this.env.pos.get_cashier_user_id() === this.env.pos.user.id
|
||||
);
|
||||
}
|
||||
showCashMoveButton() {
|
||||
return (
|
||||
super.showCashMoveButton() &&
|
||||
super.showCashMoveButton &&
|
||||
(!this.env.pos.cashier || this.env.pos.cashier.role == "manager")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="Chrome" t-inherit="point_of_sale.Chrome" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//HeaderButton" position="replace">
|
||||
<HeaderLockButton t-if="env.pos.config.module_pos_hr" />
|
||||
<HeaderButton t-if="headerButtonIsShown" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -34,6 +34,7 @@ This module adds several features to the Point of Sale that are specific to rest
|
||||
'pos_restaurant/static/src/js/**/*.js',
|
||||
('after', 'point_of_sale/static/src/scss/pos.scss', 'pos_restaurant/static/src/scss/restaurant.scss'),
|
||||
'pos_restaurant/static/src/xml/**/*',
|
||||
'pos_restaurant/static/src/app/**/*',
|
||||
],
|
||||
'web.assets_backend': [
|
||||
'point_of_sale/static/src/scss/pos_dashboard.scss',
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="Chrome" t-inherit="point_of_sale.Chrome" t-inherit-mode="extension" owl="1">
|
||||
<t t-name="pos_restaurant.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('status-buttons')]" position="before">
|
||||
<div class="back-to-floor-portal"/>
|
||||
<BackToFloorButton t-if="pos.mainScreen.component.showBackToFloorButton"/>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import PosComponent from "@point_of_sale/js/PosComponent";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
|
||||
/**
|
||||
* Props: {
|
||||
* onClick: callback
|
||||
* }
|
||||
*/
|
||||
class BackToFloorButton extends PosComponent {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
}
|
||||
get table() {
|
||||
return this.env.pos.table;
|
||||
}
|
||||
@@ -19,9 +19,6 @@ class BackToFloorButton extends PosComponent {
|
||||
return this.table != null;
|
||||
}
|
||||
backToFloorScreen() {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick();
|
||||
}
|
||||
this.showScreen("FloorScreen", { floor: this.floor });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const PosResTicketButton = (TicketButton) =>
|
||||
async onClick() {
|
||||
if (
|
||||
this.env.pos.config.iface_floorplan &&
|
||||
!this.props.isTicketScreenShown &&
|
||||
!this.isTicketScreenShown &&
|
||||
!this.env.pos.table
|
||||
) {
|
||||
try {
|
||||
|
||||
@@ -6,6 +6,7 @@ import Registries from "@point_of_sale/js/Registries";
|
||||
|
||||
const PosResPaymentScreen = (PaymentScreen) =>
|
||||
class extends PaymentScreen {
|
||||
static showBackToFloorButton = true;
|
||||
setup() {
|
||||
super.setup();
|
||||
useListener("send-payment-adjust", this._sendPaymentAdjust);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(ProductScreen, "pos_restaurant.ProductScreen", {
|
||||
showBackToFloorButton: true,
|
||||
});
|
||||
@@ -2,9 +2,22 @@
|
||||
|
||||
import ReceiptScreen from "@point_of_sale/js/Screens/ReceiptScreen/ReceiptScreen";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
import { usePos } from "@point_of_sale/app/pos_store";
|
||||
import { onWillUnmount } from "@odoo/owl";
|
||||
|
||||
const PosResReceiptScreen = (ReceiptScreen) =>
|
||||
class extends ReceiptScreen {
|
||||
static showBackToFloorButton = true;
|
||||
setup() {
|
||||
super.setup();
|
||||
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") {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
}
|
||||
});
|
||||
}
|
||||
//@override
|
||||
_addNewOrder() {
|
||||
if (!this.env.pos.config.iface_floorplan) {
|
||||
@@ -20,10 +33,6 @@ const PosResReceiptScreen = (ReceiptScreen) =>
|
||||
return super.nextScreen;
|
||||
}
|
||||
}
|
||||
onBackToFloorButtonClick() {
|
||||
// If we're here and the order is paid, we can remove it from the orders
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(ReceiptScreen, PosResReceiptScreen);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { renderToString } from "@web/core/utils/render";
|
||||
const { onMounted } = owl;
|
||||
|
||||
class TipScreen extends PosComponent {
|
||||
static showBackToFloorButton = true;
|
||||
setup() {
|
||||
super.setup();
|
||||
this.state = this.currentOrder.uiState.TipScreen;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<span
|
||||
t-if="hasTable"
|
||||
class="order-button floor-button"
|
||||
t-att-class="{ oe_hidden: props.mobileSearchBarIsShown }"
|
||||
t-on-click="backToFloorScreen"
|
||||
>
|
||||
<i class="fa fa-angle-double-left" role="img" aria-label="Back to floor" title="Back to floor" />
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="PaymentScreen" t-inherit="point_of_sale.PaymentScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('payment-screen')]" position="inside">
|
||||
<t t-portal="'.pos .back-to-floor-portal'" position="before">
|
||||
<BackToFloorButton mobileSearchBarIsShown="props.mobileSearchBarIsShown"/>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('button') and hasclass('next')]" position="attributes">
|
||||
<attribute name="t-att-hidden">env.pos.config.set_tip_after_payment and !currentOrder.is_paid()</attribute>
|
||||
</xpath>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="ProductScreen" t-inherit="point_of_sale.ProductScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('product-screen')]" position="inside">
|
||||
<t t-portal="'.pos .back-to-floor-portal'" position="before">
|
||||
<BackToFloorButton mobileSearchBarIsShown="props.mobileSearchBarIsShown"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="ReceiptScreen" t-inherit="point_of_sale.ReceiptScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('receipt-screen')]" position="inside">
|
||||
<t t-portal="'.pos .back-to-floor-portal'" position="before">
|
||||
<BackToFloorButton mobileSearchBarIsShown="props.mobileSearchBarIsShown"
|
||||
onClick.bind="onBackToFloorButtonClick"
|
||||
/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -55,9 +55,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-portal="'.pos .back-to-floor-portal'" position="before">
|
||||
<BackToFloorButton mobileSearchBarIsShown="props.mobileSearchBarIsShown"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
'pos_six/static/src/js/models.js',
|
||||
'pos_six/static/src/js/payment_six.js',
|
||||
'pos_six/static/src/xml/**/*',
|
||||
'pos_six/static/src/app/**/*',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_six.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//SyncNotification" position="after">
|
||||
<BalanceButton t-if="this.env.pos.payment_methods.some((pm) => pm.use_payment_terminal === 'six')" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,15 +0,0 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import Chrome from "@point_of_sale/js/Chrome";
|
||||
import Registries from "@point_of_sale/js/Registries";
|
||||
|
||||
const PosSixChrome = (Chrome) =>
|
||||
class extends Chrome {
|
||||
get balanceButtonIsShown() {
|
||||
return this.env.pos.payment_methods.some((pm) => pm.use_payment_terminal === "six");
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(Chrome, PosSixChrome);
|
||||
|
||||
export default Chrome;
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="Chrome" t-inherit="point_of_sale.Chrome" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//SyncNotification" position="after">
|
||||
<BalanceButton t-if="balanceButtonIsShown" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2022
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
||||
Reference in New Issue
Block a user