[REF] pos*: merge PosStore and PosGlobalState

*: l10n_ae_pos, l10n_co_pos, l10n_fr_pos_cert, l10n_gcc_pos, l10n_in_pos, point_of_sale,
pos_adyen, pos_discount, pos_epson_printer, pos_hr, pos_loyalty, pos_mercury,
pos_restaurant, pos_sale, pos_sale_product_configurator, pos_six, pos_stripe

After the architectural refactor of the PoS App, there are two classes that make sense to merge.
In this refactoring task, we're merging them.

closes odoo/odoo#124477

Task: 3358550
Related: odoo/enterprise#42257
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
vlst
2023-06-19 10:40:43 +02:00
parent eb0934c9b5
commit 1f655ffcc7
132 changed files with 2557 additions and 2600 deletions
@@ -3,7 +3,7 @@
<t t-name="l10n_ae_pos.OrderReceiptVAT" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//div[@id='post_receipt_taxes_container']" position="replace">
<div id='post_receipt_taxes_container'>
<t t-if="pos.globalState.company.country.code == 'AE'">VAT</t>
<t t-if="pos.company.country.code == 'AE'">VAT</t>
<t t-else="">Total Taxes</t>
<span t-esc="env.utils.formatCurrency(receipt.total_tax)" class="pos-receipt-right-align"/>
</div>
@@ -7,7 +7,7 @@ patch(PaymentScreen.prototype, "l10n_co_pos.PaymentScreen", {
async _postPushOrderResolve(order, order_server_ids) {
const _super = this._super;
try {
if (this.pos.globalState.is_colombian_country()) {
if (this.pos.is_colombian_country()) {
const result = await this.orm.searchRead(
"pos.order",
[["id", "in", order_server_ids]],
+3 -2
View File
@@ -1,9 +1,10 @@
/** @odoo-module */
import { PosGlobalState, Order } from "@point_of_sale/app/store/models";
import { PosStore } from "@point_of_sale/app/store/pos_store";
import { Order } from "@point_of_sale/app/store/models";
import { patch } from "@web/core/utils/patch";
patch(PosGlobalState.prototype, "l10n_co_pos.PosGlobalState", {
patch(PosStore.prototype, "l10n_co_pos.PosStore", {
is_colombian_country() {
return this.company.country.code === "CO";
},
@@ -9,13 +9,12 @@ patch(Chrome.prototype, "l10n_fr_pos_cert.Chrome", {
setup() {
this._super(...arguments);
onMounted(async () => {
const { globalState } = this.pos;
if (globalState.is_french_country() && globalState.pos_session.start_at) {
if (this.pos.is_french_country() && this.pos.pos_session.start_at) {
const now = Date.now();
const limitDate = new Date(globalState.pos_session.start_at);
const limitDate = new Date(this.pos.pos_session.start_at);
limitDate.setDate(limitDate.getDate() + 1);
if (limitDate.getTime() < now) {
const info = await globalState.getClosePosInfo();
const info = await this.pos.getClosePosInfo();
this.popup.add(ClosePosPopup, { info });
}
}
@@ -7,7 +7,7 @@ patch(PaymentScreen.prototype, "l10n_fr_pos_cert.PaymentScreen", {
async _postPushOrderResolve(order, order_server_ids) {
const _super = this._super;
try {
if (this.pos.globalState.is_french_country()) {
if (this.pos.is_french_country()) {
const result = await this.orm.searchRead(
"pos.order",
[["id", "in", order_server_ids]],
@@ -6,9 +6,9 @@ import { patch } from "@web/core/utils/patch";
patch(ClosePosPopup.prototype, "l10n_fr_pos_cert.ClosePosPopup", {
sessionIsOutdated() {
let isOutdated = false;
if (this.pos.globalState.is_french_country() && this.pos.globalState.pos_session.start_at) {
if (this.pos.is_french_country() && this.pos.pos_session.start_at) {
const now = Date.now();
const limitDate = new Date(this.pos.globalState.pos_session.start_at);
const limitDate = new Date(this.pos.pos_session.start_at);
limitDate.setDate(limitDate.getDate() + 1);
isOutdated = limitDate < now;
}
+3 -2
View File
@@ -1,12 +1,13 @@
/** @odoo-module */
import { PosGlobalState, Order, Orderline } from "@point_of_sale/app/store/models";
import { PosStore } from "@point_of_sale/app/store/pos_store";
import { Order, Orderline } from "@point_of_sale/app/store/models";
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { sprintf } from "@web/core/utils/strings";
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
patch(PosGlobalState.prototype, "l10n_fr_pos_cert.PosGlobalState", {
patch(PosStore.prototype, "l10n_fr_pos_cert.PosStore", {
is_french_country() {
var french_countries = ["FR", "MF", "MQ", "NC", "PF", "RE", "GF", "GP", "TF"];
if (!this.company.country) {
@@ -3,7 +3,7 @@
<t t-name="l10n_fr_pos_cert.Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
<xpath expr="//ul[hasclass('info-list')]" position="inside">
<t t-if="pos.globalState.is_french_country() !== false and props.line.price_type === 'manual'">
<t t-if="pos.is_french_country() !== false and props.line.price_manually_set === 'manual'">
<li class="info">
Old unit price:
<span class="oldPrice">
@@ -114,7 +114,7 @@
<xpath expr="//t[@t-esc='line.unit_name']/.." position="after">
<t t-if="receipt.is_gcc_country">
<div class="pos-receipt-left-padding">
<t t-esc="Math.round(line.quantity * Math.pow(10, pos.globalState.dp['Product Unit of Measure'])) / Math.pow(10, pos.globalState.dp['Product Unit of Measure'])"/>
<t t-esc="Math.round(line.quantity * Math.pow(10, pos.dp['Product Unit of Measure'])) / Math.pow(10, pos.dp['Product Unit of Measure'])"/>
<t t-if="!line.is_in_unit" t-esc="line.unit_name" />
x
<t t-esc="env.utils.formatCurrency(line.price_display_one, false)" />
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="l10n_in_pos.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('orderlines')]" position="before">
<t t-if="receipt.partner and pos.globalState.company.country and pos.globalState.company.country.code == 'IN'">
<t t-if="receipt.partner and pos.company.country and pos.company.country.code == 'IN'">
<div class="pos-receipt-center-align">
<div><t t-esc="receipt.partner.name" /></div>
<t t-if="receipt.partner.phone">
@@ -19,7 +19,7 @@
<t t-name="l10n_in_pos.OrderLinesReceipt" t-inherit="point_of_sale.OrderLinesReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//t[@t-foreach='receipt.orderlines']" position="inside">
<t t-if="line.l10n_in_hsn_code and pos.globalState.company.country and pos.globalState.company.country.code == 'IN'">
<t t-if="line.l10n_in_hsn_code and pos.company.country and pos.company.country.code == 'IN'">
<div class="pos-receipt-left-padding">
<span>HSN Code: </span>
<t t-esc="line.l10n_in_hsn_code"/>
@@ -14,7 +14,7 @@ export class PosBus {
this.orm = orm;
// initialize bus_service and listen on pos_config-`id` channel
bus_service.addChannel(`pos_config-${pos.globalState.config.id}`);
bus_service.addChannel(`pos_config-${pos.config.id}`);
bus_service.addEventListener("notification", ({ detail }) => {
for (const message of detail) {
this.dispatch(message);
@@ -11,8 +11,8 @@ export class LocalDisplay extends Reactive {
super();
this.setup(...arguments);
}
setup(globalState) {
this.globalState = globalState;
setup(pos) {
this.pos = pos;
}
async connect() {
if (this.popupWindow && !this.popupWindow.closed) {
@@ -59,7 +59,7 @@ export class LocalDisplay extends Reactive {
// animations.
const { body: displayBody, head: displayHead } = this.popupWindow.document;
const container = document.createElement("div");
container.innerHTML = await this.globalState.customerDisplayHTML(closeUI);
container.innerHTML = await this.pos.customerDisplayHTML(closeUI);
if (!container.innerHTML || container.innerHTML === "undefined") {
displayBody.textContent = "";
@@ -95,13 +95,13 @@ export class RemoteDisplay extends Reactive {
super();
this.setup(...arguments);
}
setup(globalState, { hardware_proxy }) {
setup(pos, { hardware_proxy }) {
this.hardwareProxy = hardware_proxy;
this.globalState = globalState;
this.pos = pos;
this.updateStatus();
}
async connect() {
const html = await this.globalState.customerDisplayHTML();
const html = await this.pos.customerDisplayHTML();
try {
const { status } = await this.hardwareProxy.message("take_control", { html });
this.status = status === "success" ? "success" : "warning";
@@ -111,7 +111,7 @@ export class RemoteDisplay extends Reactive {
}
}
async update({ closeUI = false } = {}) {
const html = await this.globalState.customerDisplayHTML(closeUI);
const html = await this.pos.customerDisplayHTML(closeUI);
if (this.isUpdatingStatus && this.hardwareProxy.connectionInfo.status === "connected") {
return this.hardwareProxy.message("customer_facing_display", { html });
}
@@ -147,19 +147,19 @@ export const customerDisplayService = {
return ["pos", ...RemoteDisplay.serviceDependencies];
},
start(env, deps) {
const { globalState } = deps.pos;
const pos = deps.pos;
const {
iface_customer_facing_display: enabled,
iface_customer_facing_display_via_proxy: proxy,
} = globalState.config;
} = pos.config;
if (!enabled) {
return;
}
const display = proxy
? new RemoteDisplay(globalState, pick(deps, ...RemoteDisplay.serviceDependencies))
: new LocalDisplay(globalState);
? new RemoteDisplay(pos, pick(deps, ...RemoteDisplay.serviceDependencies))
: new LocalDisplay(pos);
// Register an effect to update the display automatically when anything it renders changes
effect(
batched((display) => display.update()),
@@ -97,8 +97,8 @@ export class DebugWidget extends Component {
),
});
if (confirmed) {
this.pos.globalState.db.remove_all_orders();
this.pos.globalState.set_synch("connected", 0);
this.pos.db.remove_all_orders();
this.pos.set_synch("connected", 0);
}
}
async deleteUnpaidOrders() {
@@ -109,7 +109,7 @@ export class DebugWidget extends Component {
),
});
if (confirmed) {
this.pos.globalState.db.remove_all_unpaid_orders();
this.pos.db.remove_all_unpaid_orders();
window.location = "/";
}
}
@@ -123,7 +123,7 @@ export class DebugWidget extends Component {
// The implementation can be better.
preparePaidOrders() {
try {
this.paidOrdersBlob = this._createBlob(this.pos.globalState.export_paid_orders());
this.paidOrdersBlob = this._createBlob(this.pos.export_paid_orders());
this.state.isPaidOrdersReady = true;
} catch (error) {
console.warn(error);
@@ -139,7 +139,7 @@ export class DebugWidget extends Component {
// FIXME POSREF why is this two steps?
prepareUnpaidOrders() {
try {
this.unpaidOrdersBlob = this._createBlob(this.pos.globalState.export_unpaid_orders());
this.unpaidOrdersBlob = this._createBlob(this.pos.export_unpaid_orders());
this.state.isUnpaidOrdersReady = true;
} catch (error) {
console.warn(error);
@@ -155,7 +155,7 @@ export class DebugWidget extends Component {
async importOrders(event) {
const file = event.target.files[0];
if (file) {
const report = this.pos.globalState.import_orders(await file.text());
const report = this.pos.import_orders(await file.text());
await this.popup.add(OrderImportPopup, { report });
}
}
@@ -30,7 +30,7 @@ export class ErrorTracebackPopup extends ErrorPopup {
return `${this.env._t("error")} ${moment().format("YYYY-MM-DD-HH-mm-ss")}.txt`;
}
emailTraceback() {
const address = this.pos.globalState.company.email;
const address = this.pos.company.email;
const subject = this.env._t("IMPORTANT: Bug Report From Odoo Point Of Sale");
window.open(
"mailto:" +
@@ -22,15 +22,15 @@ export class OfflineErrorPopup extends ErrorPopup {
super.setup(...arguments);
this.pos = usePos();
if (!this.pos.globalState.showOfflineWarning) {
if (!this.pos.showOfflineWarning) {
this.cancel();
} else {
this.pos.globalState.set_synch("disconnected");
this.pos.set_synch("disconnected");
}
}
confirm() {
this.pos.globalState.showOfflineWarning = false;
this.pos.showOfflineWarning = false;
this.cancel();
}
}
@@ -12,7 +12,7 @@ export class BackButton extends Component {
this.ui = useState(useService("ui"));
}
async backToFloorScreen() {
this.pos.globalState.mobile_pane = "right";
this.pos.mobile_pane = "right";
this.pos.showScreen("ProductScreen");
}
}
@@ -59,13 +59,13 @@ export class CashMovePopup extends AbstractAwaitablePopup {
const extras = { formattedAmount, translatedType };
const reason = this.state.reason.trim();
await this.orm.call("pos.session", "try_cash_in_out", [
[this.pos.globalState.pos_session.id],
[this.pos.pos_session.id],
type,
amount,
reason,
extras,
]);
await this.pos.globalState.logEmployeeMessage(
await this.pos.logEmployeeMessage(
`${_t("Cash")} ${translatedType} - ${_t("Amount")}: ${formattedAmount}`,
"CASH_DRAWER_ACTION"
);
@@ -78,8 +78,8 @@ export class CashMovePopup extends AbstractAwaitablePopup {
amount,
translatedType,
formattedAmount,
cashier: this.pos.globalState.get_cashier(),
company: this.pos.globalState.company,
cashier: this.pos.get_cashier(),
company: this.pos.company,
},
});
const printResult = await this.hardwareProxy.printer.printReceipt(renderedReceipt);
@@ -116,10 +116,10 @@ export class CashMovePopup extends AbstractAwaitablePopup {
async cancel() {
super.cancel();
if (
this.pos.globalState.config.iface_cashdrawer &&
this.pos.globalState.hardwareProxy.connectionInfo.status === "connected"
this.pos.config.iface_cashdrawer &&
this.pos.hardwareProxy.connectionInfo.status === "connected"
) {
this.pos.globalState.logEmployeeMessage(_t("Cash in / out"), "ACTION_CANCELLED");
this.pos.logEmployeeMessage(_t("Cash in / out"), "ACTION_CANCELLED");
}
}
}
@@ -14,11 +14,11 @@
</span>
<div class="input-field">
<input type="text" name="amount" t-model="state.amount" t-ref="amountInput" t-on-keypress="_onAmountKeypress"/>
<t t-if="pos.globalState.currency.symbol.length > 1">
<span class="left-currency" t-esc="pos.globalState.currency.symbol" />
<t t-if="pos.currency.symbol.length > 1">
<span class="left-currency" t-esc="pos.currency.symbol" />
</t>
<t t-else="">
<span class="currency" t-esc="pos.globalState.currency.symbol" />
<span class="currency" t-esc="pos.currency.symbol" />
</t>
</div>
</div>
@@ -13,11 +13,11 @@ export class CashierName extends Component {
this.ui = useState(useService("ui"));
}
get username() {
const { name } = this.pos.globalState.get_cashier();
const { name } = this.pos.get_cashier();
return name ? name : "";
}
get avatar() {
const user_id = this.pos.globalState.get_cashier_user_id();
const user_id = this.pos.get_cashier_user_id();
const id = user_id ? user_id : -1;
return `/web/image/res.users/${id}/avatar_128`;
}
@@ -27,7 +27,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
this.hardwareProxy = useService("hardware_proxy");
this.customerDisplay = useService("customer_display");
this.manualInputCashCount = false;
this.cashControl = this.pos.globalState.config.cash_control;
this.cashControl = this.pos.config.cash_control;
this.closeSessionClicked = false;
this.moneyDetails = null;
Object.assign(this, this.props.info);
@@ -97,7 +97,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
}
async downloadSalesReport() {
return this.report.download("point_of_sale.sale_details_report", [
this.pos.globalState.pos_session.id,
this.pos.pos_session.id,
]);
}
handleInputChange(paymentId) {
@@ -142,7 +142,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
const response = await this.orm.call(
"pos.session",
"post_closing_cash_details",
[this.pos.globalState.pos_session.id],
[this.pos.pos_session.id],
{
counted_cash: this.state.payments[this.defaultCashDetails.id].counted,
}
@@ -155,7 +155,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
try {
await this.orm.call("pos.session", "update_closing_control_state_session", [
this.pos.globalState.pos_session.id,
this.pos.pos_session.id,
this.state.notes,
]);
} catch (error) {
@@ -176,7 +176,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
.filter((pm) => pm.type == "bank")
.map((pm) => [pm.id, this.state.payments[pm.id].difference]);
const response = await this.orm.call("pos.session", "close_session_from_ui", [
this.pos.globalState.pos_session.id,
this.pos.pos_session.id,
bankPaymentMethodDiffPairs,
]);
if (!response.successful) {
@@ -45,10 +45,10 @@ export class Navbar extends Component {
}
get customerFacingDisplayButtonIsShown() {
return this.pos.globalState.config.iface_customer_facing_display;
return this.pos.config.iface_customer_facing_display;
}
get showCashMoveButton() {
return Boolean(this.pos.globalState?.config?.cash_control);
return Boolean(this.pos?.config?.cash_control);
}
onCashMoveButtonClick() {
this.hardwareProxy.openCashbox(_t("Cash in / out"));
@@ -59,15 +59,14 @@ export class Navbar extends Component {
this.pos.closeScreen();
} else {
if (this._shouldLoadOrders()) {
const { globalState } = this.pos;
try {
globalState.setLoadingOrderState(true);
const message = await globalState._syncAllOrdersFromServer();
this.pos.setLoadingOrderState(true);
const message = await this.pos._syncAllOrdersFromServer();
if (message) {
this.notification.add(message, 5000);
}
} finally {
globalState.setLoadingOrderState(false);
this.pos.setLoadingOrderState(false);
this.pos.showScreen("TicketScreen");
}
} else {
@@ -77,7 +76,7 @@ export class Navbar extends Component {
}
_shouldLoadOrders() {
return this.pos.globalState.config.trusted_config_ids.length > 0;
return this.pos.config.trusted_config_ids.length > 0;
}
get isTicketScreenShown() {
@@ -85,7 +84,7 @@ export class Navbar extends Component {
}
get orderCount() {
return this.pos.globalState.get_order_list().length;
return this.pos.get_order_list().length;
}
isBurgerMenuClosed() {
@@ -101,7 +100,7 @@ export class Navbar extends Component {
}
async closeSession() {
const info = await this.pos.globalState.getClosePosInfo();
const info = await this.pos.getClosePosInfo();
this.popup.add(ClosePosPopup, { info, keepBehind: true });
}
@@ -12,7 +12,7 @@
<div class="status-buttons">
<CashierName />
<SaleDetailsButton t-if="hardwareProxy.printer" isHeaderButton="true"/>
<ProxyStatus t-if="pos.globalState.config.use_proxy" />
<ProxyStatus t-if="pos.config.use_proxy" />
<CustomerFacingDisplayButton t-if="customerFacingDisplayButtonIsShown" />
<SyncNotification />
</div>
@@ -24,7 +24,7 @@ export class ProxyStatus extends Component {
iface_print_via_proxy,
iface_cashdrawer,
iface_electronic_scale,
} = this.pos.globalState.config;
} = this.pos.config;
const devices = [
{
name: _t("Scanner"),
@@ -18,20 +18,19 @@ export class SaleDetailsButton extends Component {
}
async onClick() {
const { globalState } = this.pos;
// IMPROVEMENT: Perhaps put this logic in a parent component
// so that for unit testing, we can check if this simple
// component correctly triggers an event.
const saleDetails = await this.orm.call(
"report.point_of_sale.report_saledetails",
"get_sale_details",
[false, false, false, [globalState.pos_session.id]]
[false, false, false, [this.pos.pos_session.id]]
);
const report = renderToElement(
"point_of_sale.SaleDetailsReport",
Object.assign({}, saleDetails, {
date: new Date().toLocaleString(),
pos: globalState,
pos: this.pos,
formatCurrency: this.env.utils.formatCurrency,
})
);
@@ -10,12 +10,12 @@ export class SyncNotification extends Component {
this.pos = usePos();
}
get sync() {
return this.pos.globalState.synch;
return this.pos.synch;
}
onClick() {
if (this.pos.globalState.synch.status !== "connected") {
this.pos.globalState.showOfflineWarning = true;
if (this.pos.synch.status !== "connected") {
this.pos.showOfflineWarning = true;
}
this.pos.globalState.push_orders({ show_error: true });
this.pos.push_orders({ show_error: true });
}
}
@@ -8,7 +8,7 @@
*
* To connect the interface to the right payment methods register it:
*
* import { register_payment_method } models from "@point_of_sale/app/store/models";
* import { register_payment_method } models from "@point_of_sale/app/store/pos_store";
* register_payment_method('my_payment', MyPayment);
*
* my_payment is the technical name of the added selection in
@@ -19,7 +19,7 @@ export class Chrome extends Component {
this.pos = usePos();
this.popup = useService("popup");
const reactivePos = reactive(this.pos.globalState);
const reactivePos = reactive(this.pos);
// TODO: Should we continue on exposing posmodel as global variable?
window.posmodel = reactivePos;
@@ -33,6 +33,11 @@ export class Chrome extends Component {
onMounted(this.props.disableLoader);
}
// GETTERS //
get showCashMoveButton() {
return Boolean(this.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
@@ -29,7 +29,7 @@ export class PartnerDetailsEdit extends Component {
if (partner.property_product_pricelist) {
return partner.property_product_pricelist[0];
}
return this.pos.globalState.default_pricelist?.id ?? false;
return this.pos.default_pricelist?.id ?? false;
}
get partnerImageUrl() {
@@ -40,7 +40,7 @@
<select class="detail" name="state_id"
t-on-change="captureChange">
<option value="">None</option>
<t t-foreach="pos.globalState.states" t-as="state" t-key="state.id">
<t t-foreach="pos.states" t-as="state" t-key="state.id">
<option t-if="props.partner.country_id[0] == state.country_id[0]"
t-att-value="state.id"
t-att-selected="props.partner.state_id ? ((state.id === props.partner.state_id[0]) ? true : undefined) : undefined">
@@ -54,7 +54,7 @@
<select class="detail" name="country_id"
t-on-change="captureChange">
<option value="">None</option>
<t t-foreach="pos.globalState.countries" t-as="country" t-key="country.id">
<t t-foreach="pos.countries" t-as="country" t-key="country.id">
<option t-att-value="country.id"
t-att-selected="props.partner.country_id ? ((country.id === props.partner.country_id[0]) ? true : undefined) : undefined">
<t t-esc="country.name" />
@@ -68,9 +68,9 @@
<span class="label">Language</span>
<select class="detail" name="lang"
t-on-change="captureChange">
<t t-foreach="pos.globalState.langs" t-as="lang" t-key="lang.id">
<t t-foreach="pos.langs" t-as="lang" t-key="lang.id">
<option t-att-value="lang.code"
t-att-selected="props.partner.lang ? ((lang.code === props.partner.lang) ? true : undefined) : lang.code === pos.globalState.user.lang? true : undefined">
t-att-selected="props.partner.lang ? ((lang.code === props.partner.lang) ? true : undefined) : lang.code === pos.user.lang? true : undefined">
<t t-esc="lang.name" />
</option>
</t>
@@ -104,14 +104,14 @@
<input class="detail vat" name="vat" t-on-change="captureChange"
t-att-value="props.partner.vat || ''" />
</div>
<div t-if="pos.globalState.pricelists.length gt 1" class="partner-detail">
<div t-if="pos.pricelists.length gt 1" class="partner-detail">
<span class="label">Pricelist</span>
<select class="detail" name="property_product_pricelist"
t-on-change="captureChange">
<t t-foreach="pos.globalState.pricelists" t-as="pricelist"
<t t-foreach="pos.pricelists" t-as="pricelist"
t-key="pricelist.id">
<option t-att-value="pricelist.id"
t-att-selected="props.partner.property_product_pricelist ? (pricelist.id === props.partner.property_product_pricelist[0] ? true : undefined) : pricelist.id === pos.globalState.default_pricelist.id ? true : undefined">
t-att-selected="props.partner.property_product_pricelist ? (pricelist.id === props.partner.property_product_pricelist[0] ? true : undefined) : pricelist.id === pos.default_pricelist.id ? true : undefined">
<t t-esc="pricelist.display_name" />
</option>
</t>
@@ -69,15 +69,15 @@ export class PartnerListScreen extends Component {
// Getters
get currentOrder() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get partners() {
let res;
if (this.state.query && this.state.query.trim() !== "") {
res = this.pos.globalState.db.search_partner(this.state.query.trim());
res = this.pos.db.search_partner(this.state.query.trim());
} else {
res = this.pos.globalState.db.get_partners_sorted(1000);
res = this.pos.db.get_partners_sorted(1000);
}
res.sort(function (a, b) {
return (a.name || "").localeCompare(b.name || "");
@@ -147,15 +147,14 @@ export class PartnerListScreen extends Component {
}
createPartner() {
// initialize the edit screen with default details about country & state
const { country_id, state_id } = this.pos.globalState.company;
const { country_id, state_id } = this.pos.company;
this.state.editModeProps.partner = { country_id, state_id };
this.activateEditMode();
}
async saveChanges(processedChanges) {
const { globalState } = this.pos;
const partnerId = await this.orm.call("res.partner", "create_from_ui", [processedChanges]);
await globalState.load_new_partners();
this.state.selectedPartner = globalState.db.get_partner_by_id(partnerId);
await this.pos.load_new_partners();
this.state.selectedPartner = this.pos.db.get_partner_by_id(partnerId);
this.confirm();
}
async searchPartner() {
@@ -163,7 +162,7 @@ export class PartnerListScreen extends Component {
this.state.currentOffset = 0;
}
const result = await this.getNewPartners();
this.pos.globalState.addPartners(result);
this.pos.addPartners(result);
if (this.state.previousQuery == this.state.query) {
this.state.currentOffset += result.length;
} else {
@@ -31,7 +31,7 @@ export class PaymentScreenPaymentLines extends Component {
title: this.env._t("New amount"),
startingValue: parseFloat(paymentline.amount),
isInputSelected: true,
nbrDecimal: this.pos.globalState.currency.decimal_places,
nbrDecimal: this.pos.currency.decimal_places,
});
if (confirmed) {
@@ -34,8 +34,8 @@ export class PaymentScreen extends Component {
this.report = useService("report");
this.notification = useService("pos_notification");
this.hardwareProxy = useService("hardware_proxy");
this.payment_methods_from_config = this.pos.globalState.payment_methods.filter((method) =>
this.pos.globalState.config.payment_method_ids.includes(method.id)
this.payment_methods_from_config = this.pos.payment_methods.filter((method) =>
this.pos.config.payment_method_ids.includes(method.id)
);
this.numberBuffer = useService("number_buffer");
this.numberBuffer.use(this._getNumberBufferConfig);
@@ -62,7 +62,7 @@ export class PaymentScreen extends Component {
return config;
}
get currentOrder() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get paymentLines() {
return this.currentOrder.get_paymentlines();
@@ -158,8 +158,8 @@ export class PaymentScreen extends Component {
title: tip ? this.env._t("Change Tip") : this.env._t("Add Tip"),
startingValue: value,
isInputSelected: true,
nbrDecimal: this.pos.globalState.currency.decimal_places,
inputSuffix: this.pos.globalState.currency.symbol,
nbrDecimal: this.pos.currency.decimal_places,
inputSuffix: this.pos.currency.symbol,
});
if (confirmed) {
@@ -203,8 +203,8 @@ export class PaymentScreen extends Component {
}
async validateOrder(isForceValidate) {
this.numberBuffer.capture();
if (this.pos.globalState.config.cash_rounding) {
if (!this.pos.globalState.get_order().check_paymentlines_rounding()) {
if (this.pos.config.cash_rounding) {
if (!this.pos.get_order().check_paymentlines_rounding()) {
this.popup.add(ErrorPopup, {
title: this.env._t("Rounding error in payment lines"),
body: this.env._t(
@@ -225,7 +225,6 @@ export class PaymentScreen extends Component {
}
}
async _finalizeValidation() {
const { globalState } = this.pos;
if (this.currentOrder.is_paid_with_cash() || this.currentOrder.get_change()) {
this.hardwareProxy.openCashbox();
}
@@ -237,7 +236,7 @@ export class PaymentScreen extends Component {
try {
// 1. Save order to server.
syncOrderResult = await globalState.push_single_order(this.currentOrder);
syncOrderResult = await this.pos.push_single_order(this.currentOrder);
// 2. Invoice.
if (this.currentOrder.is_to_invoice()) {
@@ -289,10 +288,10 @@ export class PaymentScreen extends Component {
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
globalState.db.remove_unpaid_order(this.currentOrder);
this.pos.db.remove_unpaid_order(this.currentOrder);
// Ask the user to sync the remaining unsynced orders.
if (!hasError && syncOrderResult && globalState.db.get_orders().length) {
if (!hasError && syncOrderResult && this.pos.db.get_orders().length) {
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: this.env._t("Remaining unsynced orders"),
body: this.env._t(
@@ -303,7 +302,7 @@ export class PaymentScreen extends Component {
// NOTE: Not yet sure if this should be awaited or not.
// If awaited, some operations like changing screen
// might not work.
globalState.push_orders();
this.pos.push_orders();
}
}
}
@@ -410,7 +409,7 @@ export class PaymentScreen extends Component {
this.currentOrder.get_rounding_applied()
) > 0.00001
) {
if (!this.pos.globalState.payment_methods.some((pm) => pm.is_cash_count)) {
if (!this.pos.payment_methods.some((pm) => pm.is_cash_count)) {
this.popup.add(ErrorPopup, {
title: this.env._t("Cannot return change without a cash payment method"),
body: this.env._t(
@@ -474,7 +473,7 @@ export class PaymentScreen extends Component {
line.can_be_reversed = payment_terminal.supports_reversals;
// Automatically validate the order when after an electronic payment,
// the current order is fully paid and due is zero.
const { config, currency } = this.pos.globalState;
const { config, currency } = this.pos;
if (
this.currentOrder.is_paid() &&
floatIsZero(this.currentOrder.get_due(), currency.decimal_places) &&
@@ -86,16 +86,16 @@
t-on-click="toggleIsToInvoice">
<i class="fa fa-file-text-o" /> Invoice
</div>
<div t-if="pos.globalState.config.iface_tipproduct and pos.globalState.config.tip_product_id" class="button js_tip"
<div t-if="pos.config.iface_tipproduct and pos.config.tip_product_id" class="button js_tip"
t-att-class="{ highlight: currentOrder.get_tip() }"
t-on-click="addTip">
<i class="fa fa-heart" /> Tip <t t-if="currentOrder.get_tip() != 0">(<t t-esc="env.utils.formatCurrency(currentOrder.get_tip())"/>)</t>
</div>
<div t-if="pos.globalState.config.iface_cashdrawer" class="button js_cashdrawer"
<div t-if="pos.config.iface_cashdrawer" class="button js_cashdrawer"
t-on-click="openCashbox">
<i class="fa fa-archive" /> Open Cashbox
</div>
<div t-if="pos.globalState.config.ship_later" class="button"
<div t-if="pos.config.ship_later" class="button"
t-att-class="{ highlight: currentOrder.getShippingDate() }"
t-on-click="toggleShippingDatePicker">
<i class="fa fa-clock-o" /> Ship Later
@@ -22,6 +22,6 @@ export class ActionpadWidget extends Component {
return this.props.partner && this.props.partner.name.length > 10;
}
get highlightPay() {
return this.pos.globalState.get_order()?.orderlines?.length;
return this.pos.get_order()?.orderlines?.length;
}
}
@@ -16,7 +16,7 @@
Customer
</t>
</button>
<button class="button pay validation pay-order-button" t-on-click="props.actionToTrigger ? this.props.actionToTrigger : () => pos.globalState.get_order().pay()">
<button class="button pay validation pay-order-button" t-on-click="props.actionToTrigger ? this.props.actionToTrigger : () => pos.get_order().pay()">
<div class="pay-circle" t-att-class="{ 'highlight': this.highlightPay }">
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
@@ -12,7 +12,7 @@ export class CustomerButton extends Component {
}
get partner() {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
return order ? order.get_partner() : null;
}
}
@@ -21,6 +21,6 @@ ProductScreen.addControlButton({
component: CustomerButton,
position: ["before", "SetFiscalPositionButton"],
condition: function () {
return this.pos.globalState.config.module_pos_restaurant;
return this.pos.config.module_pos_restaurant;
},
});
@@ -14,7 +14,7 @@ export class OrderlineCustomerNoteButton extends Component {
this.popup = useService("popup");
}
async onClick() {
const selectedOrderline = this.pos.globalState.get_order().get_selected_orderline();
const selectedOrderline = this.pos.get_order().get_selected_orderline();
// FIXME POSREF can this happen? Shouldn't the orderline just be a prop?
if (!selectedOrderline) {
return;
@@ -14,7 +14,7 @@ export class SetFiscalPositionButton extends Component {
this.popup = useService("popup");
}
get currentOrder() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get currentFiscalPositionName() {
return this.currentOrder && this.currentOrder.fiscal_position
@@ -30,7 +30,7 @@ export class SetFiscalPositionButton extends Component {
isSelected: !currentFiscalPosition,
},
];
for (const fiscalPos of this.pos.globalState.fiscal_positions) {
for (const fiscalPos of this.pos.fiscal_positions) {
fiscalPosList.push({
id: fiscalPos.id,
label: fiscalPos.name,
@@ -61,7 +61,7 @@ export class SetFiscalPositionButton extends Component {
ProductScreen.addControlButton({
component: SetFiscalPositionButton,
condition: function () {
return this.pos.globalState.fiscal_positions.length > 0;
return this.pos.fiscal_positions.length > 0;
},
position: ["before", "SetPricelistButton"],
});
@@ -14,7 +14,7 @@ export class SetPricelistButton extends Component {
this.popup = useService("popup");
}
get currentOrder() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get currentPricelistName() {
const order = this.currentOrder;
@@ -24,7 +24,7 @@ export class SetPricelistButton extends Component {
// Create the list to be passed to the SelectionPopup.
// Pricelist object is passed as item in the list because it
// is the object that will be returned when the popup is confirmed.
const selectionList = this.pos.globalState.pricelists.map((pricelist) => ({
const selectionList = this.pos.pricelists.map((pricelist) => ({
id: pricelist.id,
label: pricelist.name,
isSelected:
@@ -32,7 +32,7 @@ export class SetPricelistButton extends Component {
item: pricelist,
}));
if (!this.pos.globalState.default_pricelist) {
if (!this.pos.default_pricelist) {
selectionList.push({
id: null,
label: this.env._t("Default Price"),
@@ -55,7 +55,7 @@ export class SetPricelistButton extends Component {
ProductScreen.addControlButton({
component: SetPricelistButton,
condition: function () {
const { config, pricelists } = this.pos.globalState;
const { config, pricelists } = this.pos;
return config.use_pricelist && pricelists.length > 0;
},
});
@@ -15,11 +15,11 @@ export class ProductInfoButton extends Component {
this.pos = usePos();
}
async click() {
const orderline = this.pos.globalState.get_order().get_selected_orderline();
const orderline = this.pos.get_order().get_selected_orderline();
if (orderline) {
const product = orderline.get_product();
const quantity = orderline.get_quantity();
const info = await this.pos.globalState.getProductInfo(product, quantity);
const info = await this.pos.getProductInfo(product, quantity);
this.popup.add(ProductInfoPopup, { info: info, product: product });
}
}
@@ -11,7 +11,7 @@ export class RefundButton extends Component {
this.pos = usePos();
}
click() {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
const partner = order.get_partner();
const searchDetails = partner ? { fieldName: "PARTNER", searchTerm: partner.name } : {};
this.pos.showScreen("TicketScreen", {
@@ -13,7 +13,7 @@ export class SaveButton extends Component {
this.notification = useService("pos_notification");
}
onClick() {
const orderline = this.pos.globalState.get_order().get_selected_orderline();
const orderline = this.pos.get_order().get_selected_orderline();
if (!orderline) {
this.notification.add(this.env._t("You cannot save an empty order"), 3000);
return;
@@ -22,14 +22,13 @@ export class SaveButton extends Component {
this.notification.add(this.env._t("Order saved for later"), 3000);
}
_selectEmptyOrder() {
const { globalState } = this.pos;
const orders = globalState.get_order_list();
const orders = this.pos.get_order_list();
const emptyOrders = orders.filter((order) => order.is_empty());
if (emptyOrders.length > 0) {
globalState.sendDraftToServer();
globalState.set_order(emptyOrders[0]);
this.pos.sendDraftToServer();
this.pos.set_order(emptyOrders[0]);
} else {
globalState.add_new_order();
this.pos.add_new_order();
}
}
}
@@ -37,6 +36,6 @@ export class SaveButton extends Component {
ProductScreen.addControlButton({
component: SaveButton,
condition: function () {
return this.pos.globalState.config.trusted_config_ids.length > 0;
return this.pos.config.trusted_config_ids.length > 0;
},
});
@@ -22,13 +22,13 @@ export class NumpadWidget extends Component {
}
get hasPriceControlRights() {
return (
this.pos.globalState.cashierHasPriceControlRights() &&
this.pos.cashierHasPriceControlRights() &&
!this.props.disabledModes.includes("price")
);
}
get hasManualDiscount() {
return (
this.pos.globalState.config.manual_discount &&
this.pos.config.manual_discount &&
!this.props.disabledModes.includes("discount")
);
}
@@ -41,7 +41,7 @@ export class NumpadWidget extends Component {
}
this.numberBuffer.capture();
this.numberBuffer.reset();
this.pos.globalState.numpadMode = mode;
this.pos.numpadMode = mode;
}
sendInput(key) {
this.numberBuffer.sendKey(key);
@@ -30,7 +30,7 @@ export class OrderWidget extends Component {
);
}
get order() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get orderlinesArray() {
return this.order ? this.order.get_orderlines() : [];
@@ -18,7 +18,7 @@ export class OrderSummary extends Component {
const totalWithoutTax = this.props.order.get_total_without_tax();
const taxAmount = total - totalWithoutTax;
return {
hasTax: !floatIsZero(taxAmount, this.pos.globalState.currency.decimal_places),
hasTax: !floatIsZero(taxAmount, this.pos.currency.decimal_places),
displayAmount: this.env.utils.formatCurrency(taxAmount),
};
}
@@ -17,7 +17,7 @@ export class Orderline extends Component {
}
selectLine() {
const line = this.props.line; // the orderline
if (this.pos.globalState.get_order().selected_orderline.id !== line.id) {
if (this.pos.get_order().selected_orderline.id !== line.id) {
this.mp_dbclk_time = new Date().getTime();
} else if (!this.mp_dbclk_time) {
this.mp_dbclk_time = new Date().getTime();
@@ -37,9 +37,9 @@ export class Orderline extends Component {
return {
selected: this.props.line.selected,
"has-change":
this.props.line.hasChange && this.pos.globalState.config.module_pos_restaurant,
this.props.line.hasChange && this.pos.config.module_pos_restaurant,
"skip-change":
this.props.line.skipChange && this.pos.globalState.config.module_pos_restaurant,
this.props.line.skipChange && this.pos.config.module_pos_restaurant,
};
}
get customerNote() {
@@ -6,7 +6,7 @@
<span class="product-name">
<t t-esc="props.line.get_full_product_name()"/>
<span> </span>
<t t-if="props.line.get_product().tracking!=='none' &amp;&amp; (pos.globalState.picking_type.use_create_lots || pos.globalState.picking_type.use_existing_lots)">
<t t-if="props.line.get_product().tracking!=='none' &amp;&amp; (pos.picking_type.use_create_lots || pos.picking_type.use_existing_lots)">
<t t-if="props.line.has_valid_product_lot()">
<i t-on-click.stop="lotIconClicked"
class="oe_link_icon fa fa-list oe_icon line-lot-icon oe_green"
@@ -25,11 +25,11 @@ export class ProductItem extends Component {
return `/web/image?model=product.product&field=image_128&id=${product.id}&unique=${product.write_date}`;
}
get pricelist() {
const current_order = this.pos.globalState.get_order();
const current_order = this.pos.get_order();
if (current_order) {
return current_order.pricelist;
}
return this.pos.globalState.default_pricelist;
return this.pos.default_pricelist;
}
get price() {
const formattedUnitPrice = this.env.utils.formatCurrency(
@@ -37,7 +37,7 @@ export class ProductItem extends Component {
);
if (this.props.product.to_weight) {
return `${formattedUnitPrice}/${
this.pos.globalState.units_by_id[this.props.product.uom_id[0]].name
this.pos.units_by_id[this.props.product.uom_id[0]].name
}`;
} else {
return formattedUnitPrice;
@@ -19,14 +19,14 @@ export class ProductInfoPopup extends AbstractAwaitablePopup {
Object.assign(this, this.props.info);
}
searchProduct(productName) {
this.pos.globalState.setSelectedCategoryId(0);
this.pos.globalState.searchProductWord = productName;
this.pos.setSelectedCategoryId(0);
this.pos.searchProductWord = productName;
this.cancel();
}
_hasMarginsCostsAccessRights() {
const isAccessibleToEveryUser =
this.pos.globalState.config.is_margins_costs_accessible_to_every_user;
const isCashierManager = this.pos.globalState.get_cashier().role === "manager";
this.pos.config.is_margins_costs_accessible_to_every_user;
const isCashierManager = this.pos.get_cashier().role === "manager";
return isAccessibleToEveryUser || isCashierManager;
}
}
@@ -50,7 +50,7 @@
</div>
<div class="section-inventory" t-if="productInfo.warehouses.length > 0">
<div class="section-title">
<span>Inventory <t t-if="pos.globalState.pos_session.update_stock_at_closing">(as of opening)</t></span>
<span>Inventory <t t-if="pos.pos_session.update_stock_at_closing">(as of opening)</t></span>
<div class="section-title-line"></div>
</div>
<div class="section-inventory-body">
@@ -42,26 +42,26 @@ export class ProductsWidgetControlPanel extends Component {
this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown;
}
_clearSearch() {
this.pos.globalState.searchProductWord = "";
this.pos.searchProductWord = "";
this.props.clearSearch();
}
get displayCategImages() {
return (
!this.ui.isSmall &&
Object.values(this.pos.globalState.db.category_by_id).some((categ) => categ.has_image)
Object.values(this.pos.db.category_by_id).some((categ) => categ.has_image)
);
}
updateSearch(event) {
this.props.updateSearch(this.pos.globalState.searchProductWord);
this.props.updateSearch(this.pos.searchProductWord);
}
async _onPressEnterKey() {
if (!this.pos.globalState.searchProductWord) {
if (!this.pos.searchProductWord) {
return;
}
this.props.loadProductFromServer();
}
searchProductFromInfo(productName) {
this.pos.globalState.searchProductWord = productName;
this.pos.searchProductWord = productName;
this.props.switchCategory(0);
this.props.updateSearch(productName);
}
@@ -11,8 +11,8 @@
<t t-if="!state.isMobile || state.mobileSearchBarIsShown">
<div class="pos-search-bar">
<i class="fa fa-search"/>
<input t-model="this.pos.globalState.searchProductWord" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
<i t-if="this.pos.globalState.searchProductWord" class="fa fa-times" t-on-click="_clearSearch"/>
<input t-model="this.pos.searchProductWord" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
<i t-if="this.pos.searchProductWord" class="fa fa-times" t-on-click="_clearSearch"/>
</div>
</t>
</div>
@@ -31,16 +31,16 @@ export class ProductsWidget extends Component {
this.orm = useService("orm");
}
get hasProducts() {
return Object.keys(this.pos.globalState.db.product_by_id).length > 0;
return Object.keys(this.pos.db.product_by_id).length > 0;
}
get selectedCategoryId() {
return this.pos.globalState.selectedCategoryId;
return this.pos.selectedCategoryId;
}
get searchWord() {
return this.pos.globalState.searchProductWord.trim();
return this.pos.searchProductWord.trim();
}
get productsToDisplay() {
const { db } = this.pos.globalState;
const { db } = this.pos;
let list = [];
if (this.searchWord !== "") {
list = db.search_product_in_category(this.selectedCategoryId, this.searchWord);
@@ -52,13 +52,13 @@ export class ProductsWidget extends Component {
});
}
get subcategories() {
const { db } = this.pos.globalState;
const { db } = this.pos;
return db
.get_category_childs_ids(this.selectedCategoryId)
.map((id) => db.get_category_by_id(id));
}
get breadcrumbs() {
const { db } = this.pos.globalState;
const { db } = this.pos;
if (this.selectedCategoryId === db.root_category_id) {
return [];
}
@@ -68,25 +68,25 @@ export class ProductsWidget extends Component {
].map((id) => db.get_category_by_id(id));
}
get hasNoCategories() {
return this.pos.globalState.db.get_category_childs_ids(0).length === 0;
return this.pos.db.get_category_childs_ids(0).length === 0;
}
get shouldShowButton() {
return this.productsToDisplay.length === 0 && this.searchWord;
}
switchCategory(categoryId) {
this.pos.globalState.setSelectedCategoryId(categoryId);
this.pos.setSelectedCategoryId(categoryId);
}
updateSearch(searchWord) {
this.pos.globalState.searchProductWord = searchWord;
this.pos.searchProductWord = searchWord;
}
clearSearch() {
this.pos.globalState.searchProductWord = "";
this.pos.searchProductWord = "";
}
updateProductList(event) {
this.switchCategory(0);
}
async onPressEnterKey() {
const { searchProductWord } = this.pos.globalState;
const { searchProductWord } = this.pos;
if (!searchProductWord) {
return;
}
@@ -117,7 +117,7 @@ export class ProductsWidget extends Component {
}
}
async loadProductFromDB() {
const { searchProductWord } = this.pos.globalState;
const { searchProductWord } = this.pos;
if (!searchProductWord) {
return;
}
@@ -144,7 +144,7 @@ export class ProductsWidget extends Component {
}
);
if (ProductIds.length) {
await this.pos.globalState._addProducts(ProductIds, false);
await this.pos._addProducts(ProductIds, false);
}
this.updateProductList();
return ProductIds;
@@ -169,10 +169,10 @@ export class ProductsWidget extends Component {
const { products, categories } = await this.orm.call(
"pos.session",
"load_product_frontend",
[this.pos.globalState.pos_session.id]
[this.pos.pos_session.id]
);
this.pos.globalState.db.add_categories(categories);
this.pos.globalState._loadProductProduct(products);
this.pos.db.add_categories(categories);
this.pos._loadProductProduct(products);
}
createNewProducts() {
@@ -72,7 +72,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
return this.currentOrder ? this.currentOrder.get_partner() : null;
}
get currentOrder() {
return this.pos.globalState.get_order();
return this.pos.get_order();
}
get total() {
return this.env.utils.formatCurrency(this.currentOrder?.get_total_with_tax() ?? 0);
@@ -81,16 +81,15 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
return this.currentOrder.orderlines?.reduce((items, line) => items + line.quantity, 0) ?? 0;
}
async updateSelectedOrderline({ buffer, key }) {
const { globalState } = this.pos;
if (globalState.numpadMode === "quantity" && globalState.disallowLineQuantityChange()) {
const order = globalState.get_order();
if (this.pos.numpadMode === "quantity" && this.pos.disallowLineQuantityChange()) {
const order = this.pos.get_order();
if (!order.orderlines.length) {
return;
}
const selectedLine = order.get_selected_orderline();
const orderlines = order.orderlines;
const lastId = orderlines.length !== 0 && orderlines.at(orderlines.length - 1).cid;
const currentQuantity = globalState.get_order().get_selected_orderline().get_quantity();
const currentQuantity = this.pos.get_order().get_selected_orderline().get_quantity();
if (selectedLine.noDecrease) {
this.popup.add(ErrorPopup, {
@@ -112,12 +111,12 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
this._setValue(val);
if (val == "remove") {
this.numberBuffer.reset();
globalState.numpadMode = "quantity";
this.pos.numpadMode = "quantity";
}
}
}
_setValue(val) {
const { numpadMode } = this.pos.globalState;
const { numpadMode } = this.pos;
if (this.currentOrder.get_selected_orderline()) {
if (numpadMode === "quantity") {
const result = this.currentOrder.get_selected_orderline().set_quantity(val);
@@ -134,8 +133,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
}
}
async _barcodeProductAction(code) {
const { globalState } = this.pos;
let product = globalState.db.get_product_by_barcode(code.base_code);
let product = this.pos.db.get_product_by_barcode(code.base_code);
if (!product) {
// find the barcode in the backend
let foundProductIds = [];
@@ -143,9 +141,9 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
["barcode", "=", code.base_code],
]);
if (foundProductIds.length) {
await globalState._addProducts(foundProductIds);
await this.pos._addProducts(foundProductIds);
// assume that the result is unique.
product = globalState.db.get_product_by_id(foundProductIds[0]);
product = this.pos.db.get_product_by_id(foundProductIds[0]);
} else {
return this.popup.add(ErrorBarcodePopup, { code: code.base_code });
}
@@ -180,7 +178,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
this.numberBuffer.reset();
}
_barcodePartnerAction(code) {
const partner = this.pos.globalState.db.get_partner_by_barcode(code.code);
const partner = this.pos.db.get_partner_by_barcode(code.code);
if (partner) {
if (this.currentOrder.get_partner() !== partner) {
this.currentOrder.set_partner(partner);
@@ -209,7 +207,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
});
const newQuantity = inputNumber && inputNumber !== "" ? parseFloat(inputNumber) : null;
if (confirmed && newQuantity !== null) {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
const selectedLine = order.get_selected_orderline();
const currentQuantity = selectedLine.get_quantity();
if (newQuantity >= currentQuantity) {
@@ -268,23 +266,22 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
// FIXME POSREF this is dead code, check if we need the business logic that's left in here
// If we do it should be in the model.
async onClickPay() {
const { globalState } = this.pos;
if (globalState.get_order().server_id) {
if (this.pos.get_order().server_id) {
try {
const isPaid = await this.orm.call("pos.order", "is_already_paid", [
globalState.get_order().server_id,
this.pos.get_order().server_id,
]);
if (isPaid) {
const searchDetails = {
fieldName: "RECEIPT_NUMBER",
searchTerm: globalState.get_order().uid,
searchTerm: this.pos.get_order().uid,
};
this.pos.showScreen("TicketScreen", {
ui: { filter: "SYNCED", searchDetails },
});
this.notification.add(this.env._t("The order has been already paid."), 3000);
globalState.removeOrder(globalState.get_order(), false);
globalState.add_new_order();
this.pos.removeOrder(this.pos.get_order(), false);
this.pos.add_new_order();
return;
}
} catch (error) {
@@ -298,7 +295,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
this.currentOrder.pay();
}
switchPane() {
this.pos.globalState.switchPane();
this.pos.switchPane();
}
}
@@ -4,7 +4,7 @@
<t t-name="point_of_sale.ProductScreen" owl="1">
<div class="product-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
<div class="screen-full-width">
<div class="leftpane pane-border" t-if="!ui.isSmall || pos.globalState.mobile_pane === 'left'">
<div class="leftpane pane-border" t-if="!ui.isSmall || pos.mobile_pane === 'left'">
<OrderWidget/>
<div class="pads">
<div class="control-buttons">
@@ -22,11 +22,11 @@
onClickMore.bind="displayAllControlPopup" />
<NumpadWidget
updateSelectedOrderline.bind="updateSelectedOrderline"
activeMode="pos.globalState.numpadMode" />
activeMode="pos.numpadMode" />
</div>
</div>
</div>
<div class="rightpane" t-if="!ui.isSmall || pos.globalState.mobile_pane === 'right'">
<div class="rightpane" t-if="!ui.isSmall || pos.mobile_pane === 'right'">
<ProductsWidget />
<t t-if="ui.isSmall">
<div class="product-reminder" t-if="showProductReminder" t-key="animationKey" t-ref="reminder">
@@ -197,7 +197,7 @@
</t>
<t t-elif="line.discount !== 0">
<div class="pos-receipt-left-padding">
<t t-if="pos.globalState.config.iface_tax_included === 'total'">
<t t-if="pos.config.iface_tax_included === 'total'">
<t t-esc="env.utils.formatCurrency(line.price_with_tax_before_discount, false)"/>
</t>
<t t-else="">
@@ -211,7 +211,7 @@
</div>
</t>
<div class="pos-receipt-left-padding">
<t t-esc="Math.round(line.quantity * Math.pow(10, pos.globalState.dp['Product Unit of Measure'])) / Math.pow(10, pos.globalState.dp['Product Unit of Measure'])"/>
<t t-esc="Math.round(line.quantity * Math.pow(10, pos.dp['Product Unit of Measure'])) / Math.pow(10, pos.dp['Product Unit of Measure'])"/>
<t t-if="!line.is_in_unit" t-esc="line.unit_name" />
x
<t t-esc="env.utils.formatCurrency(line.price_display_one)" />
@@ -23,7 +23,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
this.orderReceipt = useRef("order-receipt");
this.buttonMailReceipt = useRef("order-mail-receipt-button");
this.buttonPrintReceipt = useRef("order-print-receipt-button");
this.currentOrder = this.pos.globalState.get_order();
this.currentOrder = this.pos.get_order();
const partner = this.currentOrder.get_partner();
this.orderUiState = this.currentOrder.uiState.ReceiptScreen;
this.orderUiState.inputEmail =
@@ -49,14 +49,14 @@ export class ReceiptScreen extends AbstractReceiptScreen {
onWillStart(async () => {
// When the order is paid, if there is still a part of the order
// to send in preparation it is automatically sent
if (this.pos.globalState.orderPreparationCategories.size) {
if (this.pos.orderPreparationCategories.size) {
await this.pos.sendOrderInPreparation(this.currentOrder);
}
});
}
_addNewOrder() {
this.pos.globalState.add_new_order();
this.pos.add_new_order();
}
onSendEmail() {
if (this.buttonMailReceipt.el.classList.contains("fa-spin")) {
@@ -96,7 +96,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
get orderAmountPlusTip() {
const order = this.currentOrder;
const orderTotalAmount = order.get_total_with_tax();
const tip_product_id = this.pos.globalState.config.tip_product_id?.[0];
const tip_product_id = this.pos.config.tip_product_id?.[0];
const tipLine = order
.get_orderlines()
.find((line) => tip_product_id && line.product.id === tip_product_id);
@@ -137,24 +137,24 @@ export class ReceiptScreen extends AbstractReceiptScreen {
}
}
orderDone() {
this.pos.globalState.removeOrder(this.currentOrder);
this.pos.removeOrder(this.currentOrder);
this._addNewOrder();
const { name, props } = this.nextScreen;
this.pos.showScreen(name, props);
}
resumeOrder() {
this.pos.globalState.removeOrder(this.currentOrder);
this.pos.globalState.selectNextOrder();
this.pos.removeOrder(this.currentOrder);
this.pos.selectNextOrder();
const { name, props } = this.ticketScreen;
this.pos.showScreen(name, props);
}
continueSplitting() {
this.pos.globalState.selectedOrder = this.currentOrder.originalSplittedOrder;
this.pos.globalState.removeOrder(this.currentOrder);
this.pos.selectedOrder = this.currentOrder.originalSplittedOrder;
this.pos.removeOrder(this.currentOrder);
this.pos.showScreen("ProductScreen");
}
isResumeVisible() {
return this.pos.globalState.get_order_list().length > 1;
return this.pos.get_order_list().length > 1;
}
async printReceipt() {
this.buttonPrintReceipt.el.className = "fa fa-fw fa-spin fa-circle-o-notch";
@@ -170,7 +170,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
}
}
_shouldAutoPrint() {
return this.pos.globalState.config.iface_print_auto && !this.currentOrder._printed;
return this.pos.config.iface_print_auto && !this.currentOrder._printed;
}
_shouldCloseImmediately() {
var invoiced_finalized = this.currentOrder.is_to_invoice()
@@ -178,7 +178,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
: true;
return (
this.hardwareProxy.printer &&
this.pos.globalState.config.iface_print_skip_screen &&
this.pos.config.iface_print_skip_screen &&
invoiced_finalized
);
}
@@ -193,7 +193,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
email: this.orderUiState.inputEmail,
name: partner ? partner.name : this.orderUiState.inputEmail,
};
const order_server_id = this.pos.globalState.validated_orders_name_server_id_map[orderName];
const order_server_id = this.pos.validated_orders_name_server_id_map[orderName];
if (!order_server_id) {
this.popup.add(OfflineErrorPopup, {
title: this.env._t("Unsynced order"),
@@ -21,7 +21,7 @@ export class ReprintReceiptScreen extends AbstractReceiptScreen {
this.pos.showScreen("TicketScreen", { reuseSavedUIState: true });
}
async printReceipt() {
const { iface_print_skip_screen } = this.pos.globalState.config;
const { iface_print_skip_screen } = this.pos.config;
if (this.hardwareProxy.printer && iface_print_skip_screen) {
const result = await this._printReceipt();
if (result) {
@@ -60,8 +60,8 @@ export class ScaleScreen extends Component {
setTimeout(() => this._setWeight(), 500);
}
get _activePricelist() {
const current_order = this.pos.globalState.get_order();
let current_pricelist = this.pos.globalState.default_pricelist;
const current_order = this.pos.get_order();
let current_pricelist = this.pos.default_pricelist;
if (current_order) {
current_pricelist = current_order.pricelist;
}
@@ -76,7 +76,7 @@ export class ScaleScreen extends Component {
if (!unit_id) {
return defaultstr;
}
const unit = this.pos.globalState.units_by_id[unit_id[0]];
const unit = this.pos.units_by_id[unit_id[0]];
const weight = round_pr(this.state.weight || 0, unit.rounding);
let weightstr = weight.toFixed(Math.ceil(Math.log(1.0 / unit.rounding) / Math.log(10)));
weightstr += " " + unit.name;
@@ -96,7 +96,7 @@ export class ScaleScreen extends Component {
if (!this.props.product) {
return "";
}
return this.pos.globalState.units_by_id[this.props.product.uom_id[0]].name;
return this.pos.units_by_id[this.props.product.uom_id[0]].name;
}
}
@@ -20,7 +20,7 @@ export class OrderlineDetails extends Component {
const formatQty = (line) => {
const quantity = line.get_quantity();
const unit = line.get_unit();
const decimals = this.pos.globalState.dp["Product Unit of Measure"];
const decimals = this.pos.dp["Product Unit of Measure"];
const rounding = Math.max(unit.rounding, Math.pow(10, -decimals));
const roundedQuantity = round_pr(quantity, rounding);
return formatFloat(roundedQuantity, { digits: [69, decimals] });
@@ -55,17 +55,17 @@ export class OrderlineDetails extends Component {
return this.props.line.get_customer_note();
}
getToRefundDetail() {
return this.pos.globalState.toRefundLines[this.props.line.id];
return this.pos.toRefundLines[this.props.line.id];
}
hasRefundedQty() {
return !this.pos.globalState.isProductQtyZero(this.props.line.refunded_qty);
return !this.pos.isProductQtyZero(this.props.line.refunded_qty);
}
getFormattedRefundedQty() {
return this.env.utils.formatProductQty(this.props.line.refunded_qty);
}
hasToRefundQty() {
const toRefundDetail = this.getToRefundDetail();
return !this.pos.globalState.isProductQtyZero(toRefundDetail && toRefundDetail.qty);
return !this.pos.isProductQtyZero(toRefundDetail && toRefundDetail.qty);
}
getFormattedToRefundQty() {
const toRefundDetail = this.getToRefundDetail();
@@ -54,7 +54,7 @@ export class TicketScreen extends Component {
this.numberBuffer.use({
triggerAtInput: (event) => this._onUpdateSelectedOrderline(event),
});
this._state = this.pos.globalState.TICKET_SCREEN_STATE;
this._state = this.pos.TICKET_SCREEN_STATE;
this.state = useState({
showSearchBar: !this.ui.isSmall,
});
@@ -62,7 +62,7 @@ export class TicketScreen extends Component {
? this._state.ui
: {
selectedSyncedOrderId: null,
searchDetails: this.pos.globalState.getDefaultSearchDetails(),
searchDetails: this.pos.getDefaultSearchDetails(),
filter: null,
selectedOrderlineIds: {},
};
@@ -112,15 +112,13 @@ export class TicketScreen extends Component {
}
}
onCreateNewOrder() {
this.pos.globalState.add_new_order();
this.pos.add_new_order();
this.pos.showScreen("ProductScreen");
}
_selectNextOrder(currentOrder) {
const currentOrderIndex = this._getOrderList().indexOf(currentOrder);
const orderList = this._getOrderList();
this.pos.globalState.set_order(
orderList[currentOrderIndex + 1] || orderList[currentOrderIndex - 1]
);
this.pos.set_order(orderList[currentOrderIndex + 1] || orderList[currentOrderIndex - 1]);
}
async onDeleteOrder(order) {
const screen = order.get_screen_data();
@@ -142,18 +140,17 @@ export class TicketScreen extends Component {
return;
}
}
const { globalState } = this.pos;
if (order && (await this._onBeforeDeleteOrder(order))) {
if (Object.keys(order.lastOrderPrepaChange).length > 0) {
await this.pos.sendOrderInPreparation(order, true);
}
if (order === globalState.get_order()) {
if (order === this.pos.get_order()) {
this._selectNextOrder(order);
}
globalState.removeOrder(order);
this.pos.removeOrder(order);
}
if (globalState.isOpenOrderShareable()) {
globalState._removeOrdersFromServer();
if (this.pos.isOpenOrderShareable()) {
this.pos._removeOrdersFromServer();
}
}
async onNextPage() {
@@ -169,7 +166,7 @@ export class TicketScreen extends Component {
}
}
async onInvoiceOrder(orderId) {
this.pos.globalState._invalidateSyncedOrdersCache([orderId]);
this.pos._invalidateSyncedOrdersCache([orderId]);
await this._fetchSyncedOrders();
}
onClickOrderline(orderline) {
@@ -179,7 +176,7 @@ export class TicketScreen extends Component {
}
onClickRefundOrderUid(orderUid) {
// Open the refund order.
const refundOrder = this.pos.globalState.orders.find((order) => order.uid == orderUid);
const refundOrder = this.pos.orders.find((order) => order.uid == orderUid);
if (refundOrder) {
this._setOrder(refundOrder);
}
@@ -259,10 +256,9 @@ export class TicketScreen extends Component {
? this.props.destinationOrder
: this._getEmptyOrder(partner);
const { globalState } = this.pos;
// Add orderline for each toRefundDetail to the destinationOrder.
for (const refundDetail of allToRefundDetails) {
const product = globalState.db.get_product_by_id(refundDetail.orderline.productId);
const product = this.pos.db.get_product_by_id(refundDetail.orderline.productId);
const options = this._prepareRefundOrderlineOptions(refundDetail);
await destinationOrder.add_product(product, options);
refundDetail.destinationOrderUid = destinationOrder.uid;
@@ -274,8 +270,8 @@ export class TicketScreen extends Component {
destinationOrder.updatePricelist(partner);
}
if (globalState.get_order().cid !== destinationOrder.cid) {
globalState.set_order(destinationOrder);
if (this.pos.get_order().cid !== destinationOrder.cid) {
this.pos.set_order(destinationOrder);
}
this.pos.closeScreen();
@@ -363,7 +359,7 @@ export class TicketScreen extends Component {
const productScreenStatus = this._getScreenToStatusMap().ProductScreen;
return (
order.get_orderlines().length === 0 &&
this.pos.globalState.get_order_list().length === 1 &&
this.pos.get_order_list().length === 1 &&
status === productScreenStatus &&
order.get_paymentlines().length === 0
);
@@ -387,12 +383,12 @@ export class TicketScreen extends Component {
const selectedOrder = this.getSelectedSyncedOrder();
return selectedOrder ? order.backendId == selectedOrder.backendId : false;
} else {
const activeOrder = this.pos.globalState.get_order();
const activeOrder = this.pos.get_order();
return activeOrder ? activeOrder.uid == order.uid : false;
}
}
showCardholderName() {
return this.pos.globalState.payment_methods.some((method) => method.use_payment_terminal);
return this.pos.payment_methods.some((method) => method.use_payment_terminal);
}
getSearchBarConfig() {
return {
@@ -426,7 +422,7 @@ export class TicketScreen extends Component {
if (this._doesOrderHaveSoleItem(order)) {
return true;
}
const total = Object.values(this.pos.globalState.toRefundLines)
const total = Object.values(this.pos.toRefundLines)
.filter(
(toRefundDetail) =>
toRefundDetail.orderline.orderUid === order.uid &&
@@ -434,7 +430,7 @@ export class TicketScreen extends Component {
)
.map((toRefundDetail) => toRefundDetail.qty)
.reduce((acc, val) => acc + val, 0);
return !this.pos.globalState.isProductQtyZero(total);
return !this.pos.isProductQtyZero(total);
}
//#endregion
//#region PRIVATE METHODS
@@ -449,7 +445,7 @@ export class TicketScreen extends Component {
_getEmptyOrder(partner) {
let emptyOrderForPartner = null;
let emptyOrder = null;
for (const order of this.pos.globalState.orders) {
for (const order of this.pos.orders) {
if (order.get_orderlines().length === 0 && order.get_paymentlines().length === 0) {
if (order.get_partner() === partner) {
emptyOrderForPartner = order;
@@ -460,7 +456,7 @@ export class TicketScreen extends Component {
}
}
}
return emptyOrderForPartner || emptyOrder || this.pos.globalState.add_new_order();
return emptyOrderForPartner || emptyOrder || this.pos.add_new_order();
}
_doesOrderHaveSoleItem(order) {
const orderlines = order.get_orderlines();
@@ -469,7 +465,7 @@ export class TicketScreen extends Component {
}
const theOrderline = orderlines[0];
const refundableQty = theOrderline.get_quantity() - theOrderline.refunded_qty;
return this.pos.globalState.isProductQtyZero(refundableQty - 1);
return this.pos.isProductQtyZero(refundableQty - 1);
}
_prepareAutoRefundOnOrder(order) {
const selectedOrderlineId = this.getSelectedOrderlineId();
@@ -480,7 +476,7 @@ export class TicketScreen extends Component {
const toRefundDetail = this._getToRefundDetail(orderline);
const refundableQty = orderline.get_quantity() - orderline.refunded_qty;
if (this.pos.globalState.isProductQtyZero(refundableQty - 1) && toRefundDetail.qty === 0) {
if (this.pos.isProductQtyZero(refundableQty - 1) && toRefundDetail.qty === 0) {
toRefundDetail.qty = 1;
}
return true;
@@ -493,7 +489,7 @@ export class TicketScreen extends Component {
* @returns
*/
_getToRefundDetail(orderline) {
const { toRefundLines } = this.pos.globalState;
const { toRefundLines } = this.pos;
if (orderline.id in toRefundLines) {
return toRefundLines[orderline.id];
}
@@ -529,9 +525,9 @@ export class TicketScreen extends Component {
* @returns {Array} refundableDetails
*/
_getRefundableDetails(partner) {
return Object.values(this.pos.globalState.toRefundLines).filter(
return Object.values(this.pos.toRefundLines).filter(
({ qty, orderline, destinationOrderUid }) =>
!this.pos.globalState.isProductQtyZero(qty) &&
!this.pos.isProductQtyZero(qty) &&
(partner ? orderline.orderPartnerId == partner.id : true) &&
!destinationOrderUid
);
@@ -555,15 +551,14 @@ export class TicketScreen extends Component {
};
}
_setOrder(order) {
const { globalState } = this.pos;
if (globalState.isOpenOrderShareable()) {
globalState.sendDraftToServer();
if (this.pos.isOpenOrderShareable()) {
this.pos.sendDraftToServer();
}
globalState.set_order(order);
this.pos.set_order(order);
this.pos.closeScreen();
}
_getOrderList() {
return this.pos.globalState.get_order_list();
return this.pos.get_order_list();
}
_getFilterOptions() {
const orderStates = this._getOrderStates();
@@ -662,12 +657,11 @@ export class TicketScreen extends Component {
* order is not fetched anymore, instead, we use info from cache.
*/
async _fetchSyncedOrders() {
const { globalState } = this.pos;
const domain = this._computeSyncedOrdersDomain();
const limit = this._state.syncedOrders.nPerPage;
const offset =
(this._state.syncedOrders.currentPage - 1) * this._state.syncedOrders.nPerPage;
const config_id = globalState.config.id;
const config_id = this.pos.config.id;
const { ordersInfo, totalCount } = await this.orm.call(
"pos.order",
"search_paid_order_ids",
@@ -686,14 +680,14 @@ export class TicketScreen extends Component {
if (idsToLoad.length > 0) {
const fetchedOrders = await this.orm.call("pos.order", "export_for_ui", [idsToLoad]);
// Check for missing products and partners and load them in the PoS
await globalState._loadMissingProducts(fetchedOrders);
await globalState._loadMissingPartners(fetchedOrders);
await this.pos._loadMissingProducts(fetchedOrders);
await this.pos._loadMissingPartners(fetchedOrders);
// Cache these fetched orders so that next time, no need to fetch
// them again, unless invalidated. See `_onInvoiceOrder`.
fetchedOrders.forEach((order) => {
this._state.syncedOrders.cache[order.id] = new Order(
{},
{ pos: globalState, json: order }
{ pos: this.pos, json: order }
);
});
//Update the datetime indicator of the cache refresh
@@ -18,7 +18,7 @@ export class CashOpeningPopup extends AbstractAwaitablePopup {
this.pos = usePos();
this.state = useState({
notes: "",
openingCash: this.pos.globalState.pos_session.cash_register_balance_start || 0,
openingCash: this.pos.pos_session.cash_register_balance_start || 0,
});
this.popup = useService("popup");
this.orm = useService("orm");
@@ -27,10 +27,10 @@ export class CashOpeningPopup extends AbstractAwaitablePopup {
}
//@override
async confirm() {
this.pos.globalState.pos_session.cash_register_balance_start = this.state.openingCash;
this.pos.globalState.pos_session.state = "opened";
this.pos.pos_session.cash_register_balance_start = this.state.openingCash;
this.pos.pos_session.state = "opened";
this.orm.call("pos.session", "set_cashbox_pos", [
this.pos.globalState.pos_session.id,
this.pos.pos_session.id,
this.state.openingCash,
this.state.notes,
]);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -11,8 +11,8 @@ import { registry } from "@web/core/registry";
export const contextualUtilsService = {
dependencies: ["pos"],
start(env, { pos }) {
const currency = pos.globalState.currency;
const productUoMDecimals = pos.globalState.dp["Product Unit of Measure"];
const currency = pos.currency;
const productUoMDecimals = pos.dp["Product Unit of Measure"];
const formatProductQty = (qty) => {
return formatFloat(qty, { digits: [true, productUoMDecimals] });
@@ -11,11 +11,11 @@ export class MoneyDetailsPopup extends AbstractAwaitablePopup {
setup() {
super.setup();
this.pos = usePos();
this.currency = this.pos.globalState.currency;
this.currency = this.pos.currency;
this.state = useState({
moneyDetails: this.props.moneyDetails
? { ...this.props.moneyDetails }
: Object.fromEntries(this.pos.globalState.bills.map((bill) => [bill.value, 0])),
: Object.fromEntries(this.pos.bills.map((bill) => [bill.value, 0])),
total: this.props.total ? this.props.total : 0,
action: this.props.action ? this.props.action : null,
});
@@ -42,7 +42,7 @@ export class MoneyDetailsPopup extends AbstractAwaitablePopup {
let moneyDetailsNotes = !floatIsZero(this.state.total, this.currency.decimal_places)
? "Money details: \n"
: null;
this.pos.globalState.bills.forEach((bill) => {
this.pos.bills.forEach((bill) => {
if (this.state.moneyDetails[bill.value]) {
moneyDetailsNotes += ` - ${
this.state.moneyDetails[bill.value]
@@ -59,10 +59,10 @@ export class MoneyDetailsPopup extends AbstractAwaitablePopup {
async cancel() {
super.cancel();
if (
this.pos.globalState.config.iface_cashdrawer &&
this.pos.globalState.hardwareProxy.connectionInfo.status === "connected"
this.pos.config.iface_cashdrawer &&
this.pos.hardwareProxy.connectionInfo.status === "connected"
) {
this.pos.globalState.logEmployeeMessage(this.state.action, "ACTION_CANCELLED");
this.pos.logEmployeeMessage(this.state.action, "ACTION_CANCELLED");
}
}
}
@@ -6,7 +6,7 @@
<t t-set="html_data" t-value="{'style': 'height: 100%;'}"/>
<t t-set="title">Point of Sale Tests</t>
<t t-set="head">
<!-- we need session_info in order to properly instantiate PosGlobalState -->
<!-- we need session_info in order to properly instantiate PosStore-->
<script type="text/javascript">
var odoo = <t t-out="json.dumps({
'csrf_token': request.csrf_token(None),
+2 -1
View File
@@ -1,5 +1,6 @@
/** @odoo-module */
import { register_payment_method, Payment } from "@point_of_sale/app/store/models";
import { register_payment_method } from "@point_of_sale/app/store/pos_store";
import { Payment } from "@point_of_sale/app/store/models";
import { PaymentAdyen } from "@pos_adyen/js/payment_adyen";
import { patch } from "@web/core/utils/patch";
@@ -19,7 +19,7 @@ export class DiscountButton extends Component {
var self = this;
const { confirmed, payload } = await this.popup.add(NumberPopup, {
title: this.env._t("Discount Percentage"),
startingValue: this.pos.globalState.config.discount_pc,
startingValue: this.pos.config.discount_pc,
isInputSelected: true,
});
if (confirmed) {
@@ -29,10 +29,9 @@ export class DiscountButton extends Component {
}
async apply_discount(pc) {
const { globalState } = this.pos;
const order = globalState.get_order();
const order = this.pos.get_order();
const lines = order.get_orderlines();
const product = globalState.db.get_product_by_id(globalState.config.discount_product_id[0]);
const product = this.pos.db.get_product_by_id(this.pos.config.discount_product_id[0]);
if (product === undefined) {
await this.popup.add(ErrorPopup, {
title: this.env._t("No discount product found"),
@@ -62,8 +61,8 @@ export class DiscountButton extends Component {
tax_ids_array,
lines.filter(
(line) =>
!globalState.config.tip_product_id ||
line.product.id !== globalState.config.tip_product_id[0]
!this.pos.config.tip_product_id ||
line.product.id !== this.pos.config.tip_product_id[0]
)
);
@@ -81,7 +80,7 @@ export class DiscountButton extends Component {
? sprintf(
this.env._t("Tax: %s"),
tax_ids_array
.map((taxId) => globalState.taxes_by_id[taxId].amount + "%")
.map((taxId) => this.pos.taxes_by_id[taxId].amount + "%")
.join(", ")
)
: this.env._t("No tax")),
@@ -97,7 +96,7 @@ export class DiscountButton extends Component {
ProductScreen.addControlButton({
component: DiscountButton,
condition: function () {
const { module_pos_discount, discount_product_id } = this.pos.globalState.config;
const { module_pos_discount, discount_product_id } = this.pos.config;
return module_pos_discount && discount_product_id;
},
});
@@ -1,10 +1,10 @@
/** @odoo-module */
import { PosGlobalState } from "@point_of_sale/app/store/models";
import { PosStore } from "@point_of_sale/app/store/pos_store";
import { EpsonPrinter } from "@pos_epson_printer/js/epson_printer";
import { patch } from "@web/core/utils/patch";
patch(PosGlobalState.prototype, "pos_epson_printer.PosGlobalState", {
patch(PosStore.prototype, "pos_epson_printer.PosStore", {
after_load_server_data() {
var self = this;
return this._super(...arguments).then(function () {
+8 -10
View File
@@ -5,27 +5,25 @@ import { patch } from "@web/core/utils/patch";
patch(Navbar.prototype, "pos_hr.Navbar", {
get showCashMoveButton() {
const { cashier } = this.pos.globalState;
const { cashier } = this.pos;
return this._super(...arguments) && (!cashier || cashier.role == "manager");
},
get showCloseSessionButton() {
return (
!this.pos.globalState.config.module_pos_hr ||
(this.pos.globalState.get_cashier().role === "manager" &&
this.pos.globalState.get_cashier().user_id) ||
this.pos.globalState.get_cashier_user_id() === this.pos.globalState.user.id
!this.pos.config.module_pos_hr ||
(this.pos.get_cashier().role === "manager" && this.pos.get_cashier().user_id) ||
this.pos.get_cashier_user_id() === this.pos.user.id
);
},
get showBackendButton() {
return (
!this.pos.globalState.config.module_pos_hr ||
(this.pos.globalState.get_cashier().role === "manager" &&
this.pos.globalState.get_cashier().user_id) ||
this.pos.globalState.get_cashier_user_id() === this.pos.globalState.user.id
!this.pos.config.module_pos_hr ||
(this.pos.get_cashier().role === "manager" && this.pos.get_cashier().user_id) ||
this.pos.get_cashier_user_id() === this.pos.user.id
);
},
async showLoginScreen() {
this.pos.globalState.reset_cashier();
this.pos.reset_cashier();
await this.pos.showTempScreen("LoginScreen");
},
});
+7 -7
View File
@@ -4,22 +4,22 @@
<t t-name="pos_hr.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
<xpath expr="//li[hasclass('backend-button')]" position="attributes">
<attribute name="t-if">
showBackendButton
!pos.config.module_pos_hr or pos.get_cashier().role === 'manager' or pos.get_cashier_user_id() === pos.user.id
</attribute>
</xpath>
<xpath expr="//li[hasclass('close-button')]" position="attributes">
<attribute name="t-if">
showCloseSessionButton
!pos.config.module_pos_hr or pos.get_cashier().role === 'manager' or pos.get_cashier_user_id() === pos.user.id
</attribute>
</xpath>
<xpath expr="//li[hasclass('backend-button')]" position="after">
<li t-if="pos.globalState.config.module_pos_hr" class="menu-item navbar-button lock-button" t-on-click="showLoginScreen">
<span>
Lock
</span>
</li>
<li t-if="pos.config.module_pos_hr" class="menu-item navbar-button lock-button" t-on-click="showLoginScreen">
<span>
Lock
</span>
</li>
</xpath>
</t>
+68 -3
View File
@@ -6,16 +6,81 @@ import { PosStore } from "@point_of_sale/app/store/pos_store";
patch(PosStore.prototype, "pos_hr.PosStore", {
async setup() {
await this._super(...arguments);
if (this.globalState.config.module_pos_hr) {
if (this.config.module_pos_hr) {
this.showTempScreen("LoginScreen");
}
},
async _processData(loadedData) {
await this._super(...arguments);
if (this.config.module_pos_hr) {
this.employees = loadedData["hr.employee"];
this.employee_by_id = loadedData["employee_by_id"];
this.reset_cashier();
}
},
async after_load_server_data() {
await this._super(...arguments);
if (this.config.module_pos_hr) {
this.hasLoggedIn = !this.config.module_pos_hr;
}
},
reset_cashier() {
this.cashier = {
name: null,
id: null,
barcode: null,
user_id: null,
pin: null,
role: null,
};
},
set_cashier(employee) {
this.cashier = employee;
const selectedOrder = this.get_order();
if (selectedOrder && !selectedOrder.get_orderlines().length) {
// Order without lines can be considered to be un-owned by any employee.
// We set the cashier on that order to the currently set employee.
selectedOrder.cashier = employee;
}
if (!this.cashierHasPriceControlRights() && this.numpadMode === "price") {
this.numpadMode = "quantity";
}
},
/**{name: null, id: null, barcode: null, user_id:null, pin:null}
* If pos_hr is activated, return {name: string, id: int, barcode: string, pin: string, user_id: int}
* @returns {null|*}
*/
get_cashier() {
if (this.config.module_pos_hr) {
return this.cashier;
}
return this._super(...arguments);
},
get_cashier_user_id() {
if (this.config.module_pos_hr) {
return this.cashier.user_id ? this.cashier.user_id : null;
}
return this._super(...arguments);
},
async logEmployeeMessage(action, message) {
if (!this.config.module_pos_hr) {
this._super(...arguments);
return;
}
await this.orm.call("pos.session", "log_partner_message", [
this.pos_session.id,
this.cashier.work_contact_id,
action,
message,
]);
},
/**
* @override
*/
shouldShowCashControl() {
if (this.globalState.config.module_pos_hr) {
return this._super(...arguments) && this.globalState.hasLoggedIn;
if (this.config.module_pos_hr) {
return this._super(...arguments) && this.hasLoggedIn;
}
return this._super(...arguments);
},
+3 -3
View File
@@ -11,15 +11,15 @@ patch(CashierName.prototype, "pos_hr.CashierName", {
},
//@Override
get avatar() {
if (this.pos.globalState.config.module_pos_hr) {
const cashier = this.pos.globalState.get_cashier();
if (this.pos.config.module_pos_hr) {
const cashier = this.pos.get_cashier();
return `/web/image/hr.employee/${cashier.id}/avatar_128`;
}
return this._super(...arguments);
},
//@Override
get cssClass() {
if (this.pos.globalState.config.module_pos_hr) {
if (this.pos.config.module_pos_hr) {
return { oe_status: true };
}
return this._super(...arguments);
+11
View File
@@ -0,0 +1,11 @@
/** @odoo-module */
import { Chrome } from "@point_of_sale/app/pos_app";
import { patch } from "@web/core/utils/patch";
patch(Chrome.prototype, "pos_hr.Chrome", {
get showCashMoveButton() {
const { cashier } = this.pos;
return this._super(...arguments) && (!cashier || cashier.role == "manager");
},
});
@@ -10,7 +10,7 @@ export class HeaderLockButton extends Component {
this.pos = usePos();
}
async showLoginScreen() {
this.pos.globalState.reset_cashier();
this.pos.reset_cashier();
await this.pos.showTempScreen("LoginScreen");
}
}
+2 -2
View File
@@ -19,12 +19,12 @@ export class LoginScreen extends Component {
back() {
this.props.resolve({ confirmed: false, payload: false });
this.pos.closeTempScreen();
this.pos.globalState.hasLoggedIn = true;
this.pos.hasLoggedIn = true;
this.pos.openCashControl();
}
get shopName() {
return this.pos.globalState.config.name;
return this.pos.config.name;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ import { patch } from "@web/core/utils/patch";
patch(PaymentScreen.prototype, "pos_hr.PaymentScreen", {
async _finalizeValidation() {
this.currentOrder.cashier = this.pos.globalState.get_cashier();
this.currentOrder.cashier = this.pos.get_cashier();
await this._super(...arguments);
},
});
@@ -14,19 +14,17 @@ export function useCashierSelector(
{ onCashierChanged, exclusive } = { onCashierChanged: () => {}, exclusive: false }
) {
const popup = useService("popup");
const { globalState } = usePos();
const pos = usePos();
useBarcodeReader(
{
async cashier(code) {
const employee = globalState.employees.find(
(emp) => emp.barcode === Sha1.hash(code.code)
);
const employee = pos.employees.find((emp) => emp.barcode === Sha1.hash(code.code));
if (
employee &&
employee !== globalState.get_cashier() &&
employee !== pos.get_cashier() &&
(!employee.pin || (await checkPin(employee)))
) {
globalState.set_cashier(employee);
pos.set_cashier(employee);
if (onCashierChanged) {
onCashierChanged();
}
@@ -60,9 +58,9 @@ export function useCashierSelector(
* Select a cashier, the returning value will either be an object or nothing (undefined)
*/
return async function selectCashier() {
if (globalState.config.module_pos_hr) {
const employeesList = globalState.employees
.filter((employee) => employee.id !== globalState.get_cashier().id)
if (pos.config.module_pos_hr) {
const employeesList = pos.employees
.filter((employee) => employee.id !== pos.get_cashier().id)
.map((employee) => {
return {
id: employee.id,
@@ -80,7 +78,7 @@ export function useCashierSelector(
return;
}
globalState.set_cashier(employee);
pos.set_cashier(employee);
if (onCashierChanged) {
onCashierChanged();
}
+1 -68
View File
@@ -1,75 +1,8 @@
/** @odoo-module */
import { PosGlobalState, Order } from "@point_of_sale/app/store/models";
import { Order } from "@point_of_sale/app/store/models";
import { patch } from "@web/core/utils/patch";
patch(PosGlobalState.prototype, "pos_hr.PosGlobalState", {
async _processData(loadedData) {
await this._super(...arguments);
if (this.config.module_pos_hr) {
this.employees = loadedData["hr.employee"];
this.employee_by_id = loadedData["employee_by_id"];
this.reset_cashier();
}
},
async after_load_server_data() {
await this._super(...arguments);
if (this.config.module_pos_hr) {
this.hasLoggedIn = !this.config.module_pos_hr;
}
},
reset_cashier() {
this.cashier = {
name: null,
id: null,
barcode: null,
user_id: null,
pin: null,
role: null,
};
},
set_cashier(employee) {
this.cashier = employee;
const selectedOrder = this.get_order();
if (selectedOrder && !selectedOrder.get_orderlines().length) {
// Order without lines can be considered to be un-owned by any employee.
// We set the cashier on that order to the currently set employee.
selectedOrder.cashier = employee;
}
if (!this.cashierHasPriceControlRights() && this.numpadMode === "price") {
this.numpadMode = "quantity";
}
},
/**{name: null, id: null, barcode: null, user_id:null, pin:null}
* If pos_hr is activated, return {name: string, id: int, barcode: string, pin: string, user_id: int}
* @returns {null|*}
*/
get_cashier() {
if (this.config.module_pos_hr) {
return this.cashier;
}
return this._super(...arguments);
},
get_cashier_user_id() {
if (this.config.module_pos_hr) {
return this.cashier.user_id ? this.cashier.user_id : null;
}
return this._super(...arguments);
},
async logEmployeeMessage(action, message) {
if (!this.config.module_pos_hr) {
this._super(...arguments);
return;
}
await this.orm.call("pos.session", "log_partner_message", [
this.pos_session.id,
this.cashier.work_contact_id,
action,
message,
]);
},
});
patch(Order.prototype, "pos_hr.Order", {
setup(options) {
this._super(...arguments);
+211 -21
View File
@@ -6,13 +6,18 @@ import { _t } from "@web/core/l10n/translation";
import { SelectionPopup } from "@point_of_sale/app/utils/input_popups/selection_popup";
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
import { TextInputPopup } from "@point_of_sale/app/utils/input_popups/text_input_popup";
import { Domain, InvalidDomainError } from "@web/core/domain";
import { sprintf } from "@web/core/utils/strings";
import { PosLoyaltyCard } from "@pos_loyalty/js/Loyalty";
const COUPON_CACHE_MAX_SIZE = 4096; // Maximum coupon cache size, prevents long run memory issues and (to some extent) invalid data
patch(PosStore.prototype, "pos_loyalty.PosStore", {
async addProductFromUi(product, options) {
const _super = this._super;
const order = this.globalState.get_order();
const linkedProgramIds = this.globalState.productId2ProgramIds[product.id] || [];
const linkedPrograms = linkedProgramIds.map((id) => this.globalState.program_by_id[id]);
const order = this.get_order();
const linkedProgramIds = this.productId2ProgramIds[product.id] || [];
const linkedPrograms = linkedProgramIds.map((id) => this.program_by_id[id]);
let selectedProgram = null;
if (linkedPrograms.length > 1) {
const { confirmed, payload: program } = await this.popup.add(SelectionPopup, {
@@ -32,7 +37,7 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
} else if (linkedPrograms.length === 1) {
selectedProgram = linkedPrograms[0];
}
const orderTotal = this.globalState.get_order().get_total_with_tax();
const orderTotal = this.get_order().get_total_with_tax();
if (
selectedProgram &&
["gift_card", "ewallet"].includes(selectedProgram.program_type) &&
@@ -82,15 +87,12 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
options.eWalletGiftCardProgram = program;
// If gift card program setting is 'scan_use', ask for the code.
if (this.globalState.config.gift_card_settings == "scan_use") {
const { confirmed, payload: code } = await this.globalState.env.services.popup.add(
TextInputPopup,
{
title: _t("Generate a Gift Card"),
startingValue: "",
placeholder: _t("Enter the gift card code"),
}
);
if (this.config.gift_card_settings == "scan_use") {
const { confirmed, payload: code } = await this.env.services.popup.add(TextInputPopup, {
title: _t("Generate a Gift Card"),
startingValue: "",
placeholder: _t("Enter the gift card code"),
});
if (!confirmed) {
return false;
}
@@ -124,9 +126,7 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
options.giftCardId = giftCard.id;
}
} else {
this.globalState.env.services.pos_notification.add(
"Please enter a valid gift card code."
);
this.env.services.pos_notification.add("Please enter a valid gift card code.");
return false;
}
}
@@ -144,7 +144,7 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
* it comes with the corresponding reward product line.
*/
getPotentialFreeProductRewards() {
const order = this.globalState.get_order();
const order = this.get_order();
const allCouponPrograms = Object.values(order.couponPointChanges)
.map((pe) => {
return {
@@ -162,7 +162,7 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
);
const result = [];
for (const couponProgram of allCouponPrograms) {
const program = this.globalState.program_by_id[couponProgram.program_id];
const program = this.program_by_id[couponProgram.program_id];
const points = order._getRealCouponPoints(couponProgram.coupon_id);
const hasLine = order.orderlines.filter((line) => !line.is_reward_line).length > 0;
for (const reward of program.rewards.filter(
@@ -175,9 +175,7 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
const considerTheReward =
program.applies_on !== "both" || (program.applies_on == "both" && hasLine);
if (reward.reward_type === "product" && considerTheReward) {
const product = this.globalState.db.get_product_by_id(
reward.reward_product_ids[0]
);
const product = this.db.get_product_by_id(reward.reward_product_ids[0]);
const potentialQty = order._computePotentialFreeProductQty(
reward,
product,
@@ -196,4 +194,196 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
}
return result;
},
//@override
async _processData(loadedData) {
this.couponCache = {};
this.partnerId2CouponIds = {};
this.rewards = loadedData["loyalty.reward"] || [];
for (const reward of this.rewards) {
reward.all_discount_product_ids = new Set(reward.all_discount_product_ids);
}
await this._super(loadedData);
this.productId2ProgramIds = loadedData["product_id_to_program_ids"];
this.programs = loadedData["loyalty.program"] || []; //TODO: rename to `loyaltyPrograms` etc
this.rules = loadedData["loyalty.rule"] || [];
this._loadLoyaltyData();
},
_loadProductProduct(products) {
this._super(...arguments);
for (const reward of this.rewards) {
this.compute_discount_product_ids(reward, products);
}
this.rewards = this.rewards.filter(Boolean);
},
compute_discount_product_ids(reward, products) {
const reward_product_domain = JSON.parse(reward.reward_product_domain);
if (!reward_product_domain) {
return;
}
const domain = new Domain(reward_product_domain);
try {
products
.filter((product) => domain.contains(product))
.forEach((product) => reward.all_discount_product_ids.add(product.id));
} catch (error) {
if (!(error instanceof InvalidDomainError)) {
throw error;
}
const index = this.rewards.indexOf(reward);
if (index != -1) {
this.pos.env.services.popup.add(ErrorPopup, {
title: _t("A reward could not be loaded"),
body: sprintf(
_t(
'The reward "%s" contain an error in its domain, your domain must be compatible with the PoS client'
),
this.rewards[index].description
),
});
this.rewards[index] = null;
}
}
},
_loadLoyaltyData() {
this.program_by_id = {};
this.reward_by_id = {};
for (const program of this.programs) {
this.program_by_id[program.id] = program;
if (program.date_to) {
program.date_to = new Date(program.date_to);
}
program.rules = [];
program.rewards = [];
}
for (const rule of this.rules) {
rule.valid_product_ids = new Set(rule.valid_product_ids);
rule.program_id = this.program_by_id[rule.program_id[0]];
rule.program_id.rules.push(rule);
}
for (const reward of this.rewards) {
this.reward_by_id[reward.id] = reward;
reward.program_id = this.program_by_id[reward.program_id[0]];
reward.discount_line_product_id = this.db.get_product_by_id(
reward.discount_line_product_id[0]
);
reward.all_discount_product_ids = new Set(reward.all_discount_product_ids);
reward.program_id.rewards.push(reward);
}
},
async load_server_data() {
await this._super(...arguments);
if (this.selectedOrder) {
this.selectedOrder._updateRewards();
}
},
set_order(order) {
const result = this._super(...arguments);
// FIXME - JCB: This is a temporary fix.
// When an order is selected, it doesn't always contain the reward lines.
// And the list of active programs are not always correct. This is because
// of the use of DropPrevious in _updateRewards.
if (order) {
order._updateRewards();
}
return result;
},
/**
* Fetches `loyalty.card` records from the server and adds/updates them in our cache.
*
* @param {domain} domain For the search
* @param {int} limit Default to 1
*/
async fetchCoupons(domain, limit = 1) {
const result = await this.env.services.orm.searchRead(
"loyalty.card",
domain,
["id", "points", "code", "partner_id", "program_id"],
{ limit }
);
if (Object.keys(this.couponCache).length + result.length > COUPON_CACHE_MAX_SIZE) {
this.couponCache = {};
this.partnerId2CouponIds = {};
// Make sure that the current order has no invalid data.
if (this.selectedOrder) {
this.selectedOrder.invalidCoupons = true;
}
}
const couponList = [];
for (const dbCoupon of result) {
const coupon = new PosLoyaltyCard(
dbCoupon.code,
dbCoupon.id,
dbCoupon.program_id[0],
dbCoupon.partner_id[0],
dbCoupon.points
);
this.couponCache[coupon.id] = coupon;
this.partnerId2CouponIds[coupon.partner_id] =
this.partnerId2CouponIds[coupon.partner_id] || new Set();
this.partnerId2CouponIds[coupon.partner_id].add(coupon.id);
couponList.push(coupon);
}
return couponList;
},
/**
* Fetches a loyalty card for the given program and partner, put in cache afterwards
* if a matching card is found in the cache, that one is used instead.
* If no card is found a local only card will be created until the order is validated.
*
* @param {int} programId
* @param {int} partnerId
*/
async fetchLoyaltyCard(programId, partnerId) {
for (const coupon of Object.values(this.couponCache)) {
if (coupon.partner_id === partnerId && coupon.program_id === programId) {
return coupon;
}
}
const fetchedCoupons = await this.fetchCoupons([
["partner_id", "=", partnerId],
["program_id", "=", programId],
]);
const dbCoupon = fetchedCoupons.length > 0 ? fetchedCoupons[0] : null;
return dbCoupon || new PosLoyaltyCard(null, null, programId, partnerId, 0);
},
getLoyaltyCards(partner) {
const loyaltyCards = [];
if (this.partnerId2CouponIds[partner.id]) {
this.partnerId2CouponIds[partner.id].forEach((couponId) =>
loyaltyCards.push(this.couponCache[couponId])
);
}
return loyaltyCards;
},
addPartners(partners) {
const result = this._super(partners);
// cache the loyalty cards of the partners
for (const partner of partners) {
for (const [couponId, { code, program_id, points }] of Object.entries(
partner.loyalty_cards || {}
)) {
this.couponCache[couponId] = new PosLoyaltyCard(
code,
parseInt(couponId, 10),
program_id,
partner.id,
points
);
this.partnerId2CouponIds[partner.id] =
this.partnerId2CouponIds[partner.id] || new Set();
this.partnerId2CouponIds[partner.id].add(couponId);
}
}
return result;
},
});
@@ -23,7 +23,7 @@ export class PromoCodeButton extends Component {
if (confirmed) {
code = code.trim();
if (code !== "") {
this.pos.globalState.get_order().activateCode(code);
this.pos.get_order().activateCode(code);
}
}
}
@@ -32,7 +32,7 @@ export class PromoCodeButton extends Component {
ProductScreen.addControlButton({
component: PromoCodeButton,
condition: function () {
return this.pos.globalState.programs.some((p) =>
return this.pos.programs.some((p) =>
["coupons", "promotion", "gift_card", "promo_code"].includes(p.program_type)
);
},
@@ -11,17 +11,17 @@ export class ResetProgramsButton extends Component {
this.pos = usePos();
}
_isDisabled() {
return !this.pos.globalState.get_order().isProgramsResettable();
return !this.pos.get_order().isProgramsResettable();
}
click() {
this.pos.globalState.get_order()._resetPrograms();
this.pos.get_order()._resetPrograms();
}
}
ProductScreen.addControlButton({
component: ResetProgramsButton,
condition: function () {
return this.pos.globalState.programs.some((p) =>
return this.pos.programs.some((p) =>
["coupons", "promotion"].includes(p.program_type)
);
},
@@ -30,7 +30,7 @@ export class RewardButton extends Component {
}
_getPotentialRewards() {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
// Claimable rewards excluding those from eWallet programs.
// eWallet rewards are handled in the eWalletButton.
let rewards = [];
@@ -61,14 +61,14 @@ export class RewardButton extends Component {
* @param {Integer} coupon_id
*/
async _applyReward(reward, coupon_id, potentialQty) {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
order.disabledRewards.delete(reward.id);
const args = {};
if (reward.reward_type === "product" && reward.multi_product) {
const productsList = reward.reward_product_ids.map((product_id) => ({
id: product_id,
label: this.pos.globalState.db.get_product_by_id(product_id).display_name,
label: this.pos.db.get_product_by_id(product_id).display_name,
item: product_id,
}));
const { confirmed, payload: selectedProduct } = await this.popup.add(SelectionPopup, {
@@ -125,6 +125,6 @@ export class RewardButton extends Component {
ProductScreen.addControlButton({
component: RewardButton,
condition: function () {
return this.pos.globalState.programs.length > 0;
return this.pos.programs.length > 0;
},
});
@@ -23,11 +23,11 @@ export class eWalletButton extends Component {
return eWalletRewards;
}
_getEWalletPrograms() {
return this.pos.globalState.programs.filter((p) => p.program_type == "ewallet");
return this.pos.programs.filter((p) => p.program_type == "ewallet");
}
async _onClickWalletButton() {
const order = this.pos.globalState.get_order();
const eWalletPrograms = this.pos.globalState.programs.filter(
const order = this.pos.get_order();
const eWalletPrograms = this.pos.programs.filter(
(p) => p.program_type == "ewallet"
);
const orderTotal = order.get_total_with_tax();
@@ -50,7 +50,7 @@ export class eWalletButton extends Component {
}
}
if (selectedProgram) {
const eWalletProduct = this.pos.globalState.db.get_product_by_id(
const eWalletProduct = this.pos.db.get_product_by_id(
selectedProgram.trigger_product_ids[0]
);
this.pos.addProductFromUi(eWalletProduct, {
@@ -110,6 +110,6 @@ export class eWalletButton extends Component {
ProductScreen.addControlButton({
component: eWalletButton,
condition: function () {
return this.pos.globalState.programs.filter((p) => p.program_type == "ewallet").length > 0;
return this.pos.programs.filter((p) => p.program_type == "ewallet").length > 0;
},
});
+1 -203
View File
@@ -1,15 +1,13 @@
/** @odoo-module **/
import { Order, Orderline, PosGlobalState } from "@point_of_sale/app/store/models";
import { Order, Orderline } from "@point_of_sale/app/store/models";
import { Mutex } from "@web/core/utils/concurrency";
import concurrency from "web.concurrency";
import { roundDecimals, roundPrecision } from "@web/core/utils/numbers";
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup";
import { Domain, InvalidDomainError } from "@web/core/domain";
import { sprintf } from "@web/core/utils/strings";
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
// FIXME: Perhaps MutexedDropPrevious can be replaced by the new KeepLast.
// > This might require thorough investigation on how _updateRewards work.
@@ -17,8 +15,6 @@ import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
const dropPrevious = new concurrency.MutexedDropPrevious(); // Used for queuing reward updates
const mutex = new Mutex(); // Used for sequential cache updates
const COUPON_CACHE_MAX_SIZE = 4096; // Maximum coupon cache size, prevents long run memory issues and (to some extent) invalid data
function _newRandomRewardCode() {
return (Math.random() + 1).toString(36).substring(3);
}
@@ -80,204 +76,6 @@ function computeFreeQuantity(numberItems, n, m) {
return Math.floor(free + adjustment);
}
patch(PosGlobalState.prototype, "pos_loyalty.PosGlobalState", {
//@override
async _processData(loadedData) {
this.couponCache = {};
this.partnerId2CouponIds = {};
this.rewards = loadedData["loyalty.reward"] || [];
for (const reward of this.rewards) {
reward.all_discount_product_ids = new Set(reward.all_discount_product_ids);
}
await this._super(loadedData);
this.productId2ProgramIds = loadedData["product_id_to_program_ids"];
this.programs = loadedData["loyalty.program"] || []; //TODO: rename to `loyaltyPrograms` etc
this.rules = loadedData["loyalty.rule"] || [];
this._loadLoyaltyData();
},
_loadProductProduct(products) {
this._super(...arguments);
for (const reward of this.rewards) {
this.compute_discount_product_ids(reward, products);
}
this.rewards = this.rewards.filter(Boolean);
},
compute_discount_product_ids(reward, products) {
const reward_product_domain = JSON.parse(reward.reward_product_domain);
if (!reward_product_domain) {
return;
}
const domain = new Domain(reward_product_domain);
try {
products
.filter((product) => domain.contains(product))
.forEach((product) => reward.all_discount_product_ids.add(product.id));
} catch (error) {
if (!(error instanceof InvalidDomainError)) {
throw error;
}
const index = this.rewards.indexOf(reward);
if (index != -1) {
this.pos.env.services.popup.add(ErrorPopup, {
title: _t("A reward could not be loaded"),
body: sprintf(
_t(
'The reward "%s" contain an error in its domain, your domain must be compatible with the PoS client'
),
this.rewards[index].description
),
});
this.rewards[index] = null;
}
}
},
_loadLoyaltyData() {
this.program_by_id = {};
this.reward_by_id = {};
for (const program of this.programs) {
this.program_by_id[program.id] = program;
if (program.date_from) {
program.date_from = new Date(program.date_from);
}
if (program.date_to) {
program.date_to = new Date(program.date_to);
}
program.rules = [];
program.rewards = [];
}
for (const rule of this.rules) {
rule.valid_product_ids = new Set(rule.valid_product_ids);
rule.program_id = this.program_by_id[rule.program_id[0]];
rule.program_id.rules.push(rule);
}
for (const reward of this.rewards) {
this.reward_by_id[reward.id] = reward;
reward.program_id = this.program_by_id[reward.program_id[0]];
reward.discount_line_product_id = this.db.get_product_by_id(
reward.discount_line_product_id[0]
);
reward.all_discount_product_ids = new Set(reward.all_discount_product_ids);
reward.program_id.rewards.push(reward);
}
},
async load_server_data() {
await this._super(...arguments);
if (this.selectedOrder) {
this.selectedOrder._updateRewards();
}
},
set_order(order) {
const result = this._super(...arguments);
// FIXME - JCB: This is a temporary fix.
// When an order is selected, it doesn't always contain the reward lines.
// And the list of active programs are not always correct. This is because
// of the use of DropPrevious in _updateRewards.
if (order) {
order._updateRewards();
}
return result;
},
/**
* Fetches `loyalty.card` records from the server and adds/updates them in our cache.
*
* @param {domain} domain For the search
* @param {int} limit Default to 1
*/
async fetchCoupons(domain, limit = 1) {
const result = await this.env.services.orm.searchRead(
"loyalty.card",
domain,
["id", "points", "code", "partner_id", "program_id"],
{ limit }
);
if (Object.keys(this.couponCache).length + result.length > COUPON_CACHE_MAX_SIZE) {
this.couponCache = {};
this.partnerId2CouponIds = {};
// Make sure that the current order has no invalid data.
if (this.selectedOrder) {
this.selectedOrder.invalidCoupons = true;
}
}
const couponList = [];
for (const dbCoupon of result) {
const coupon = new PosLoyaltyCard(
dbCoupon.code,
dbCoupon.id,
dbCoupon.program_id[0],
dbCoupon.partner_id[0],
dbCoupon.points
);
this.couponCache[coupon.id] = coupon;
this.partnerId2CouponIds[coupon.partner_id] =
this.partnerId2CouponIds[coupon.partner_id] || new Set();
this.partnerId2CouponIds[coupon.partner_id].add(coupon.id);
couponList.push(coupon);
}
return couponList;
},
/**
* Fetches a loyalty card for the given program and partner, put in cache afterwards
* if a matching card is found in the cache, that one is used instead.
* If no card is found a local only card will be created until the order is validated.
*
* @param {int} programId
* @param {int} partnerId
*/
async fetchLoyaltyCard(programId, partnerId) {
for (const coupon of Object.values(this.couponCache)) {
if (coupon.partner_id === partnerId && coupon.program_id === programId) {
return coupon;
}
}
const fetchedCoupons = await this.fetchCoupons([
["partner_id", "=", partnerId],
["program_id", "=", programId],
]);
const dbCoupon = fetchedCoupons.length > 0 ? fetchedCoupons[0] : null;
return dbCoupon || new PosLoyaltyCard(null, null, programId, partnerId, 0);
},
getLoyaltyCards(partner) {
const loyaltyCards = [];
if (this.partnerId2CouponIds[partner.id]) {
this.partnerId2CouponIds[partner.id].forEach((couponId) =>
loyaltyCards.push(this.couponCache[couponId])
);
}
return loyaltyCards;
},
addPartners(partners) {
const result = this._super(partners);
// cache the loyalty cards of the partners
for (const partner of partners) {
for (const [couponId, { code, program_id, points }] of Object.entries(
partner.loyalty_cards || {}
)) {
this.couponCache[couponId] = new PosLoyaltyCard(
code,
parseInt(couponId, 10),
program_id,
partner.id,
points
);
this.partnerId2CouponIds[partner.id] =
this.partnerId2CouponIds[partner.id] || new Set();
this.partnerId2CouponIds[partner.id].add(couponId);
}
}
return result;
},
});
patch(Orderline.prototype, "pos_loyalty.Orderline", {
export_as_JSON() {
const result = this._super(...arguments);
@@ -5,7 +5,7 @@ import { patch } from "@web/core/utils/patch";
patch(OrderSummary.prototype, "pos_loyalty.OrderSummary", {
getLoyaltyPoints() {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
return order.getLoyaltyPoints();
},
});
@@ -11,9 +11,9 @@ patch(Orderline.prototype, "pos_loyalty.Orderline", {
);
},
_isGiftCardOrEWalletReward() {
const coupon = this.pos.globalState.couponCache[this.props.line.coupon_id];
const coupon = this.pos.couponCache[this.props.line.coupon_id];
if (coupon) {
const program = this.pos.globalState.program_by_id[coupon.program_id];
const program = this.pos.program_by_id[coupon.program_id];
return (
["ewallet", "gift_card"].includes(program.program_type) &&
this.props.line.is_reward_line
@@ -22,7 +22,7 @@ patch(Orderline.prototype, "pos_loyalty.Orderline", {
return false;
},
_getGiftCardOrEWalletBalance() {
const coupon = this.pos.globalState.couponCache[this.props.line.coupon_id];
const coupon = this.pos.couponCache[this.props.line.coupon_id];
if (coupon) {
return this.env.utils.formatCurrency(coupon.balance);
}
@@ -12,7 +12,7 @@ patch(PartnerLine.prototype, "pos_loyalty.PartnerLine", {
this.pos = usePos();
},
_getLoyaltyPointsRepr(loyaltyCard) {
const program = this.pos.globalState.program_by_id[loyaltyCard.program_id];
const program = this.pos.program_by_id[loyaltyCard.program_id];
if (program.program_type === "ewallet") {
return `${program.name}: ${this.env.utils.formatCurrency(loyaltyCard.balance)}`;
}
@@ -41,7 +41,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
[[], pointChanges, newCodes]
);
// Payload may contain the points of the concerned coupons to be updated in case of error. (So that rewards can be corrected)
const { couponCache } = this.pos.globalState;
const { couponCache } = this.pos;
if (payload && payload.updated_points) {
for (const pointChange of Object.entries(payload.updated_points)) {
if (couponCache[pointChange[0]]) {
@@ -80,7 +80,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
async _postPushOrderResolve(order, server_ids) {
// Compile data for our function
const _super = this._super;
const { program_by_id, reward_by_id, couponCache } = this.pos.globalState;
const { program_by_id, reward_by_id, couponCache } = this.pos;
const rewardLines = order._get_reward_lines();
const partner = order.get_partner();
let couponData = Object.values(order.couponPointChanges).reduce((agg, pe) => {
@@ -41,7 +41,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
!selectedLine.manual_reward &&
(key === "Backspace" || key === "Delete")
) {
const reward = this.pos.globalState.reward_by_id[selectedLine.reward_id];
const reward = this.pos.reward_by_id[selectedLine.reward_id];
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: this.env._t("Deactivating reward"),
body: sprintf(
@@ -87,7 +87,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
}
if (selectedLine.is_reward_line && val === "remove") {
this.currentOrder.disabledRewards.add(selectedLine.reward_id);
const { couponCache } = this.pos.globalState;
const { couponCache } = this.pos;
const coupon = couponCache[selectedLine.coupon_id];
if (
coupon &&
@@ -43,12 +43,12 @@ patch(TicketScreen.prototype, "pos_loyalty.TicketScreen", {
);
},
_isEWalletGiftCard(orderline) {
const linkedProgramIds = this.pos.globalState.productId2ProgramIds[orderline.product.id];
const linkedProgramIds = this.pos.productId2ProgramIds[orderline.product.id];
if (linkedProgramIds) {
return linkedProgramIds.length > 0;
}
if (orderline.is_reward_line) {
const reward = this.pos.globalState.reward_by_id[orderline.reward_id];
const reward = this.pos.reward_by_id[orderline.reward_id];
const program = reward && reward.program_id;
if (program && ["gift_card", "ewallet"].includes(program.program_type)) {
return true;
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.eWalletButton" owl="1">
<t t-set="_order" t-value="pos.globalState.get_order()" />
<t t-set="_order" t-value="pos.get_order()" />
<t t-set="_orderTotal" t-value="_order.get_total_with_tax()" />
<t t-set="_eWalletPrograms" t-value="_getEWalletPrograms()" />
<t t-set="_eWalletRewards" t-value="_getEWalletRewards(_order)" />
@@ -3,7 +3,7 @@
<t t-name="pos_loyalty.PartnerLine" t-inherit="point_of_sale.PartnerLine" t-inherit-mode="extension" owl="1">
<xpath expr="//td[hasclass('partner-line-balance')]" position="inside">
<t t-set="_loyaltyCards" t-value="pos.globalState.getLoyaltyCards(props.partner)" />
<t t-set="_loyaltyCards" t-value="pos.getLoyaltyCards(props.partner)" />
<t t-foreach="_loyaltyCards" t-as="_loyaltyCard" t-key="_loyaltyCard.id">
<div class="pos-right-align">
<t t-esc="_getLoyaltyPointsRepr(_loyaltyCard)"/>
@@ -96,7 +96,7 @@ const lookUpCodeTransaction = {
patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
setup() {
this._super(...arguments);
if (this.pos.globalState.getOnlinePaymentMethods().length !== 0) {
if (this.pos.getOnlinePaymentMethods().length !== 0) {
useBarcodeReader({
credit: this.credit_code_action,
});
@@ -123,7 +123,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
},
_get_swipe_pending_line() {
var i = 0;
var lines = this.pos.globalState.get_order().get_paymentlines();
var lines = this.pos.get_order().get_paymentlines();
for (i = 0; i < lines.length; i++) {
if (lines[i].mercury_swipe_pending) {
@@ -135,7 +135,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
},
_does_credit_payment_line_exist(amount, card_number, card_brand, card_owner_name) {
var i = 0;
var lines = this.pos.globalState.get_order().get_paymentlines();
var lines = this.pos.get_order().get_paymentlines();
for (i = 0; i < lines.length; i++) {
if (
@@ -191,8 +191,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
},
// Handler to manage the card reader string
credit_code_transaction(parsed_result, old_deferred, retry_nr) {
const { globalState } = this.pos.globalState;
const order = globalState.get_order();
const order = this.pos.get_order();
if (order.get_due(order.selected_paymentline) < 0) {
this.popup.add(ErrorPopup, {
title: this.env._t("Refunds not supported"),
@@ -203,11 +202,11 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
return;
}
if (globalState.getOnlinePaymentMethods().length === 0) {
if (this.pos.getOnlinePaymentMethods().length === 0) {
return;
}
var decodedMagtek = globalState.decodeMagtek(parsed_result.code);
var decodedMagtek = this.pos.decodeMagtek(parsed_result.code);
if (!decodedMagtek) {
this.popup.add(ErrorPopup, {
@@ -225,7 +224,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
if (swipe_pending_line) {
purchase_amount = swipe_pending_line.get_amount();
} else {
purchase_amount = globalState.get_order().get_due();
purchase_amount = this.pos.get_order().get_due();
}
var transaction = {
@@ -233,7 +232,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
encrypted_block: decodedMagtek["encrypted_block"],
transaction_type: "Credit",
transaction_code: "Sale",
invoice_no: globalState.get_order().uid.replace(/-/g, ""),
invoice_no: this.pos.get_order().uid.replace(/-/g, ""),
purchase: purchase_amount,
payment_method_id: parsed_result.payment_method_id,
};
@@ -283,7 +282,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
return;
}
var response = globalState.decodeMercuryResponse(data);
var response = this.pos.decodeMercuryResponse(data);
response.payment_method_id = parsed_result.payment_method_id;
if (response.status === "Approved") {
@@ -303,7 +302,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
});
} else {
// If the payment is approved, add a payment line
var order = globalState.get_order();
var order = this.pos.get_order();
if (swipe_pending_line) {
order.select_paymentline(swipe_pending_line);
@@ -379,7 +378,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
return;
},
credit_code_action(parsed_result) {
var online_payment_methods = this.pos.globalState.getOnlinePaymentMethods();
var online_payment_methods = this.pos.getOnlinePaymentMethods();
if (online_payment_methods.length === 1) {
parsed_result.payment_method_id = online_payment_methods[0].item;
@@ -408,7 +407,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
}
},
remove_paymentline_by_ref(line) {
this.pos.globalState.get_order().remove_paymentline(line);
this.pos.get_order().remove_paymentline(line);
this.numberBuffer.reset();
},
do_reversal(line, is_voidsale, old_deferred, retry_nr) {
@@ -467,7 +466,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
return;
}
var response = self.pos.globalState.decodeMercuryResponse(data);
var response = self.pos.decodeMercuryResponse(data);
if (!is_voidsale) {
if (response.status != "Approved" || response.message != "REVERSED") {
@@ -520,7 +519,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
* @override
*/
addNewPaymentLine(paymentMethod) {
const order = this.pos.globalState.get_order();
const order = this.pos.get_order();
const res = this._super(...arguments);
if (res && paymentMethod.pos_mercury_config_id) {
order.selected_paymentline.mercury_swipe_pending = true;
@@ -1,9 +1,10 @@
/** @odoo-module */
import { PosGlobalState, Order, Payment } from "@point_of_sale/app/store/models";
import { PosStore } from "@point_of_sale/app/store/pos_store";
import { Order, Payment } from "@point_of_sale/app/store/models";
import { patch } from "@web/core/utils/patch";
patch(PosGlobalState.prototype, "pos_mercury.PosGlobalState", {
patch(PosStore.prototype, "pos_mercury.PosStore", {
getOnlinePaymentMethods() {
var online_payment_methods = [];
@@ -44,7 +44,7 @@ export class EditableTable extends Component {
}
onMoveStart() {
if (this.pos.globalState.floorPlanStyle == "kanban") {
if (this.pos.floorPlanStyle == "kanban") {
return;
}
this.startTable = { ...this.props.table };
@@ -60,7 +60,7 @@ export class EditableTable extends Component {
}
onMove({ dx, dy }) {
if (this.pos.globalState.floorPlanStyle == "kanban") {
if (this.pos.floorPlanStyle == "kanban") {
return;
}
const { minX, minY, maxX, maxY } = getLimits(this.root.el, this.props.limit.el);
@@ -76,7 +76,7 @@ export class EditableTable extends Component {
}
onResizeHandleMove([moveX, moveY], { dx, dy }) {
if (this.pos.globalState.floorPlanStyle == "kanban") {
if (this.pos.floorPlanStyle == "kanban") {
return;
}
// Working with min/max x and y makes constraints much easier to apply uniformly
@@ -128,7 +128,7 @@ export class EditableTable extends Component {
_setElementStyle() {
const table = this.props.table;
if (this.pos.globalState.floorPlanStyle == "kanban") {
if (this.pos.floorPlanStyle == "kanban") {
const floor = table.floor;
const index = floor.tables.indexOf(table);
const minWidth = 100 + 20;
@@ -9,7 +9,7 @@
<span class="table-seats">
<t t-esc="props.table.seats" />
</span>
<t t-if="pos.globalState.floorPlanStyle != 'kanban'" t-foreach="handles" t-as="handle" t-key="handle">
<t t-if="pos.floorPlanStyle != 'kanban'" t-foreach="handles" t-as="handle" t-key="handle">
<span class="table-handle" t-ref="{{handle}}" t-att-style="computeHandleStyle(handle)"/>
</t>
</div>
@@ -35,7 +35,7 @@ export class FloorScreen extends Component {
this.pos = usePos();
this.popup = useService("popup");
this.orm = useService("orm");
const floor = this.props.floor || this.pos.globalState.floors[0];
const floor = this.props.floor || this.pos.floors[0];
this.state = useState({
selectedFloorId: floor ? floor.id : null,
selectedTableIds: [],
@@ -44,7 +44,7 @@ export class FloorScreen extends Component {
isColorPicker: false,
});
const ui = useState(useService("ui"));
this.pos.globalState.floorPlanStyle = ui.isSmall ? "kanban" : "default";
this.pos.floorPlanStyle = ui.isSmall ? "kanban" : "default";
this.floorMapRef = useRef("floor-map-ref");
this.addFloorRef = useRef("add-floor-ref");
this.map = useRef("map");
@@ -56,7 +56,7 @@ export class FloorScreen extends Component {
onPatched() {
this.floorMapRef.el.style.background = this.state.floorBackground;
this.addFloorRef.el.style.background = this.state.floorBackground;
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
if (!this.pos.isEditMode && this.pos.floors.length > 0) {
this.addFloorRef.el.style.display = "none";
} else {
this.addFloorRef.el.style.display = "initial";
@@ -64,9 +64,9 @@ export class FloorScreen extends Component {
this.state.floorMapScrollTop = this.floorMapRef.el.getBoundingClientRect().top;
}
async onWillStart() {
const table = this.pos.globalState.table;
const table = this.pos.table;
if (table) {
const orders = this.pos.globalState.get_order_list();
const orders = this.pos.get_order_list();
const tableOrders = orders.filter(
(order) => order.tableId === table.id && !order.finalized
);
@@ -84,13 +84,13 @@ export class FloorScreen extends Component {
table.changes_count = qtyChange.changed;
table.skip_changes = qtyChange.skipped;
}
await this.pos.globalState.unsetTable();
await this.pos.unsetTable();
}
onMounted() {
this.pos.openCashControl();
this.floorMapRef.el.style.background = this.state.floorBackground;
this.addFloorRef.el.style.background = this.state.floorBackground;
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
if (!this.pos.isEditMode && this.pos.floors.length > 0) {
this.addFloorRef.el.style.display = "none";
} else {
this.addFloorRef.el.style.display = "initial";
@@ -272,14 +272,14 @@ export class FloorScreen extends Component {
delete tableCopy.floor;
const tableId = await this.orm.call("restaurant.table", "create_from_ui", [tableCopy]);
table.id = tableId;
this.pos.globalState.tables_by_id[tableId] = table;
this.pos.tables_by_id[tableId] = table;
}
async _renameFloor(floorId, newName) {
await this.orm.call("restaurant.floor", "rename_floor", [floorId, newName]);
}
get activeFloor() {
return this.state.selectedFloorId
? this.pos.globalState.floors_by_id[this.state.selectedFloorId]
? this.pos.floors_by_id[this.state.selectedFloorId]
: null;
}
get activeTables() {
@@ -291,12 +291,12 @@ export class FloorScreen extends Component {
get selectedTables() {
const tables = [];
this.state.selectedTableIds.forEach((id) => {
tables.push(this.pos.globalState.tables_by_id[id]);
tables.push(this.pos.tables_by_id[id]);
});
return tables;
}
get nbrFloors() {
return this.pos.globalState.floors.length;
return this.pos.floors.length;
}
movePinch(hypot) {
const delta = hypot / this.scalehypot;
@@ -324,8 +324,8 @@ export class FloorScreen extends Component {
this.state.selectedTableIds = [];
}
toggleEditMode() {
this.pos.globalState.toggleEditMode();
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
this.pos.toggleEditMode();
if (!this.pos.isEditMode && this.pos.floors.length > 0) {
this.addFloorRef.el.style.display = "none";
} else {
this.addFloorRef.el.style.display = "initial";
@@ -333,8 +333,7 @@ export class FloorScreen extends Component {
this.state.selectedTableIds = [];
}
async onSelectTable(table, ev) {
const { globalState } = this.pos;
if (globalState.isEditMode) {
if (this.pos.isEditMode) {
if (ev.ctrlKey || ev.metaKey) {
this.state.selectedTableIds.push(table.id);
} else {
@@ -342,11 +341,11 @@ export class FloorScreen extends Component {
this.state.selectedTableIds.push(table.id);
}
} else {
if (globalState.orderToTransfer) {
await globalState.transferTable(table);
if (this.pos.orderToTransfer) {
await this.pos.transferTable(table);
} else {
try {
await globalState.setTable(table);
await this.pos.setTable(table);
} catch (e) {
if (!(e instanceof ConnectionLostError)) {
throw e;
@@ -355,7 +354,7 @@ export class FloorScreen extends Component {
Promise.reject(e);
}
}
const order = globalState.get_order();
const order = this.pos.get_order();
this.pos.showScreen(order.get_screen_data().name);
}
}
@@ -369,16 +368,15 @@ export class FloorScreen extends Component {
if (!confirmed) {
return;
}
const { globalState } = this.pos;
const floor = await this.orm.call("restaurant.floor", "create_from_ui", [
newName,
"#ACADAD",
globalState.config.id,
this.pos.config.id,
]);
globalState.floors_by_id[floor.id] = floor;
globalState.floors.push(floor);
this.pos.floors_by_id[floor.id] = floor;
this.pos.floors.push(floor);
this.selectFloor(floor);
globalState.isEditMode = true;
this.pos.isEditMode = true;
}
async createTable() {
const newTable = await this._createTableHelper();
@@ -389,17 +387,16 @@ export class FloorScreen extends Component {
}
async duplicateTableOrFloor() {
if (this.selectedTables.length == 0) {
const { globalState } = this.pos;
const floor = this.activeFloor;
const tables = this.activeFloor.tables;
const newFloorName = floor.name + " (copy)";
const newFloor = await this.orm.call("restaurant.floor", "create_from_ui", [
newFloorName,
floor.background_color,
globalState.config.id,
this.pos.config.id,
]);
globalState.floors_by_id[newFloor.id] = newFloor;
globalState.floors.push(newFloor);
this.pos.floors_by_id[newFloor.id] = newFloor;
this.pos.floors.push(newFloor);
this.selectFloor(newFloor);
for (const table of tables) {
await this._createTableHelper(table, true);
@@ -506,7 +503,6 @@ export class FloorScreen extends Component {
this.state.isColorPicker = !this.state.isColorPicker;
}
async deleteFloorOrTable() {
const { globalState } = this.pos;
if (this.selectedTables.length == 0) {
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: `Removing floor ${this.activeFloor.name}`,
@@ -521,28 +517,28 @@ export class FloorScreen extends Component {
const originalSelectedFloorId = this.activeFloor.id;
await this.orm.call("restaurant.floor", "deactivate_floor", [
originalSelectedFloorId,
globalState.pos_session.id,
this.pos.pos_session.id,
]);
const floor = globalState.floors_by_id[originalSelectedFloorId];
const orderList = [...globalState.get_order_list()];
const floor = this.pos.floors_by_id[originalSelectedFloorId];
const orderList = [...this.pos.get_order_list()];
for (const order of orderList) {
if (floor.table_ids.includes(order.tableId)) {
globalState.removeOrder(order, false);
this.pos.removeOrder(order, false);
}
}
floor.table_ids.forEach((tableId) => {
delete globalState.tables_by_id[tableId];
delete this.pos.tables_by_id[tableId];
});
delete globalState.floors_by_id[originalSelectedFloorId];
globalState.floors = globalState.floors.filter(
delete this.pos.floors_by_id[originalSelectedFloorId];
this.pos.floors = this.pos.floors.filter(
(floor) => floor.id != originalSelectedFloorId
);
globalState.TICKET_SCREEN_STATE.syncedOrders.cache = {};
if (globalState.floors.length > 0) {
this.selectFloor(globalState.floors[0]);
this.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {};
if (this.pos.floors.length > 0) {
this.selectFloor(this.pos.floors[0]);
} else {
globalState.isEditMode = false;
globalState.floorPlanStyle = "default";
this.pos.isEditMode = false;
this.pos.floorPlanStyle = "default";
this.state.floorBackground = null;
}
return;
@@ -561,17 +557,17 @@ export class FloorScreen extends Component {
]);
if (!response) {
//remove order not send to server
for (const order of globalState.get_order_list()) {
for (const order of this.pos.get_order_list()) {
if (order.tableId == id) {
globalState.removeOrder(order, false);
this.pos.removeOrder(order, false);
}
}
globalState.tables_by_id[id].active = false;
this.pos.tables_by_id[id].active = false;
await this.orm.call("restaurant.table", "create_from_ui", [
{ active: false, id: id },
]);
this.activeFloor.tables = this.activeTables.filter((table) => table.id !== id);
delete globalState.tables_by_id[id];
delete this.pos.tables_by_id[id];
} else {
await this.popup.add(ErrorPopup, {
title: this.env._t("Delete Error"),
@@ -593,7 +589,7 @@ export class FloorScreen extends Component {
if (equalsCheck(this.state.selectedTableIds, originalSelectedTableIds)) {
this.state.selectedTableIds = [];
}
globalState.TICKET_SCREEN_STATE.syncedOrders.cache = {};
this.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {};
}
}
@@ -3,7 +3,7 @@
<t t-name="pos_restaurant.FloorScreen" owl="1">
<div class="floor-screen screen">
<EditBar t-if="pos.globalState.isEditMode" selectedTables="selectedTables" nbrFloors="nbrFloors"
<EditBar t-if="pos.isEditMode" selectedTables="selectedTables" nbrFloors="nbrFloors"
floorMapScrollTop="state.floorMapScrollTop" isColorPicker="state.isColorPicker" toggleColorPicker.bind="toggleColorPicker"
createTable.bind="createTable" duplicateTableOrFloor.bind="duplicateTableOrFloor" renameTable.bind="renameTable"
changeSeatsNum.bind="changeSeatsNum" changeToCircle.bind="changeToCircle" changeToSquare.bind="changeToSquare"
@@ -11,7 +11,7 @@
toggleEditMode.bind="toggleEditMode"
/>
<div class="floor-selector">
<t t-foreach="pos.globalState.floors" t-as="floor" t-key="floor.id">
<t t-foreach="pos.floors" t-as="floor" t-key="floor.id">
<span class="button button-floor" t-att-class="{ active: floor.id === state.selectedFloorId }" t-on-click="() => this.selectFloor(floor)">
<t t-esc="floor.name" />
</span>
@@ -29,7 +29,7 @@
class="floor-map"
t-ref="floor-map-ref"
>
<t t-if="pos.globalState.floors.length > 0">
<t t-if="pos.floors.length > 0">
<div t-if="isFloorEmpty" class="empty-floor" t-ref="map">
<span>No tables available,<br/>ADD your first table</span>
</div>

Some files were not shown because too many files have changed in this diff Show More