[REF] pos*: use fine grained reactivity instead of deep renders
*: l10n_ae_pos, l10n_co_pos, l10n_fr_pos_cert, l10n_gcc_pos, l10n_in_pos, point_of_sale, pos_discount, pos_hr, pos_loyalty, pos_mercury, pos_restaurant, pos_sale, pos_six The code of the pos was one of the first adopters of owl 1, as such, the code was not initially written to take full advantage of the owl reactivity system that would eventually make its way into owl 2. In order to convert the code quickly when migrating to owl 2, a big shortcut was taken: whenever something in the state of the pos changed, the entire UI would be rerendered. While this works decently well in practice, it can create confusing situations because the mental model needed to understand how components work in the pos is different from the rest of the code base. This commit changes the existing code to use the typical fine-grained reactivity model used everywhere else: components individually subscribe to the pieces of state that they use and will rerender on their own when this state changes. In order to achieve this, the pos global state has been removed from the environment and should now be access through the use of the custom hook `usePos` which will subscribe the component to the state that it reads. This also has some minor performance benefits as we only render the parts of the UI that actually need to update whenever there is a state mutation, instead of the entire UI which can be expensive in some cases (eg, the product screen will filter all products during rendering to find only the products that match the search, which is expensive when there are a lot of loaded products) closes odoo/odoo#120992 Related: odoo/enterprise#40873 Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<t t-name="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="env.pos.company.country.code == 'AE'">VAT</t>
|
||||
<t t-if="pos.globalState.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.env.pos.is_colombian_country()) {
|
||||
if (this.pos.globalState.is_colombian_country()) {
|
||||
const result = await this.orm.searchRead(
|
||||
"pos.order",
|
||||
[["id", "in", order_server_ids]],
|
||||
|
||||
@@ -9,12 +9,13 @@ patch(Chrome.prototype, "l10n_fr_pos_cert.Chrome", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
onMounted(async () => {
|
||||
if (this.env.pos.is_french_country() && this.env.pos.pos_session.start_at) {
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.is_french_country() && globalState.pos_session.start_at) {
|
||||
const now = Date.now();
|
||||
const limitDate = new Date(this.env.pos.pos_session.start_at);
|
||||
const limitDate = new Date(globalState.pos_session.start_at);
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
if (limitDate.getTime() < now) {
|
||||
const info = await this.env.pos.getClosePosInfo();
|
||||
const info = await globalState.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.env.pos.is_french_country()) {
|
||||
if (this.pos.globalState.is_french_country()) {
|
||||
const result = await this.orm.searchRead(
|
||||
"pos.order",
|
||||
[["id", "in", order_server_ids]],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//ul[hasclass('info-list')]" position="inside">
|
||||
<t t-if="env.pos.is_french_country() !== false and props.line.price_manually_set">
|
||||
<t t-if="pos.globalState.is_french_country() !== false and props.line.price_manually_set">
|
||||
<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, env.pos.dp['Product Unit of Measure'])) / Math.pow(10, env.pos.dp['Product Unit of Measure'])"/>
|
||||
<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-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="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 env.pos.company.country and env.pos.company.country.code == 'IN'">
|
||||
<t t-if="receipt.partner and pos.globalState.company.country and pos.globalState.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="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 env.pos.company.country and env.pos.company.country.code == 'IN'">
|
||||
<t t-if="line.l10n_in_hsn_code and pos.globalState.company.country and pos.globalState.company.country.code == 'IN'">
|
||||
<div class="pos-receipt-left-padding">
|
||||
<span>HSN Code: </span>
|
||||
<t t-esc="line.l10n_in_hsn_code"/>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { OrderImportPopup } from "@point_of_sale/js/Popups/OrderImportPopup";
|
||||
import { useBus, useService } from "@web/core/utils/hooks";
|
||||
|
||||
import { useEffect, useRef, useState, Component } from "@odoo/owl";
|
||||
import { usePos } from "../pos_hook";
|
||||
|
||||
export class DebugWidget extends Component {
|
||||
static components = { Transition };
|
||||
@@ -15,6 +16,7 @@ export class DebugWidget extends Component {
|
||||
static props = { state: { type: Object, shape: { showWidget: Boolean } } };
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.debug = useService("debug");
|
||||
this.popup = useService("popup");
|
||||
this.barcodeReader = useService("barcode_reader");
|
||||
@@ -29,11 +31,11 @@ export class DebugWidget extends Component {
|
||||
buffer: numberBuffer.get(),
|
||||
});
|
||||
this.root = useRef("root");
|
||||
this.pos = useState({ left: null, top: null });
|
||||
this.position = useState({ left: null, top: null });
|
||||
useEffect(
|
||||
(root) => {
|
||||
this.pos.left = root?.offsetLeft;
|
||||
this.pos.top = root?.offsetTop;
|
||||
this.position.left = root?.offsetLeft;
|
||||
this.position.top = root?.offsetTop;
|
||||
},
|
||||
() => [this.root.el]
|
||||
);
|
||||
@@ -41,12 +43,12 @@ export class DebugWidget extends Component {
|
||||
useMovable({
|
||||
ref: this.root,
|
||||
onMoveStart: () => {
|
||||
posBeforeMove = { ...this.pos };
|
||||
posBeforeMove = { ...this.position };
|
||||
},
|
||||
onMove: ({ dx, dy }) => {
|
||||
const { minX, minY, maxX, maxY } = getLimits(this.root.el, document.body);
|
||||
this.pos.left = constrain(posBeforeMove.left + dx, minX, maxX);
|
||||
this.pos.top = constrain(posBeforeMove.top + dy, minY, maxY);
|
||||
this.position.left = constrain(posBeforeMove.left + dx, minX, maxX);
|
||||
this.position.top = constrain(posBeforeMove.top + dy, minY, maxY);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -96,8 +98,8 @@ export class DebugWidget extends Component {
|
||||
),
|
||||
});
|
||||
if (confirmed) {
|
||||
this.env.pos.db.remove_all_orders();
|
||||
this.env.pos.set_synch("connected", 0);
|
||||
this.pos.globalState.db.remove_all_orders();
|
||||
this.pos.globalState.set_synch("connected", 0);
|
||||
}
|
||||
}
|
||||
async deleteUnpaidOrders() {
|
||||
@@ -108,7 +110,7 @@ export class DebugWidget extends Component {
|
||||
),
|
||||
});
|
||||
if (confirmed) {
|
||||
this.env.pos.db.remove_all_unpaid_orders();
|
||||
this.pos.globalState.db.remove_all_unpaid_orders();
|
||||
window.location = "/";
|
||||
}
|
||||
}
|
||||
@@ -122,7 +124,7 @@ export class DebugWidget extends Component {
|
||||
// The implementation can be better.
|
||||
preparePaidOrders() {
|
||||
try {
|
||||
this.paidOrdersBlob = this._createBlob(this.env.pos.export_paid_orders());
|
||||
this.paidOrdersBlob = this._createBlob(this.pos.globalState.export_paid_orders());
|
||||
this.state.isPaidOrdersReady = true;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
@@ -138,7 +140,7 @@ export class DebugWidget extends Component {
|
||||
// FIXME POSREF why is this two steps?
|
||||
prepareUnpaidOrders() {
|
||||
try {
|
||||
this.unpaidOrdersBlob = this._createBlob(this.env.pos.export_unpaid_orders());
|
||||
this.unpaidOrdersBlob = this._createBlob(this.pos.globalState.export_unpaid_orders());
|
||||
this.state.isUnpaidOrdersReady = true;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
@@ -154,7 +156,7 @@ export class DebugWidget extends Component {
|
||||
async importOrders(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const report = this.env.pos.import_orders(await file.text());
|
||||
const report = this.pos.globalState.import_orders(await file.text());
|
||||
await this.popup.add(OrderImportPopup, { report });
|
||||
}
|
||||
}
|
||||
@@ -168,7 +170,7 @@ export class DebugWidget extends Component {
|
||||
return `"${this.state.buffer}"`;
|
||||
}
|
||||
get style() {
|
||||
const { left, top } = this.pos;
|
||||
const { left, top } = this.position;
|
||||
return top === null ? "" : `position: absolute; left: ${left}px; top: ${top}px;`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class Navbar extends Component {
|
||||
}
|
||||
|
||||
get customerFacingDisplayButtonIsShown() {
|
||||
return this.env.pos.config.iface_customer_facing_display;
|
||||
return this.pos.globalState.config.iface_customer_facing_display;
|
||||
}
|
||||
|
||||
onCashMoveButtonClick() {
|
||||
@@ -58,14 +58,15 @@ export class Navbar extends Component {
|
||||
this.pos.closeScreen();
|
||||
} else {
|
||||
if (this._shouldLoadOrders()) {
|
||||
const { globalState } = this.pos;
|
||||
try {
|
||||
this.env.pos.setLoadingOrderState(true);
|
||||
const message = await this.env.pos._syncAllOrdersFromServer();
|
||||
globalState.setLoadingOrderState(true);
|
||||
const message = await globalState._syncAllOrdersFromServer();
|
||||
if (message) {
|
||||
this.notification.add(message, 5000);
|
||||
}
|
||||
} finally {
|
||||
this.env.pos.setLoadingOrderState(false);
|
||||
globalState.setLoadingOrderState(false);
|
||||
this.pos.showScreen("TicketScreen");
|
||||
}
|
||||
} else {
|
||||
@@ -75,7 +76,7 @@ export class Navbar extends Component {
|
||||
}
|
||||
|
||||
_shouldLoadOrders() {
|
||||
return this.env.pos.config.trusted_config_ids.length > 0;
|
||||
return this.pos.globalState.config.trusted_config_ids.length > 0;
|
||||
}
|
||||
|
||||
get isTicketScreenShown() {
|
||||
@@ -83,11 +84,7 @@ export class Navbar extends Component {
|
||||
}
|
||||
|
||||
get orderCount() {
|
||||
// FIXME POSREF: can this condition ever be false?
|
||||
if (this.env.pos) {
|
||||
return this.env.pos.get_order_list().length;
|
||||
}
|
||||
return 0;
|
||||
return this.pos.globalState.get_order_list().length;
|
||||
}
|
||||
|
||||
isBurgerMenuClosed() {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="status-buttons">
|
||||
<CashierName />
|
||||
<SaleDetailsButton t-if="hardwareProxy.printer" isHeaderButton="true"/>
|
||||
<ProxyStatus t-if="env.pos.config.use_proxy" />
|
||||
<ProxyStatus t-if="pos.globalState.config.use_proxy" />
|
||||
<CustomerFacingDisplayButton t-if="customerFacingDisplayButtonIsShown" />
|
||||
<SyncNotification />
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { batched } from "@point_of_sale/js/utils";
|
||||
import { throttleForAnimation } from "@web/core/utils/timing";
|
||||
import { Transition } from "@web/core/transition";
|
||||
import { MainComponentsContainer } from "@web/core/main_components_container";
|
||||
@@ -36,16 +35,12 @@ export class Chrome extends Component {
|
||||
get isMobile() {
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
pos: reactive(
|
||||
reactivePos,
|
||||
batched(() => this.render(true)) // FIXME POSREF remove render(true)
|
||||
),
|
||||
});
|
||||
let currentIsMobile = this.env.isMobile;
|
||||
const updateUI = throttleForAnimation(() => {
|
||||
if (this.env.isMobile !== currentIsMobile) {
|
||||
currentIsMobile = this.env.isMobile;
|
||||
// FIXME POSREF
|
||||
// FIXME POSREF use reactive mobile instead
|
||||
this.render(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
// Previously UsernameWidget
|
||||
export class CashierName extends Component {
|
||||
static template = "CashierName";
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get username() {
|
||||
const { name } = this.env.pos.get_cashier();
|
||||
const { name } = this.pos.globalState.get_cashier();
|
||||
return name ? name : "";
|
||||
}
|
||||
get avatar() {
|
||||
const user_id = this.env.pos.get_cashier_user_id();
|
||||
const user_id = this.pos.globalState.get_cashier_user_id();
|
||||
const id = user_id ? user_id : -1;
|
||||
return `/web/image/res.users/${id}/avatar_128`;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ClosePosPopup } from "@point_of_sale/js/Popups/ClosePosPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
// Previously HeaderButtonWidget
|
||||
// This is the close session button
|
||||
@@ -11,11 +12,12 @@ export class HeaderButton extends Component {
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const info = await this.env.pos.getClosePosInfo();
|
||||
const info = await this.pos.globalState.getClosePosInfo();
|
||||
this.popup.add(ClosePosPopup, { info: info, keepBehind: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ProxyStatus extends Component {
|
||||
iface_print_via_proxy,
|
||||
iface_cashdrawer,
|
||||
iface_electronic_scale,
|
||||
} = this.env.pos.config;
|
||||
} = this.pos.globalState.config;
|
||||
const devices = [
|
||||
{
|
||||
name: _t("Scanner"),
|
||||
|
||||
@@ -4,31 +4,34 @@ import { useService } from "@web/core/utils/hooks";
|
||||
import { renderToElement } from "@web/core/utils/render";
|
||||
import { ErrorPopup } from "../Popups/ErrorPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class SaleDetailsButton extends Component {
|
||||
static template = "SaleDetailsButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.orm = useService("orm");
|
||||
this.hardwareProxy = useService("hardware_proxy");
|
||||
}
|
||||
|
||||
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, [this.env.pos.pos_session.id]]
|
||||
[false, false, false, [globalState.pos_session.id]]
|
||||
);
|
||||
const report = renderToElement(
|
||||
"SaleDetailsReport",
|
||||
Object.assign({}, saleDetails, {
|
||||
date: new Date().toLocaleString(),
|
||||
pos: this.env.pos,
|
||||
pos: globalState,
|
||||
formatCurrency: this.env.utils.formatCurrency,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class SyncNotification extends Component {
|
||||
static template = "SyncNotification";
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get sync() {
|
||||
return this.pos.globalState.synch;
|
||||
}
|
||||
onClick() {
|
||||
this.env.pos.push_orders({ show_error: true });
|
||||
this.pos.globalState.push_orders({ show_error: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,11 @@ export const ControlButtonsMixin = (x) => {
|
||||
* @param {Object} controlButton
|
||||
* @param {new (...args: any[]) => Component} controlButton.component the
|
||||
* component class
|
||||
* FIXME POSREF: maybe just pass the pos explicitly to allow for arrow functions?
|
||||
* Should probably refactor this so that it's not a mixin anymore.
|
||||
* @param {Function} controlButton.condition zero argument function that is bound
|
||||
* to the instance of ProductScreen, such that `this.env.pos` can be used
|
||||
* inside the function.
|
||||
* to the instance of ProductScreen. This gives the condition acces to
|
||||
* the pos store and related object which it may need.
|
||||
* @param {Array} [controlButton.position] array of two elements
|
||||
* [locator, relativeTo]
|
||||
* locator: string -> any of ('before', 'after', 'replace')
|
||||
|
||||
@@ -11,7 +11,7 @@ export class MobileOrderWidget extends Component {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get order() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get total() {
|
||||
const _total = this.order ? this.order.get_total_with_tax() : 0;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getDataURLFromFile } from "@web/core/utils/urls";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class PartnerDetailsEdit extends Component {
|
||||
static template = "PartnerDetailsEdit";
|
||||
@@ -12,6 +13,7 @@ export class PartnerDetailsEdit extends Component {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.pos = usePos();
|
||||
this.intFields = ["country_id", "state_id", "property_product_pricelist"];
|
||||
const partner = this.props.partner;
|
||||
this.changes = {
|
||||
@@ -23,13 +25,12 @@ export class PartnerDetailsEdit extends Component {
|
||||
save: () => this.saveChanges(),
|
||||
});
|
||||
}
|
||||
// FIXME POSREF naming
|
||||
setDefaultPricelist(partner) {
|
||||
if (partner.property_product_pricelist) {
|
||||
return partner.property_product_pricelist[0];
|
||||
} else if (this.env.pos.default_pricelist) {
|
||||
return this.env.pos.default_pricelist.id;
|
||||
}
|
||||
return false;
|
||||
return this.pos.globalState.default_pricelist?.id ?? false;
|
||||
}
|
||||
|
||||
get partnerImageUrl() {
|
||||
@@ -82,8 +83,6 @@ export class PartnerDetailsEdit extends Component {
|
||||
if (loadedImage) {
|
||||
const resizedImage = await this._resizeImage(loadedImage, 800, 600);
|
||||
this.changes.image_1920 = resizedImage.toDataURL();
|
||||
// Rerender to reflect the changes in the screen
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useService } from "@web/core/utils/hooks";
|
||||
import { PartnerLine } from "./PartnerLine";
|
||||
import { PartnerDetailsEdit } from "./PartnerDetailsEdit";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { Component, onWillUnmount, useRef } from "@odoo/owl";
|
||||
import { Component, onWillUnmount, useRef, useState } from "@odoo/owl";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
|
||||
/**
|
||||
@@ -36,12 +36,7 @@ export class PartnerListScreen extends Component {
|
||||
this.notification = useService("pos_notification");
|
||||
this.searchWordInputRef = useRef("search-word-input-partner");
|
||||
|
||||
// We are not using useState here because the object
|
||||
// passed to useState converts the object and its contents
|
||||
// to Observer proxy. Not sure of the side-effects of making
|
||||
// a persistent object, such as pos, into Observer. But it
|
||||
// is better to be safe.
|
||||
this.state = {
|
||||
this.state = useState({
|
||||
query: null,
|
||||
selectedPartner: this.props.partner,
|
||||
detailIsShown: false,
|
||||
@@ -50,7 +45,7 @@ export class PartnerListScreen extends Component {
|
||||
},
|
||||
previousQuery: "",
|
||||
currentOffset: 0,
|
||||
};
|
||||
});
|
||||
this.updatePartnerList = debounce(this.updatePartnerList, 70);
|
||||
onWillUnmount(this.updatePartnerList.cancel);
|
||||
this.partnerEditor = {}; // create an imperative handle for PartnerDetailsEdit
|
||||
@@ -59,7 +54,6 @@ export class PartnerListScreen extends Component {
|
||||
back() {
|
||||
if (this.state.detailIsShown) {
|
||||
this.state.detailIsShown = false;
|
||||
this.render(true);
|
||||
} else {
|
||||
this.props.resolve({ confirmed: false, payload: false });
|
||||
this.pos.closeTempScreen();
|
||||
@@ -71,20 +65,19 @@ export class PartnerListScreen extends Component {
|
||||
}
|
||||
activateEditMode() {
|
||||
this.state.detailIsShown = true;
|
||||
this.render(true);
|
||||
}
|
||||
// Getters
|
||||
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
|
||||
get partners() {
|
||||
let res;
|
||||
if (this.state.query && this.state.query.trim() !== "") {
|
||||
res = this.env.pos.db.search_partner(this.state.query.trim());
|
||||
res = this.pos.globalState.db.search_partner(this.state.query.trim());
|
||||
} else {
|
||||
res = this.env.pos.db.get_partners_sorted(1000);
|
||||
res = this.pos.globalState.db.get_partners_sorted(1000);
|
||||
}
|
||||
res.sort(function (a, b) {
|
||||
return (a.name || "").localeCompare(b.name || "");
|
||||
@@ -134,13 +127,11 @@ export class PartnerListScreen extends Component {
|
||||
_clearSearch() {
|
||||
this.searchWordInputRef.el.value = "";
|
||||
this.state.query = "";
|
||||
this.render(true);
|
||||
}
|
||||
// We declare this event handler as a debounce function in
|
||||
// order to lower its trigger rate.
|
||||
async updatePartnerList(event) {
|
||||
this.state.query = event.target.value;
|
||||
this.render(true);
|
||||
}
|
||||
clickPartner(partner) {
|
||||
if (this.state.selectedPartner && this.state.selectedPartner.id === partner.id) {
|
||||
@@ -156,16 +147,15 @@ export class PartnerListScreen extends Component {
|
||||
}
|
||||
createPartner() {
|
||||
// initialize the edit screen with default details about country & state
|
||||
this.state.editModeProps.partner = {
|
||||
country_id: this.env.pos.company.country_id,
|
||||
state_id: this.env.pos.company.state_id,
|
||||
};
|
||||
const { country_id, state_id } = this.pos.globalState.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 this.env.pos.load_new_partners();
|
||||
this.state.selectedPartner = this.env.pos.db.get_partner_by_id(partnerId);
|
||||
await globalState.load_new_partners();
|
||||
this.state.selectedPartner = globalState.db.get_partner_by_id(partnerId);
|
||||
this.confirm();
|
||||
}
|
||||
async searchPartner() {
|
||||
@@ -173,8 +163,7 @@ export class PartnerListScreen extends Component {
|
||||
this.state.currentOffset = 0;
|
||||
}
|
||||
const result = await this.getNewPartners();
|
||||
this.env.pos.addPartners(result);
|
||||
this.render(true);
|
||||
this.pos.globalState.addPartners(result);
|
||||
if (this.state.previousQuery == this.state.query) {
|
||||
this.state.currentOffset += result.length;
|
||||
} else {
|
||||
|
||||
@@ -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.env.pos.payment_methods.filter((method) =>
|
||||
this.env.pos.config.payment_method_ids.includes(method.id)
|
||||
this.payment_methods_from_config = this.pos.globalState.payment_methods.filter((method) =>
|
||||
this.pos.globalState.config.payment_method_ids.includes(method.id)
|
||||
);
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.numberBuffer.use(this._getNumberBufferConfig);
|
||||
@@ -71,7 +71,7 @@ export class PaymentScreen extends Component {
|
||||
return config;
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get paymentLines() {
|
||||
return this.currentOrder.get_paymentlines();
|
||||
@@ -130,9 +130,7 @@ export class PaymentScreen extends Component {
|
||||
}
|
||||
}
|
||||
toggleIsToInvoice() {
|
||||
// click_invoice
|
||||
this.currentOrder.set_to_invoice(!this.currentOrder.is_to_invoice());
|
||||
this.render(true);
|
||||
}
|
||||
openCashbox() {
|
||||
this.hardwareProxy.printer.openCashbox();
|
||||
@@ -147,7 +145,7 @@ export class PaymentScreen extends Component {
|
||||
title: tip ? this.env._t("Change Tip") : this.env._t("Add Tip"),
|
||||
startingValue: value,
|
||||
isInputSelected: true,
|
||||
inputSuffix: this.env.pos.currency.symbol,
|
||||
inputSuffix: this.pos.globalState.currency.symbol,
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
@@ -167,7 +165,6 @@ export class PaymentScreen extends Component {
|
||||
}
|
||||
}
|
||||
deletePaymentLine(cid) {
|
||||
var self = this;
|
||||
const line = this.paymentLines.find((line) => line.cid === cid);
|
||||
// If a paymentline with a payment terminal linked to
|
||||
// it is removed, the terminal should get a cancel
|
||||
@@ -176,27 +173,24 @@ export class PaymentScreen extends Component {
|
||||
line.set_payment_status("waitingCancel");
|
||||
line.payment_method.payment_terminal
|
||||
.send_payment_cancel(this.currentOrder, cid)
|
||||
.then(function () {
|
||||
self.currentOrder.remove_paymentline(line);
|
||||
.then(() => {
|
||||
this.currentOrder.remove_paymentline(line);
|
||||
this.numberBuffer.reset();
|
||||
self.render(true);
|
||||
});
|
||||
} else if (line.get_payment_status() !== "waitingCancel") {
|
||||
this.currentOrder.remove_paymentline(line);
|
||||
this.numberBuffer.reset();
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
selectPaymentLine(cid) {
|
||||
const line = this.paymentLines.find((line) => line.cid === cid);
|
||||
this.currentOrder.select_paymentline(line);
|
||||
this.numberBuffer.reset();
|
||||
this.render(true);
|
||||
}
|
||||
async validateOrder(isForceValidate) {
|
||||
this.numberBuffer.capture();
|
||||
if (this.env.pos.config.cash_rounding) {
|
||||
if (!this.env.pos.get_order().check_paymentlines_rounding()) {
|
||||
if (this.pos.globalState.config.cash_rounding) {
|
||||
if (!this.pos.globalState.get_order().check_paymentlines_rounding()) {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Rounding error in payment lines"),
|
||||
body: this.env._t(
|
||||
@@ -217,9 +211,10 @@ export class PaymentScreen extends Component {
|
||||
}
|
||||
}
|
||||
async _finalizeValidation() {
|
||||
const { globalState } = this.pos;
|
||||
if (
|
||||
(this.currentOrder.is_paid_with_cash() || this.currentOrder.get_change()) &&
|
||||
this.env.pos.config.iface_cashdrawer
|
||||
globalState.config.iface_cashdrawer
|
||||
) {
|
||||
this.hardwareProxy.printer.openCashbox();
|
||||
}
|
||||
@@ -231,7 +226,7 @@ export class PaymentScreen extends Component {
|
||||
|
||||
try {
|
||||
// 1. Save order to server.
|
||||
syncOrderResult = await this.env.pos.push_single_order(this.currentOrder);
|
||||
syncOrderResult = await globalState.push_single_order(this.currentOrder);
|
||||
|
||||
// 2. Invoice.
|
||||
if (this.currentOrder.is_to_invoice()) {
|
||||
@@ -283,10 +278,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
|
||||
this.env.pos.db.remove_unpaid_order(this.currentOrder);
|
||||
globalState.db.remove_unpaid_order(this.currentOrder);
|
||||
|
||||
// Ask the user to sync the remaining unsynced orders.
|
||||
if (!hasError && syncOrderResult && this.env.pos.db.get_orders().length) {
|
||||
if (!hasError && syncOrderResult && globalState.db.get_orders().length) {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Remaining unsynced orders"),
|
||||
body: this.env._t(
|
||||
@@ -297,7 +292,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.
|
||||
this.env.pos.push_orders();
|
||||
globalState.push_orders();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,11 +399,7 @@ export class PaymentScreen extends Component {
|
||||
this.currentOrder.get_rounding_applied()
|
||||
) > 0.00001
|
||||
) {
|
||||
var cash = false;
|
||||
for (var i = 0; i < this.env.pos.payment_methods.length; i++) {
|
||||
cash = cash || this.env.pos.payment_methods[i].is_cash_count;
|
||||
}
|
||||
if (!cash) {
|
||||
if (!this.pos.globalState.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(
|
||||
@@ -472,10 +463,11 @@ 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;
|
||||
if (
|
||||
this.currentOrder.is_paid() &&
|
||||
floatIsZero(this.currentOrder.get_due(), this.env.pos.currency.decimal_places) &&
|
||||
this.env.pos.config.auto_validate_terminal_payment
|
||||
floatIsZero(this.currentOrder.get_due(), currency.decimal_places) &&
|
||||
config.auto_validate_terminal_payment
|
||||
) {
|
||||
this.validateOrder(false);
|
||||
}
|
||||
|
||||
+4
-2
@@ -4,16 +4,18 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { TextAreaPopup } from "@point_of_sale/js/Popups/TextAreaPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class OrderlineCustomerNoteButton extends Component {
|
||||
static template = "OrderlineCustomerNoteButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
async onClick() {
|
||||
const selectedOrderline = this.env.pos.get_order().get_selected_orderline();
|
||||
const selectedOrderline = this.pos.globalState.get_order().get_selected_orderline();
|
||||
// FIXME POSREF can this happen? Shouldn't the orderline just be a prop?
|
||||
if (!selectedOrderline) {
|
||||
return;
|
||||
}
|
||||
|
||||
+4
-3
@@ -4,20 +4,21 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ProductInfoPopup } from "@point_of_sale/js/Popups/ProductInfoPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class ProductInfoButton extends Component {
|
||||
static template = "ProductInfoButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
async click() {
|
||||
const orderline = this.env.pos.get_order().get_selected_orderline();
|
||||
const orderline = this.pos.globalState.get_order().get_selected_orderline();
|
||||
if (orderline) {
|
||||
const product = orderline.get_product();
|
||||
const quantity = orderline.get_quantity();
|
||||
const info = await this.env.pos.getProductInfo(product, quantity);
|
||||
const info = await this.pos.globalState.getProductInfo(product, quantity);
|
||||
this.popup.add(ProductInfoPopup, { info: info, product: product });
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -8,15 +8,15 @@ export class RefundButton extends Component {
|
||||
static template = "point_of_sale.RefundButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
}
|
||||
click() {
|
||||
const partner = this.env.pos.get_order().get_partner();
|
||||
const order = this.pos.globalState.get_order();
|
||||
const partner = order.get_partner();
|
||||
const searchDetails = partner ? { fieldName: "PARTNER", searchTerm: partner.name } : {};
|
||||
this.pos.showScreen("TicketScreen", {
|
||||
ui: { filter: "SYNCED", searchDetails },
|
||||
destinationOrder: this.env.pos.get_order(),
|
||||
destinationOrder: order,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
@@ -8,11 +9,11 @@ export class SaveButton extends Component {
|
||||
static template = "point_of_sale.SaveButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.notification = useService("pos_notification");
|
||||
}
|
||||
onClick() {
|
||||
const orderline = this.env.pos.get_order().get_selected_orderline();
|
||||
const orderline = this.pos.globalState.get_order().get_selected_orderline();
|
||||
if (!orderline) {
|
||||
this.notification.add(this.env._t("You cannot save an empty order"), 3000);
|
||||
return;
|
||||
@@ -21,13 +22,14 @@ export class SaveButton extends Component {
|
||||
this.notification.add(this.env._t("Order saved for later"), 3000);
|
||||
}
|
||||
_selectEmptyOrder() {
|
||||
const orders = this.env.pos.get_order_list();
|
||||
const { globalState } = this.pos;
|
||||
const orders = globalState.get_order_list();
|
||||
const emptyOrders = orders.filter((order) => order.is_empty());
|
||||
if (emptyOrders.length > 0) {
|
||||
this.env.pos.sendDraftToServer();
|
||||
this.env.pos.set_order(emptyOrders[0]);
|
||||
globalState.sendDraftToServer();
|
||||
globalState.set_order(emptyOrders[0]);
|
||||
} else {
|
||||
this.env.pos.add_new_order();
|
||||
globalState.add_new_order();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +37,6 @@ export class SaveButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: SaveButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.trusted_config_ids.length > 0;
|
||||
return this.pos.globalState.config.trusted_config_ids.length > 0;
|
||||
},
|
||||
});
|
||||
|
||||
+5
-4
@@ -4,16 +4,17 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class SetFiscalPositionButton extends Component {
|
||||
static template = "SetFiscalPositionButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get currentFiscalPositionName() {
|
||||
return this.currentOrder && this.currentOrder.fiscal_position
|
||||
@@ -29,7 +30,7 @@ export class SetFiscalPositionButton extends Component {
|
||||
isSelected: !currentFiscalPosition,
|
||||
},
|
||||
];
|
||||
for (const fiscalPos of this.env.pos.fiscal_positions) {
|
||||
for (const fiscalPos of this.pos.globalState.fiscal_positions) {
|
||||
fiscalPosList.push({
|
||||
id: fiscalPos.id,
|
||||
label: fiscalPos.name,
|
||||
@@ -60,7 +61,7 @@ export class SetFiscalPositionButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: SetFiscalPositionButton,
|
||||
condition: function () {
|
||||
return this.env.pos.fiscal_positions.length > 0;
|
||||
return this.pos.globalState.fiscal_positions.length > 0;
|
||||
},
|
||||
position: ["before", "SetPricelistButton"],
|
||||
});
|
||||
|
||||
+7
-5
@@ -4,16 +4,17 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class SetPricelistButton extends Component {
|
||||
static template = "SetPricelistButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get currentPricelistName() {
|
||||
const order = this.currentOrder;
|
||||
@@ -23,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.env.pos.pricelists.map((pricelist) => ({
|
||||
const selectionList = this.pos.globalState.pricelists.map((pricelist) => ({
|
||||
id: pricelist.id,
|
||||
label: pricelist.name,
|
||||
isSelected:
|
||||
@@ -31,7 +32,7 @@ export class SetPricelistButton extends Component {
|
||||
item: pricelist,
|
||||
}));
|
||||
|
||||
if (!this.env.pos.default_pricelist) {
|
||||
if (!this.pos.globalState.default_pricelist) {
|
||||
selectionList.push({
|
||||
id: null,
|
||||
label: this.env._t("Default Price"),
|
||||
@@ -54,6 +55,7 @@ export class SetPricelistButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: SetPricelistButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.use_pricelist && this.env.pos.pricelists.length > 0;
|
||||
const { config, pricelists } = this.pos.globalState;
|
||||
return config.use_pricelist && pricelists.length > 0;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
/**
|
||||
* @prop {'quantity' | 'price' | 'discount'} activeMode
|
||||
@@ -15,18 +16,20 @@ export class NumpadWidget extends Component {
|
||||
disableSign: false,
|
||||
};
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.localization = useService("localization");
|
||||
}
|
||||
get hasPriceControlRights() {
|
||||
return (
|
||||
this.env.pos.cashierHasPriceControlRights() &&
|
||||
this.pos.globalState.cashierHasPriceControlRights() &&
|
||||
!this.props.disabledModes.includes("price")
|
||||
);
|
||||
}
|
||||
get hasManualDiscount() {
|
||||
return (
|
||||
this.env.pos.config.manual_discount && !this.props.disabledModes.includes("discount")
|
||||
this.pos.globalState.config.manual_discount &&
|
||||
!this.props.disabledModes.includes("discount")
|
||||
);
|
||||
}
|
||||
changeMode(mode) {
|
||||
@@ -38,7 +41,7 @@ export class NumpadWidget extends Component {
|
||||
}
|
||||
this.numberBuffer.capture();
|
||||
this.numberBuffer.reset();
|
||||
this.env.pos.numpadMode = mode;
|
||||
this.pos.globalState.numpadMode = mode;
|
||||
}
|
||||
sendInput(key) {
|
||||
this.numberBuffer.sendKey(key);
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { floatIsZero } from "@web/core/utils/numbers";
|
||||
|
||||
export class OrderSummary extends Component {
|
||||
static template = "OrderSummary";
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
getTotal() {
|
||||
return this.env.utils.formatCurrency(this.props.order.get_total_with_tax());
|
||||
}
|
||||
@@ -14,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.env.pos.currency.decimal_places),
|
||||
hasTax: !floatIsZero(taxAmount, this.pos.globalState.currency.decimal_places),
|
||||
displayAmount: this.env.utils.formatCurrency(taxAmount),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EditListPopup } from "@point_of_sale/js/Popups/EditListPopup";
|
||||
import { Orderline } from "./Orderline";
|
||||
import { OrderSummary } from "./OrderSummary";
|
||||
import { Component, useEffect, useRef } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class OrderWidget extends Component {
|
||||
static components = { Orderline, OrderSummary };
|
||||
@@ -13,6 +14,7 @@ export class OrderWidget extends Component {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.numberBuffer = useService("number_buffer");
|
||||
this.scrollableRef = useRef("scrollable");
|
||||
@@ -29,7 +31,7 @@ export class OrderWidget extends Component {
|
||||
);
|
||||
}
|
||||
get order() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get orderlinesArray() {
|
||||
return this.order ? this.order.get_orderlines() : [];
|
||||
|
||||
@@ -18,7 +18,7 @@ export class Orderline extends Component {
|
||||
|
||||
selectLine() {
|
||||
const line = this.props.line; // the orderline
|
||||
if (this.env.pos.get_order().selected_orderline.id !== line.id) {
|
||||
if (this.pos.globalState.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();
|
||||
|
||||
@@ -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.env.pos.get_order();
|
||||
const current_order = this.pos.globalState.get_order();
|
||||
if (current_order) {
|
||||
return current_order.pricelist;
|
||||
}
|
||||
return this.env.pos.default_pricelist;
|
||||
return this.pos.globalState.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.env.pos.units_by_id[this.props.product.uom_id[0]].name
|
||||
this.pos.globalState.units_by_id[this.props.product.uom_id[0]].name
|
||||
}`;
|
||||
} else {
|
||||
return formattedUnitPrice;
|
||||
|
||||
@@ -76,21 +76,19 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
return this.currentOrder ? this.currentOrder.get_partner() : null;
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
async updateSelectedOrderline({ buffer, key }) {
|
||||
if (this.env.pos.numpadMode === "quantity" && this.env.pos.disallowLineQuantityChange()) {
|
||||
const order = this.env.pos.get_order();
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.numpadMode === "quantity" && globalState.disallowLineQuantityChange()) {
|
||||
const order = globalState.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 = this.env.pos
|
||||
.get_order()
|
||||
.get_selected_orderline()
|
||||
.get_quantity();
|
||||
const currentQuantity = globalState.get_order().get_selected_orderline().get_quantity();
|
||||
|
||||
if (selectedLine.noDecrease) {
|
||||
this.popup.add(ErrorPopup, {
|
||||
@@ -112,20 +110,21 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
this._setValue(val);
|
||||
if (val == "remove") {
|
||||
this.numberBuffer.reset();
|
||||
this.env.pos.numpadMode = "quantity";
|
||||
globalState.numpadMode = "quantity";
|
||||
}
|
||||
}
|
||||
}
|
||||
_setValue(val) {
|
||||
const { numpadMode } = this.pos.globalState;
|
||||
if (this.currentOrder.get_selected_orderline()) {
|
||||
if (this.env.pos.numpadMode === "quantity") {
|
||||
if (numpadMode === "quantity") {
|
||||
const result = this.currentOrder.get_selected_orderline().set_quantity(val);
|
||||
if (!result) {
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
} else if (this.env.pos.numpadMode === "discount") {
|
||||
} else if (numpadMode === "discount") {
|
||||
this.currentOrder.get_selected_orderline().set_discount(val);
|
||||
} else if (this.env.pos.numpadMode === "price") {
|
||||
} else if (numpadMode === "price") {
|
||||
var selected_orderline = this.currentOrder.get_selected_orderline();
|
||||
selected_orderline.price_manually_set = true;
|
||||
selected_orderline.set_unit_price(val);
|
||||
@@ -133,7 +132,8 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
}
|
||||
}
|
||||
async _barcodeProductAction(code) {
|
||||
let product = this.env.pos.db.get_product_by_barcode(code.base_code);
|
||||
const { globalState } = this.pos;
|
||||
let product = globalState.db.get_product_by_barcode(code.base_code);
|
||||
if (!product) {
|
||||
// find the barcode in the backend
|
||||
let foundProductIds = [];
|
||||
@@ -141,9 +141,9 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
["barcode", "=", code.base_code],
|
||||
]);
|
||||
if (foundProductIds.length) {
|
||||
await this.env.pos._addProducts(foundProductIds);
|
||||
await globalState._addProducts(foundProductIds);
|
||||
// assume that the result is unique.
|
||||
product = this.env.pos.db.get_product_by_id(foundProductIds[0]);
|
||||
product = globalState.db.get_product_by_id(foundProductIds[0]);
|
||||
} else {
|
||||
return this.popup.add(ErrorBarcodePopup, { code: code.base_code });
|
||||
}
|
||||
@@ -178,7 +178,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
this.numberBuffer.reset();
|
||||
}
|
||||
_barcodePartnerAction(code) {
|
||||
const partner = this.env.pos.db.get_partner_by_barcode(code.code);
|
||||
const partner = this.pos.globalState.db.get_partner_by_barcode(code.code);
|
||||
if (partner) {
|
||||
if (this.currentOrder.get_partner() !== partner) {
|
||||
this.currentOrder.set_partner(partner);
|
||||
@@ -207,8 +207,8 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
});
|
||||
const newQuantity = inputNumber && inputNumber !== "" ? parseFloat(inputNumber) : null;
|
||||
if (confirmed && newQuantity !== null) {
|
||||
const order = this.env.pos.get_order();
|
||||
const selectedLine = this.env.pos.get_order().get_selected_orderline();
|
||||
const order = this.pos.globalState.get_order();
|
||||
const selectedLine = order.get_selected_orderline();
|
||||
const currentQuantity = selectedLine.get_quantity();
|
||||
if (newQuantity >= currentQuantity) {
|
||||
selectedLine.set_quantity(newQuantity);
|
||||
@@ -230,22 +230,23 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
}
|
||||
}
|
||||
async onClickPay() {
|
||||
if (this.env.pos.get_order().server_id) {
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.get_order().server_id) {
|
||||
try {
|
||||
const isPaid = await this.orm.call("pos.order", "is_already_paid", [
|
||||
this.env.pos.get_order().server_id,
|
||||
globalState.get_order().server_id,
|
||||
]);
|
||||
if (isPaid) {
|
||||
const searchDetails = {
|
||||
fieldName: "RECEIPT_NUMBER",
|
||||
searchTerm: this.env.pos.get_order().uid,
|
||||
searchTerm: globalState.get_order().uid,
|
||||
};
|
||||
this.pos.showScreen("TicketScreen", {
|
||||
ui: { filter: "SYNCED", searchDetails },
|
||||
});
|
||||
this.notification.add(this.env._t("The order has been already paid."), 3000);
|
||||
this.env.pos.removeOrder(this.env.pos.get_order(), false);
|
||||
this.env.pos.add_new_order();
|
||||
globalState.removeOrder(globalState.get_order(), false);
|
||||
globalState.add_new_order();
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -257,14 +258,13 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
|
||||
}
|
||||
}
|
||||
if (
|
||||
this.env.pos
|
||||
globalState
|
||||
.get_order()
|
||||
.orderlines.some(
|
||||
(line) =>
|
||||
line.get_product().tracking !== "none" && !line.has_valid_product_lot()
|
||||
) &&
|
||||
(this.env.pos.picking_type.use_create_lots ||
|
||||
this.env.pos.picking_type.use_existing_lots)
|
||||
(globalState.picking_type.use_create_lots || globalState.picking_type.use_existing_lots)
|
||||
) {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Some Serial/Lot Numbers are missing"),
|
||||
|
||||
@@ -35,63 +35,63 @@ export class ProductsWidget extends Component {
|
||||
return Object.keys(this.pos.globalState.db.product_by_id).length > 0;
|
||||
}
|
||||
get selectedCategoryId() {
|
||||
return this.env.pos.selectedCategoryId;
|
||||
return this.pos.globalState.selectedCategoryId;
|
||||
}
|
||||
get searchWord() {
|
||||
return this.env.pos.searchProductWord.trim();
|
||||
return this.pos.globalState.searchProductWord.trim();
|
||||
}
|
||||
get productsToDisplay() {
|
||||
const { db } = this.pos.globalState;
|
||||
let list = [];
|
||||
if (this.searchWord !== "") {
|
||||
list = this.env.pos.db.search_product_in_category(
|
||||
this.selectedCategoryId,
|
||||
this.searchWord
|
||||
);
|
||||
list = db.search_product_in_category(this.selectedCategoryId, this.searchWord);
|
||||
} else {
|
||||
list = this.env.pos.db.get_product_by_category(this.selectedCategoryId);
|
||||
list = db.get_product_by_category(this.selectedCategoryId);
|
||||
}
|
||||
return list.sort(function (a, b) {
|
||||
return a.display_name.localeCompare(b.display_name);
|
||||
});
|
||||
}
|
||||
get subcategories() {
|
||||
return this.env.pos.db
|
||||
const { db } = this.pos.globalState;
|
||||
return db
|
||||
.get_category_childs_ids(this.selectedCategoryId)
|
||||
.map((id) => this.env.pos.db.get_category_by_id(id));
|
||||
.map((id) => db.get_category_by_id(id));
|
||||
}
|
||||
get breadcrumbs() {
|
||||
if (this.selectedCategoryId === this.env.pos.db.root_category_id) {
|
||||
const { db } = this.pos.globalState;
|
||||
if (this.selectedCategoryId === db.root_category_id) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
...this.env.pos.db.get_category_ancestors_ids(this.selectedCategoryId).slice(1),
|
||||
...db.get_category_ancestors_ids(this.selectedCategoryId).slice(1),
|
||||
this.selectedCategoryId,
|
||||
].map((id) => this.env.pos.db.get_category_by_id(id));
|
||||
].map((id) => db.get_category_by_id(id));
|
||||
}
|
||||
get hasNoCategories() {
|
||||
return this.env.pos.db.get_category_childs_ids(0).length === 0;
|
||||
return this.pos.globalState.db.get_category_childs_ids(0).length === 0;
|
||||
}
|
||||
get shouldShowButton() {
|
||||
return this.productsToDisplay.length === 0 && this.searchWord;
|
||||
}
|
||||
switchCategory(categoryId) {
|
||||
this.env.pos.setSelectedCategoryId(categoryId);
|
||||
this.pos.globalState.setSelectedCategoryId(categoryId);
|
||||
}
|
||||
updateSearch(searchWord) {
|
||||
this.env.pos.searchProductWord = searchWord;
|
||||
this.pos.globalState.searchProductWord = searchWord;
|
||||
}
|
||||
clearSearch() {
|
||||
this.env.pos.searchProductWord = "";
|
||||
this.pos.globalState.searchProductWord = "";
|
||||
}
|
||||
updateProductList(event) {
|
||||
this.render(true);
|
||||
this.switchCategory(0);
|
||||
}
|
||||
async onPressEnterKey() {
|
||||
if (!this.env.pos.searchProductWord) {
|
||||
const { searchProductWord } = this.pos.globalState;
|
||||
if (!searchProductWord) {
|
||||
return;
|
||||
}
|
||||
if (this.state.previousSearchWord != this.env.pos.searchProductWord) {
|
||||
if (this.state.previousSearchWord !== searchProductWord) {
|
||||
this.state.currentOffset = 0;
|
||||
}
|
||||
const result = await this.loadProductFromDB();
|
||||
@@ -100,28 +100,26 @@ export class ProductsWidget extends Component {
|
||||
sprintf(
|
||||
this.env._t('%s product(s) found for "%s".'),
|
||||
result.length,
|
||||
this.env.pos.searchProductWord
|
||||
searchProductWord
|
||||
),
|
||||
3000
|
||||
);
|
||||
} else {
|
||||
this.notification.add(
|
||||
sprintf(
|
||||
this.env._t('No more product found for "%s".'),
|
||||
this.env.pos.searchProductWord
|
||||
),
|
||||
sprintf(this.env._t('No more product found for "%s".'), searchProductWord),
|
||||
3000
|
||||
);
|
||||
}
|
||||
if (this.state.previousSearchWord == this.env.pos.searchProductWord) {
|
||||
if (this.state.previousSearchWord === searchProductWord) {
|
||||
this.state.currentOffset += result.length;
|
||||
} else {
|
||||
this.state.previousSearchWord = this.env.pos.searchProductWord;
|
||||
this.state.previousSearchWord = searchProductWord;
|
||||
this.state.currentOffset = result.length;
|
||||
}
|
||||
}
|
||||
async loadProductFromDB() {
|
||||
if (!this.env.pos.searchProductWord) {
|
||||
const { searchProductWord } = this.pos.globalState;
|
||||
if (!searchProductWord) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,9 +134,9 @@ export class ProductsWidget extends Component {
|
||||
["available_in_pos", "=", true],
|
||||
"|",
|
||||
"|",
|
||||
["name", "ilike", this.env.pos.searchProductWord],
|
||||
["default_code", "ilike", this.env.pos.searchProductWord],
|
||||
["barcode", "ilike", this.env.pos.searchProductWord],
|
||||
["name", "ilike", searchProductWord],
|
||||
["default_code", "ilike", searchProductWord],
|
||||
["barcode", "ilike", searchProductWord],
|
||||
],
|
||||
],
|
||||
{
|
||||
@@ -147,7 +145,7 @@ export class ProductsWidget extends Component {
|
||||
}
|
||||
);
|
||||
if (ProductIds.length) {
|
||||
await this.env.pos._addProducts(ProductIds, false);
|
||||
await this.pos.globalState._addProducts(ProductIds, false);
|
||||
}
|
||||
this.updateProductList();
|
||||
return ProductIds;
|
||||
|
||||
+6
-6
@@ -24,26 +24,26 @@ export class ProductsWidgetControlPanel extends Component {
|
||||
this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown;
|
||||
}
|
||||
_clearSearch() {
|
||||
this.env.pos.searchProductWord = "";
|
||||
this.pos.globalState.searchProductWord = "";
|
||||
this.props.clearSearch();
|
||||
}
|
||||
get displayCategImages() {
|
||||
return (
|
||||
Object.values(this.env.pos.db.category_by_id).some((categ) => categ.has_image) &&
|
||||
!this.env.isMobile
|
||||
!this.env.isMobile &&
|
||||
Object.values(this.pos.globalState.db.category_by_id).some((categ) => categ.has_image)
|
||||
);
|
||||
}
|
||||
updateSearch(event) {
|
||||
this.props.updateSearch(this.env.pos.searchProductWord);
|
||||
this.props.updateSearch(this.pos.globalState.searchProductWord);
|
||||
}
|
||||
async _onPressEnterKey() {
|
||||
if (!this.env.pos.searchProductWord) {
|
||||
if (!this.pos.globalState.searchProductWord) {
|
||||
return;
|
||||
}
|
||||
this.props.loadProductFromServer();
|
||||
}
|
||||
searchProductFromInfo(productName) {
|
||||
this.env.pos.searchProductWord = productName;
|
||||
this.pos.globalState.searchProductWord = productName;
|
||||
this.props.switchCategory(0);
|
||||
this.props.updateSearch(productName);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { WrappedProductNameLines } from "./WrappedProductNameLines";
|
||||
import { Component, onWillUpdateProps } from "@odoo/owl";
|
||||
|
||||
@@ -9,6 +10,7 @@ export class OrderReceipt extends Component {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this._receiptEnv = this.props.order.getOrderReceiptEnv();
|
||||
|
||||
onWillUpdateProps((nextProps) => {
|
||||
|
||||
@@ -22,7 +22,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.env.pos.get_order();
|
||||
this.currentOrder = this.pos.globalState.get_order();
|
||||
const partner = this.currentOrder.get_partner();
|
||||
this.orderUiState = this.currentOrder.uiState.ReceiptScreen;
|
||||
this.orderUiState.inputEmail =
|
||||
@@ -52,7 +52,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
});
|
||||
}
|
||||
_addNewOrder() {
|
||||
this.env.pos.add_new_order();
|
||||
this.pos.globalState.add_new_order();
|
||||
}
|
||||
onSendEmail() {
|
||||
if (this.buttonMailReceipt.el.classList.contains("fa-spin")) {
|
||||
@@ -92,8 +92,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
get orderAmountPlusTip() {
|
||||
const order = this.currentOrder;
|
||||
const orderTotalAmount = order.get_total_with_tax();
|
||||
const tip_product_id =
|
||||
this.env.pos.config.tip_product_id && this.env.pos.config.tip_product_id[0];
|
||||
const tip_product_id = this.pos.globalState.config.tip_product_id?.[0];
|
||||
const tipLine = order
|
||||
.get_orderlines()
|
||||
.find((line) => tip_product_id && line.product.id === tip_product_id);
|
||||
@@ -134,19 +133,19 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
}
|
||||
}
|
||||
orderDone() {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
this.pos.globalState.removeOrder(this.currentOrder);
|
||||
this._addNewOrder();
|
||||
const { name, props } = this.nextScreen;
|
||||
this.pos.showScreen(name, props);
|
||||
}
|
||||
resumeOrder() {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
this.env.pos.selectNextOrder();
|
||||
this.pos.globalState.removeOrder(this.currentOrder);
|
||||
this.pos.globalState.selectNextOrder();
|
||||
const { name, props } = this.ticketScreen;
|
||||
this.pos.showScreen(name, props);
|
||||
}
|
||||
isResumeVisible() {
|
||||
return this.env.pos.get_order_list().length > 1;
|
||||
return this.pos.globalState.get_order_list().length > 1;
|
||||
}
|
||||
async printReceipt() {
|
||||
this.buttonPrintReceipt.el.className = "fa fa-fw fa-spin fa-circle-o-notch";
|
||||
@@ -160,7 +159,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
this.buttonPrintReceipt.el.className = "fa fa-print";
|
||||
}
|
||||
_shouldAutoPrint() {
|
||||
return this.env.pos.config.iface_print_auto && !this.currentOrder._printed;
|
||||
return this.pos.globalState.config.iface_print_auto && !this.currentOrder._printed;
|
||||
}
|
||||
_shouldCloseImmediately() {
|
||||
var invoiced_finalized = this.currentOrder.is_to_invoice()
|
||||
@@ -168,7 +167,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
: true;
|
||||
return (
|
||||
this.hardwareProxy.printer &&
|
||||
this.env.pos.config.iface_print_skip_screen &&
|
||||
this.pos.globalState.config.iface_print_skip_screen &&
|
||||
invoiced_finalized
|
||||
);
|
||||
}
|
||||
@@ -183,7 +182,7 @@ export class ReceiptScreen extends AbstractReceiptScreen {
|
||||
email: this.orderUiState.inputEmail,
|
||||
name: partner ? partner.name : this.orderUiState.inputEmail,
|
||||
};
|
||||
const order_server_id = this.env.pos.validated_orders_name_server_id_map[orderName];
|
||||
const order_server_id = this.pos.globalState.validated_orders_name_server_id_map[orderName];
|
||||
if (!order_server_id) {
|
||||
this.popup.add(OfflineErrorPopup, {
|
||||
title: this.env._t("Unsynced order"),
|
||||
|
||||
@@ -14,7 +14,7 @@ export class ScaleScreen extends Component {
|
||||
* @param {Object} props.product The product to weight.
|
||||
*/
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.hardwareProxy = useService("hardware_proxy");
|
||||
useExternalListener(document, "keyup", this._onHotkeys);
|
||||
this.state = useState({ weight: 0 });
|
||||
@@ -60,8 +60,8 @@ export class ScaleScreen extends Component {
|
||||
setTimeout(() => this._setWeight(), 500);
|
||||
}
|
||||
get _activePricelist() {
|
||||
const current_order = this.env.pos.get_order();
|
||||
let current_pricelist = this.env.pos.default_pricelist;
|
||||
const current_order = this.pos.globalState.get_order();
|
||||
let current_pricelist = this.pos.globalState.default_pricelist;
|
||||
if (current_order) {
|
||||
current_pricelist = current_order.pricelist;
|
||||
}
|
||||
@@ -69,14 +69,14 @@ export class ScaleScreen extends Component {
|
||||
}
|
||||
get productWeightString() {
|
||||
const defaultstr = (this.state.weight || 0).toFixed(3) + " Kg";
|
||||
if (!this.props.product || !this.env.pos) {
|
||||
if (!this.props.product) {
|
||||
return defaultstr;
|
||||
}
|
||||
const unit_id = this.props.product.uom_id;
|
||||
if (!unit_id) {
|
||||
return defaultstr;
|
||||
}
|
||||
const unit = this.env.pos.units_by_id[unit_id[0]];
|
||||
const unit = this.pos.globalState.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;
|
||||
@@ -90,14 +90,13 @@ export class ScaleScreen extends Component {
|
||||
return (product ? product.get_price(this._activePricelist, this.state.weight) : 0) || 0;
|
||||
}
|
||||
get productName() {
|
||||
return (
|
||||
(this.props.product ? this.props.product.display_name : undefined) || "Unnamed Product"
|
||||
);
|
||||
return this.props.product?.display_name || "Unnamed Product";
|
||||
}
|
||||
get productUom() {
|
||||
return this.props.product
|
||||
? this.env.pos.units_by_id[this.props.product.uom_id[0]].name
|
||||
: "";
|
||||
if (!this.props.product) {
|
||||
return "";
|
||||
}
|
||||
return this.pos.globalState.units_by_id[this.props.product.uom_id[0]].name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Component } from "@odoo/owl";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
import { formatFloat } from "@web/views/fields/formatters";
|
||||
import { roundPrecision as round_pr } from "@web/core/utils/numbers";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
/**
|
||||
* @props {pos.order.line} line
|
||||
@@ -11,12 +12,15 @@ import { roundPrecision as round_pr } from "@web/core/utils/numbers";
|
||||
export class OrderlineDetails extends Component {
|
||||
static template = "OrderlineDetails";
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get line() {
|
||||
const line = this.props.line;
|
||||
const formatQty = (line) => {
|
||||
const quantity = line.get_quantity();
|
||||
const unit = line.get_unit();
|
||||
const decimals = this.env.pos.dp["Product Unit of Measure"];
|
||||
const decimals = this.pos.globalState.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] });
|
||||
@@ -51,17 +55,17 @@ export class OrderlineDetails extends Component {
|
||||
return this.props.line.get_customer_note();
|
||||
}
|
||||
getToRefundDetail() {
|
||||
return this.env.pos.toRefundLines[this.props.line.id];
|
||||
return this.pos.globalState.toRefundLines[this.props.line.id];
|
||||
}
|
||||
hasRefundedQty() {
|
||||
return !this.env.pos.isProductQtyZero(this.props.line.refunded_qty);
|
||||
return !this.pos.globalState.isProductQtyZero(this.props.line.refunded_qty);
|
||||
}
|
||||
getFormattedRefundedQty() {
|
||||
return this.env.utils.formatProductQty(this.props.line.refunded_qty);
|
||||
}
|
||||
hasToRefundQty() {
|
||||
const toRefundDetail = this.getToRefundDetail();
|
||||
return !this.env.pos.isProductQtyZero(toRefundDetail && toRefundDetail.qty);
|
||||
return !this.pos.globalState.isProductQtyZero(toRefundDetail && toRefundDetail.qty);
|
||||
}
|
||||
getFormattedToRefundQty() {
|
||||
const toRefundDetail = this.getToRefundDetail();
|
||||
|
||||
@@ -21,7 +21,8 @@ export class ReprintReceiptScreen extends AbstractReceiptScreen {
|
||||
this.pos.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
}
|
||||
async printReceipt() {
|
||||
if (this.hardwareProxy.printer && this.env.pos.config.iface_print_skip_screen) {
|
||||
const { iface_print_skip_screen } = this.pos.globalState.config;
|
||||
if (this.hardwareProxy.printer && iface_print_skip_screen) {
|
||||
const result = await this._printReceipt();
|
||||
if (result) {
|
||||
this.pos.showScreen("TicketScreen", { reuseSavedUIState: true });
|
||||
|
||||
@@ -54,7 +54,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
this.numberBuffer.use({
|
||||
triggerAtInput: (event) => this._onUpdateSelectedOrderline(event),
|
||||
});
|
||||
this._state = this.env.pos.TICKET_SCREEN_STATE;
|
||||
this._state = this.pos.globalState.TICKET_SCREEN_STATE;
|
||||
this.state = useState({
|
||||
showSearchBar: !this.env.isMobile,
|
||||
});
|
||||
@@ -62,7 +62,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
? this._state.ui
|
||||
: {
|
||||
selectedSyncedOrderId: null,
|
||||
searchDetails: this.env.pos.getDefaultSearchDetails(),
|
||||
searchDetails: this.pos.globalState.getDefaultSearchDetails(),
|
||||
filter: null,
|
||||
selectedOrderlineIds: {},
|
||||
};
|
||||
@@ -115,13 +115,13 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
}
|
||||
}
|
||||
onCreateNewOrder() {
|
||||
this.env.pos.add_new_order();
|
||||
this.pos.globalState.add_new_order();
|
||||
this.pos.showScreen("ProductScreen");
|
||||
}
|
||||
_selectNextOrder(currentOrder) {
|
||||
const currentOrderIndex = this._getOrderList().indexOf(currentOrder);
|
||||
const orderList = this._getOrderList();
|
||||
this.env.pos.set_order(
|
||||
this.pos.globalState.set_order(
|
||||
orderList[currentOrderIndex + 1] || orderList[currentOrderIndex - 1]
|
||||
);
|
||||
}
|
||||
@@ -145,14 +145,15 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const { globalState } = this.pos;
|
||||
if (order && (await this._onBeforeDeleteOrder(order))) {
|
||||
if (order === this.env.pos.get_order()) {
|
||||
if (order === globalState.get_order()) {
|
||||
this._selectNextOrder(order);
|
||||
}
|
||||
this.env.pos.removeOrder(order);
|
||||
globalState.removeOrder(order);
|
||||
}
|
||||
if (this.env.pos.isOpenOrderShareable()) {
|
||||
this.env.pos._removeOrdersFromServer();
|
||||
if (globalState.isOpenOrderShareable()) {
|
||||
globalState._removeOrdersFromServer();
|
||||
}
|
||||
}
|
||||
async onNextPage() {
|
||||
@@ -168,7 +169,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
}
|
||||
}
|
||||
async onInvoiceOrder(orderId) {
|
||||
this.env.pos._invalidateSyncedOrdersCache([orderId]);
|
||||
this.pos.globalState._invalidateSyncedOrdersCache([orderId]);
|
||||
await this._fetchSyncedOrders();
|
||||
}
|
||||
onClickOrderline(orderline) {
|
||||
@@ -178,7 +179,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
}
|
||||
onClickRefundOrderUid(orderUid) {
|
||||
// Open the refund order.
|
||||
const refundOrder = this.env.pos.orders.find((order) => order.uid == orderUid);
|
||||
const refundOrder = this.pos.globalState.orders.find((order) => order.uid == orderUid);
|
||||
if (refundOrder) {
|
||||
this._setOrder(refundOrder);
|
||||
}
|
||||
@@ -258,9 +259,10 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
? 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 = this.env.pos.db.get_product_by_id(refundDetail.orderline.productId);
|
||||
const product = globalState.db.get_product_by_id(refundDetail.orderline.productId);
|
||||
const options = this._prepareRefundOrderlineOptions(refundDetail);
|
||||
await destinationOrder.add_product(product, options);
|
||||
refundDetail.destinationOrderUid = destinationOrder.uid;
|
||||
@@ -272,8 +274,8 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
destinationOrder.updatePricelist(partner);
|
||||
}
|
||||
|
||||
if (this.env.pos.get_order().cid !== destinationOrder.cid) {
|
||||
this.env.pos.set_order(destinationOrder);
|
||||
if (globalState.get_order().cid !== destinationOrder.cid) {
|
||||
globalState.set_order(destinationOrder);
|
||||
}
|
||||
|
||||
this.onCloseScreen();
|
||||
@@ -361,7 +363,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
const productScreenStatus = this._getScreenToStatusMap().ProductScreen;
|
||||
return (
|
||||
order.get_orderlines().length === 0 &&
|
||||
this.env.pos.get_order_list().length === 1 &&
|
||||
this.pos.globalState.get_order_list().length === 1 &&
|
||||
status === productScreenStatus &&
|
||||
order.get_paymentlines().length === 0
|
||||
);
|
||||
@@ -385,12 +387,12 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
const selectedOrder = this.getSelectedSyncedOrder();
|
||||
return selectedOrder ? order.backendId == selectedOrder.backendId : false;
|
||||
} else {
|
||||
const activeOrder = this.env.pos.get_order();
|
||||
const activeOrder = this.pos.globalState.get_order();
|
||||
return activeOrder ? activeOrder.uid == order.uid : false;
|
||||
}
|
||||
}
|
||||
showCardholderName() {
|
||||
return this.env.pos.payment_methods.some((method) => method.use_payment_terminal);
|
||||
return this.pos.globalState.payment_methods.some((method) => method.use_payment_terminal);
|
||||
}
|
||||
getSearchBarConfig() {
|
||||
return {
|
||||
@@ -424,7 +426,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
if (this._doesOrderHaveSoleItem(order)) {
|
||||
return true;
|
||||
}
|
||||
const total = Object.values(this.env.pos.toRefundLines)
|
||||
const total = Object.values(this.pos.globalState.toRefundLines)
|
||||
.filter(
|
||||
(toRefundDetail) =>
|
||||
toRefundDetail.orderline.orderUid === order.uid &&
|
||||
@@ -432,7 +434,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
)
|
||||
.map((toRefundDetail) => toRefundDetail.qty)
|
||||
.reduce((acc, val) => acc + val, 0);
|
||||
return !this.env.pos.isProductQtyZero(total);
|
||||
return !this.pos.globalState.isProductQtyZero(total);
|
||||
}
|
||||
//#endregion
|
||||
//#region PRIVATE METHODS
|
||||
@@ -447,7 +449,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
_getEmptyOrder(partner) {
|
||||
let emptyOrderForPartner = null;
|
||||
let emptyOrder = null;
|
||||
for (const order of this.env.pos.orders) {
|
||||
for (const order of this.pos.globalState.orders) {
|
||||
if (order.get_orderlines().length === 0 && order.get_paymentlines().length === 0) {
|
||||
if (order.get_partner() === partner) {
|
||||
emptyOrderForPartner = order;
|
||||
@@ -458,7 +460,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
}
|
||||
}
|
||||
}
|
||||
return emptyOrderForPartner || emptyOrder || this.env.pos.add_new_order();
|
||||
return emptyOrderForPartner || emptyOrder || this.pos.globalState.add_new_order();
|
||||
}
|
||||
_doesOrderHaveSoleItem(order) {
|
||||
const orderlines = order.get_orderlines();
|
||||
@@ -467,7 +469,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
}
|
||||
const theOrderline = orderlines[0];
|
||||
const refundableQty = theOrderline.get_quantity() - theOrderline.refunded_qty;
|
||||
return this.env.pos.isProductQtyZero(refundableQty - 1);
|
||||
return this.pos.globalState.isProductQtyZero(refundableQty - 1);
|
||||
}
|
||||
_prepareAutoRefundOnOrder(order) {
|
||||
const selectedOrderlineId = this.getSelectedOrderlineId();
|
||||
@@ -478,7 +480,7 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
|
||||
const toRefundDetail = this._getToRefundDetail(orderline);
|
||||
const refundableQty = orderline.get_quantity() - orderline.refunded_qty;
|
||||
if (this.env.pos.isProductQtyZero(refundableQty - 1) && toRefundDetail.qty === 0) {
|
||||
if (this.pos.globalState.isProductQtyZero(refundableQty - 1) && toRefundDetail.qty === 0) {
|
||||
toRefundDetail.qty = 1;
|
||||
}
|
||||
return true;
|
||||
@@ -491,30 +493,30 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
* @returns
|
||||
*/
|
||||
_getToRefundDetail(orderline) {
|
||||
if (orderline.id in this.env.pos.toRefundLines) {
|
||||
return this.env.pos.toRefundLines[orderline.id];
|
||||
} else {
|
||||
const partner = orderline.order.get_partner();
|
||||
const orderPartnerId = partner ? partner.id : false;
|
||||
const newToRefundDetail = {
|
||||
qty: 0,
|
||||
orderline: {
|
||||
id: orderline.id,
|
||||
productId: orderline.product.id,
|
||||
price: orderline.price,
|
||||
qty: orderline.quantity,
|
||||
refundedQty: orderline.refunded_qty,
|
||||
orderUid: orderline.order.uid,
|
||||
orderBackendId: orderline.order.backendId,
|
||||
orderPartnerId,
|
||||
tax_ids: orderline.get_taxes().map((tax) => tax.id),
|
||||
discount: orderline.discount,
|
||||
},
|
||||
destinationOrderUid: false,
|
||||
};
|
||||
this.env.pos.toRefundLines[orderline.id] = newToRefundDetail;
|
||||
return newToRefundDetail;
|
||||
const { toRefundLines } = this.pos.globalState;
|
||||
if (orderline.id in toRefundLines) {
|
||||
return toRefundLines[orderline.id];
|
||||
}
|
||||
const partner = orderline.order.get_partner();
|
||||
const orderPartnerId = partner ? partner.id : false;
|
||||
const newToRefundDetail = {
|
||||
qty: 0,
|
||||
orderline: {
|
||||
id: orderline.id,
|
||||
productId: orderline.product.id,
|
||||
price: orderline.price,
|
||||
qty: orderline.quantity,
|
||||
refundedQty: orderline.refunded_qty,
|
||||
orderUid: orderline.order.uid,
|
||||
orderBackendId: orderline.order.backendId,
|
||||
orderPartnerId,
|
||||
tax_ids: orderline.get_taxes().map((tax) => tax.id),
|
||||
discount: orderline.discount,
|
||||
},
|
||||
destinationOrderUid: false,
|
||||
};
|
||||
toRefundLines[orderline.id] = newToRefundDetail;
|
||||
return newToRefundDetail;
|
||||
}
|
||||
/**
|
||||
* Select the lines from toRefundLines, as they can come from different orders.
|
||||
@@ -527,9 +529,9 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
* @returns {Array} refundableDetails
|
||||
*/
|
||||
_getRefundableDetails(partner) {
|
||||
return Object.values(this.env.pos.toRefundLines).filter(
|
||||
return Object.values(this.pos.globalState.toRefundLines).filter(
|
||||
({ qty, orderline, destinationOrderUid }) =>
|
||||
!this.env.pos.isProductQtyZero(qty) &&
|
||||
!this.pos.globalState.isProductQtyZero(qty) &&
|
||||
(partner ? orderline.orderPartnerId == partner.id : true) &&
|
||||
!destinationOrderUid
|
||||
);
|
||||
@@ -553,14 +555,15 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
};
|
||||
}
|
||||
_setOrder(order) {
|
||||
if (this.env.pos.isOpenOrderShareable()) {
|
||||
this.env.pos.sendDraftToServer();
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.isOpenOrderShareable()) {
|
||||
globalState.sendDraftToServer();
|
||||
}
|
||||
this.env.pos.set_order(order);
|
||||
globalState.set_order(order);
|
||||
this.close();
|
||||
}
|
||||
_getOrderList() {
|
||||
return this.env.pos.get_order_list();
|
||||
return this.pos.globalState.get_order_list();
|
||||
}
|
||||
_getFilterOptions() {
|
||||
const orderStates = this._getOrderStates();
|
||||
@@ -659,20 +662,17 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
* 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 { ordersInfo, totalCount } = await this.orm.call(
|
||||
"pos.order",
|
||||
"search_paid_order_ids",
|
||||
[],
|
||||
{
|
||||
config_id: this.env.pos.config.id,
|
||||
domain,
|
||||
limit,
|
||||
offset,
|
||||
}
|
||||
{ config_id, domain, limit, offset }
|
||||
);
|
||||
const idsNotInCache = ordersInfo.filter(
|
||||
(orderInfo) => !(orderInfo[0] in this._state.syncedOrders.cache)
|
||||
@@ -686,14 +686,14 @@ export class TicketScreen extends IndependentToOrderScreen {
|
||||
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 this.env.pos._loadMissingProducts(fetchedOrders);
|
||||
await this.env.pos._loadMissingPartners(fetchedOrders);
|
||||
await globalState._loadMissingProducts(fetchedOrders);
|
||||
await globalState._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: this.env.pos, json: order }
|
||||
{ pos: globalState, json: order }
|
||||
);
|
||||
});
|
||||
//Update the datetime indicator of the cache refresh
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
|
||||
<t t-name="SyncNotification" owl="1">
|
||||
<div class="oe_status" t-on-click="onClick">
|
||||
<span t-if="env.pos.synch.pending" class="js_msg">
|
||||
<t t-esc="env.pos.synch.pending" />
|
||||
<span t-if="sync.pending" class="js_msg">
|
||||
<t t-esc="sync.pending" />
|
||||
<span> </span>
|
||||
</span>
|
||||
<div t-if="env.pos.synch.status === 'connected'" class="js_connected oe_icon oe_green">
|
||||
<div t-if="sync.status === 'connected'" class="js_connected oe_icon oe_green">
|
||||
<i class="fa fa-fw fa-wifi" role="img" aria-label="Synchronisation Connected"
|
||||
title="Synchronisation Connected"></i>
|
||||
</div>
|
||||
<div t-if="env.pos.synch.status === 'connecting'" class="js_connecting oe_icon">
|
||||
<div t-if="sync.status === 'connecting'" class="js_connecting oe_icon">
|
||||
<i class="fa fa-fw fa-spin fa-circle-o-notch" role="img"
|
||||
aria-label="Synchronisation Connecting" title="Synchronisation Connecting"></i>
|
||||
</div>
|
||||
<div t-if="env.pos.synch.status === 'disconnected'" class="js_disconnected oe_icon oe_red">
|
||||
<div t-if="sync.status === 'disconnected'" class="js_disconnected oe_icon oe_red">
|
||||
<i class="fa fa-fw fa-wifi" role="img" aria-label="Synchronisation Disconnected"
|
||||
title="Synchronisation Disconnected"></i>
|
||||
</div>
|
||||
<div t-if="env.pos.synch.status === 'error'" class="js_error oe_icon oe_red">
|
||||
<div t-if="sync.status === 'error'" class="js_error oe_icon oe_red">
|
||||
<i class="fa fa-fw fa-warning" role="img" aria-label="Synchronisation Error"
|
||||
title="Synchronisation Error"></i>
|
||||
</div>
|
||||
|
||||
+7
-7
@@ -40,7 +40,7 @@
|
||||
<select class="detail" name="state_id"
|
||||
t-on-change="captureChange">
|
||||
<option value="">None</option>
|
||||
<t t-foreach="env.pos.states" t-as="state" t-key="state.id">
|
||||
<t t-foreach="pos.globalState.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="env.pos.countries" t-as="country" t-key="country.id">
|
||||
<t t-foreach="pos.globalState.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="env.pos.langs" t-as="lang" t-key="lang.id">
|
||||
<t t-foreach="pos.globalState.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 === env.pos.user.lang? true : undefined">
|
||||
t-att-selected="props.partner.lang ? ((lang.code === props.partner.lang) ? true : undefined) : lang.code === pos.globalState.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="env.pos.pricelists.length gt 1" class="partner-detail">
|
||||
<div t-if="pos.globalState.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="env.pos.pricelists" t-as="pricelist"
|
||||
<t t-foreach="pos.globalState.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 === env.pos.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.globalState.default_pricelist.id ? true : undefined">
|
||||
<t t-esc="pricelist.display_name" />
|
||||
</option>
|
||||
</t>
|
||||
|
||||
@@ -87,16 +87,16 @@
|
||||
t-on-click="toggleIsToInvoice">
|
||||
<i class="fa fa-file-text-o" /> Invoice
|
||||
</div>
|
||||
<div t-if="env.pos.config.tip_product_id" class="button js_tip"
|
||||
<div t-if="pos.globalState.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="env.pos.config.iface_cashdrawer" class="button js_cashdrawer"
|
||||
<div t-if="pos.globalState.config.iface_cashdrawer" class="button js_cashdrawer"
|
||||
t-on-click="openCashbox">
|
||||
<i class="fa fa-archive" /> Open Cashbox
|
||||
</div>
|
||||
<div t-if="env.pos.config.ship_later" class="button"
|
||||
<div t-if="pos.globalState.config.ship_later" class="button"
|
||||
t-att-class="{ highlight: currentOrder.getShippingDate() }"
|
||||
t-on-click="toggleShippingDatePicker">
|
||||
<i class="fa fa-clock-o" /> Ship Later
|
||||
|
||||
@@ -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' && (env.pos.picking_type.use_create_lots || env.pos.picking_type.use_existing_lots)">
|
||||
<t t-if="props.line.get_product().tracking!=='none' && (pos.globalState.picking_type.use_create_lots || pos.globalState.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,7 +25,7 @@
|
||||
onSwitchPane.bind="switchPane" />
|
||||
<NumpadWidget
|
||||
updateSelectedOrderline.bind="updateSelectedOrderline"
|
||||
activeMode="env.pos.numpadMode" />
|
||||
activeMode="pos.globalState.numpadMode" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@
|
||||
<t t-if="!env.isMobile || state.mobileSearchBarIsShown">
|
||||
<div class="pos-search-bar">
|
||||
<i class="fa fa-search"/>
|
||||
<input t-model="this.env.pos.searchProductWord" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
|
||||
<i t-if="this.env.pos.searchProductWord" class="fa fa-times" t-on-click="_clearSearch"/>
|
||||
<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"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
</t>
|
||||
<t t-elif="line.discount !== 0">
|
||||
<div class="pos-receipt-left-padding">
|
||||
<t t-if="env.pos.config.iface_tax_included === 'total'">
|
||||
<t t-if="pos.globalState.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, env.pos.dp['Product Unit of Measure'])) / Math.pow(10, env.pos.dp['Product Unit of Measure'])"/>
|
||||
<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-if="!line.is_in_unit" t-esc="line.unit_name" />
|
||||
x
|
||||
<t t-esc="env.utils.formatCurrency(line.price_display_one)" />
|
||||
|
||||
@@ -6,19 +6,20 @@ import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class DiscountButton extends Component {
|
||||
static template = "DiscountButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
async click() {
|
||||
var self = this;
|
||||
const { confirmed, payload } = await this.popup.add(NumberPopup, {
|
||||
title: this.env._t("Discount Percentage"),
|
||||
startingValue: this.env.pos.config.discount_pc,
|
||||
startingValue: this.pos.globalState.config.discount_pc,
|
||||
isInputSelected: true,
|
||||
});
|
||||
if (confirmed) {
|
||||
@@ -28,9 +29,10 @@ export class DiscountButton extends Component {
|
||||
}
|
||||
|
||||
async apply_discount(pc) {
|
||||
var order = this.env.pos.get_order();
|
||||
var lines = order.get_orderlines();
|
||||
var product = this.env.pos.db.get_product_by_id(this.env.pos.config.discount_product_id[0]);
|
||||
const { globalState } = this.pos;
|
||||
const order = globalState.get_order();
|
||||
const lines = order.get_orderlines();
|
||||
const product = globalState.db.get_product_by_id(globalState.config.discount_product_id[0]);
|
||||
if (product === undefined) {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("No discount product found"),
|
||||
@@ -56,7 +58,14 @@ export class DiscountButton extends Component {
|
||||
.filter((id) => id !== "")
|
||||
.map((id) => Number(id));
|
||||
|
||||
const baseToDiscount = order.calculate_base_amount(tax_ids_array, lines.filter((line) => !this.env.pos.config.tip_product_id || line.product.id !== this.env.pos.config.tip_product_id[0]));
|
||||
const baseToDiscount = order.calculate_base_amount(
|
||||
tax_ids_array,
|
||||
lines.filter(
|
||||
(line) =>
|
||||
!globalState.config.tip_product_id ||
|
||||
line.product.id !== globalState.config.tip_product_id[0]
|
||||
)
|
||||
);
|
||||
|
||||
// We add the price as manually set to avoid recomputation when changing customer.
|
||||
const discount = (-pc / 100.0) * baseToDiscount;
|
||||
@@ -72,7 +81,7 @@ export class DiscountButton extends Component {
|
||||
? sprintf(
|
||||
this.env._t("Tax: %s"),
|
||||
tax_ids_array
|
||||
.map((taxId) => this.env.pos.taxes_by_id[taxId].amount + "%")
|
||||
.map((taxId) => globalState.taxes_by_id[taxId].amount + "%")
|
||||
.join(", ")
|
||||
)
|
||||
: this.env._t("No tax")),
|
||||
@@ -88,6 +97,7 @@ export class DiscountButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: DiscountButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.module_pos_discount && this.env.pos.config.discount_product_id;
|
||||
const { module_pos_discount, discount_product_id } = this.pos.globalState.config;
|
||||
return module_pos_discount && discount_product_id;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<t t-name="pos_hr.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('end-nav-button')]" position="replace">
|
||||
<div class="end-nav-button">
|
||||
<BackendButton t-if="!env.pos.config.module_pos_hr"/>
|
||||
<BackendButton t-elif="(env.pos.get_cashier().role === 'manager' || env.pos.get_cashier_user_id() === env.pos.user.id)"/>
|
||||
<HeaderLockButton t-if="env.pos.config.module_pos_hr" />
|
||||
<div class="close-button" t-if="!env.pos.config.module_pos_hr || env.pos.get_cashier().role === 'manager' || env.pos.get_cashier_user_id() === env.pos.user.id">
|
||||
<BackendButton t-if="!pos.globalState.config.module_pos_hr"/>
|
||||
<BackendButton t-elif="(pos.globalState.get_cashier().role === 'manager' || pos.globalState.get_cashier_user_id() === pos.globalState.user.id)"/>
|
||||
<HeaderLockButton t-if="pos.globalState.config.module_pos_hr" />
|
||||
<div class="close-button" t-if="!pos.globalState.config.module_pos_hr || pos.globalState.get_cashier().role === 'manager' || pos.globalState.get_cashier_user_id() === pos.globalState.user.id">
|
||||
<HeaderButton/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,15 +11,15 @@ patch(CashierName.prototype, "pos_hr.CashierName", {
|
||||
},
|
||||
//@Override
|
||||
get avatar() {
|
||||
if (this.env.pos.config.module_pos_hr) {
|
||||
const cashier = this.env.pos.get_cashier();
|
||||
if (this.pos.globalState.config.module_pos_hr) {
|
||||
const cashier = this.pos.globalState.get_cashier();
|
||||
return `/web/image/hr.employee/${cashier.id}/avatar_128`;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
//@Override
|
||||
get cssClass() {
|
||||
if (this.env.pos.config.module_pos_hr) {
|
||||
if (this.pos.globalState.config.module_pos_hr) {
|
||||
return { oe_status: true };
|
||||
}
|
||||
return this._super(...arguments);
|
||||
|
||||
@@ -5,9 +5,7 @@ import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(Chrome.prototype, "pos_hr.Chrome", {
|
||||
get showCashMoveButton() {
|
||||
return (
|
||||
this._super(...arguments) &&
|
||||
(!this.env.pos.cashier || this.env.pos.cashier.role == "manager")
|
||||
);
|
||||
const { cashier } = this.pos.globalState;
|
||||
return this._super(...arguments) && (!cashier || cashier.role == "manager");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ export class HeaderLockButton extends Component {
|
||||
this.pos = usePos();
|
||||
}
|
||||
async showLoginScreen() {
|
||||
this.env.pos.reset_cashier();
|
||||
this.pos.globalState.reset_cashier();
|
||||
await this.pos.showTempScreen("LoginScreen");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ export class LoginScreen extends Component {
|
||||
back() {
|
||||
this.props.resolve({ confirmed: false, payload: false });
|
||||
this.pos.closeTempScreen();
|
||||
this.env.pos.hasLoggedIn = true;
|
||||
this.pos.globalState.hasLoggedIn = true;
|
||||
this.pos.openCashControl();
|
||||
}
|
||||
|
||||
get shopName() {
|
||||
return this.env.pos.config.name;
|
||||
return this.pos.globalState.config.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(PaymentScreen.prototype, "pos_hr.PaymentScreen", {
|
||||
async _finalizeValidation() {
|
||||
this.currentOrder.cashier = this.env.pos.get_cashier();
|
||||
this.currentOrder.cashier = this.pos.globalState.get_cashier();
|
||||
await this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,26 +7,26 @@ import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { SelectionPopup } from "@point_of_sale/js/Popups/SelectionPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { useEnv } from "@odoo/owl";
|
||||
import { useBarcodeReader } from "@point_of_sale/app/barcode_reader_hook";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export function useCashierSelector(
|
||||
{ onCashierChanged, exclusive } = { onCashierChanged: () => {}, exclusive: false }
|
||||
) {
|
||||
const popup = useService("popup");
|
||||
const env = useEnv();
|
||||
const { globalState } = usePos();
|
||||
useBarcodeReader(
|
||||
{
|
||||
async cashier(code) {
|
||||
const employee = env.pos.employees.find(
|
||||
const employee = globalState.employees.find(
|
||||
(emp) => emp.barcode === Sha1.hash(code.code)
|
||||
);
|
||||
if (
|
||||
employee &&
|
||||
employee !== env.pos.get_cashier() &&
|
||||
employee !== globalState.get_cashier() &&
|
||||
(!employee.pin || (await checkPin(employee)))
|
||||
) {
|
||||
env.pos.set_cashier(employee);
|
||||
globalState.set_cashier(employee);
|
||||
if (onCashierChanged) {
|
||||
onCashierChanged();
|
||||
}
|
||||
@@ -60,9 +60,9 @@ export function useCashierSelector(
|
||||
* Select a cashier, the returning value will either be an object or nothing (undefined)
|
||||
*/
|
||||
return async function selectCashier() {
|
||||
if (env.pos.config.module_pos_hr) {
|
||||
const employeesList = env.pos.employees
|
||||
.filter((employee) => employee.id !== env.pos.get_cashier().id)
|
||||
if (globalState.config.module_pos_hr) {
|
||||
const employeesList = globalState.employees
|
||||
.filter((employee) => employee.id !== globalState.get_cashier().id)
|
||||
.map((employee) => {
|
||||
return {
|
||||
id: employee.id,
|
||||
@@ -80,7 +80,7 @@ export function useCashierSelector(
|
||||
return;
|
||||
}
|
||||
|
||||
env.pos.set_cashier(employee);
|
||||
globalState.set_cashier(employee);
|
||||
if (onCashierChanged) {
|
||||
onCashierChanged();
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { TextInputPopup } from "@point_of_sale/js/Popups/TextInputPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class PromoCodeButton extends Component {
|
||||
static template = "PromoCodeButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
|
||||
@@ -22,7 +23,7 @@ export class PromoCodeButton extends Component {
|
||||
if (confirmed) {
|
||||
code = code.trim();
|
||||
if (code !== "") {
|
||||
this.env.pos.get_order().activateCode(code);
|
||||
this.pos.globalState.get_order().activateCode(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +32,7 @@ export class PromoCodeButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: PromoCodeButton,
|
||||
condition: function () {
|
||||
return this.env.pos.programs.some((p) =>
|
||||
return this.pos.globalState.programs.some((p) =>
|
||||
["coupons", "promotion", "gift_card", "promo_code"].includes(p.program_type)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -2,24 +2,27 @@
|
||||
|
||||
import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class ResetProgramsButton extends Component {
|
||||
static template = "ResetProgramsButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
}
|
||||
_isDisabled() {
|
||||
return !this.env.pos.get_order().isProgramsResettable();
|
||||
return !this.pos.globalState.get_order().isProgramsResettable();
|
||||
}
|
||||
click() {
|
||||
this.env.pos.get_order()._resetPrograms();
|
||||
this.pos.globalState.get_order()._resetPrograms();
|
||||
}
|
||||
}
|
||||
|
||||
ProductScreen.addControlButton({
|
||||
component: ResetProgramsButton,
|
||||
condition: function () {
|
||||
return this.env.pos.programs.some((p) => ["coupons", "promotion"].includes(p.program_type));
|
||||
return this.pos.globalState.programs.some((p) =>
|
||||
["coupons", "promotion"].includes(p.program_type)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,7 +10,6 @@ export class RewardButton extends Component {
|
||||
static template = "RewardButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.popup = useService("popup");
|
||||
this.pos = usePos();
|
||||
this.notification = useService("pos_notification");
|
||||
@@ -31,7 +30,7 @@ export class RewardButton extends Component {
|
||||
}
|
||||
|
||||
_getPotentialRewards() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
// Claimable rewards excluding those from eWallet programs.
|
||||
// eWallet rewards are handled in the eWalletButton.
|
||||
let rewards = [];
|
||||
@@ -62,14 +61,14 @@ export class RewardButton extends Component {
|
||||
* @param {Integer} coupon_id
|
||||
*/
|
||||
async _applyReward(reward, coupon_id, potentialQty) {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.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.env.pos.db.get_product_by_id(product_id).display_name,
|
||||
label: this.pos.globalState.db.get_product_by_id(product_id).display_name,
|
||||
item: product_id,
|
||||
}));
|
||||
const { confirmed, payload: selectedProduct } = await this.popup.add(SelectionPopup, {
|
||||
@@ -126,6 +125,6 @@ export class RewardButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: RewardButton,
|
||||
condition: function () {
|
||||
return this.env.pos.programs.length > 0;
|
||||
return this.pos.globalState.programs.length > 0;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ export class eWalletButton extends Component {
|
||||
static template = "point_of_sale.eWalletButton";
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.popup = useService("popup");
|
||||
this.pos = usePos();
|
||||
}
|
||||
@@ -24,11 +23,13 @@ export class eWalletButton extends Component {
|
||||
return eWalletRewards;
|
||||
}
|
||||
_getEWalletPrograms() {
|
||||
return this.env.pos.programs.filter((p) => p.program_type == "ewallet");
|
||||
return this.pos.globalState.programs.filter((p) => p.program_type == "ewallet");
|
||||
}
|
||||
async _onClickWalletButton() {
|
||||
const order = this.env.pos.get_order();
|
||||
const eWalletPrograms = this.env.pos.programs.filter((p) => p.program_type == "ewallet");
|
||||
const order = this.pos.globalState.get_order();
|
||||
const eWalletPrograms = this.pos.globalState.programs.filter(
|
||||
(p) => p.program_type == "ewallet"
|
||||
);
|
||||
const orderTotal = order.get_total_with_tax();
|
||||
const eWalletRewards = this._getEWalletRewards(order);
|
||||
if (orderTotal < 0 && eWalletPrograms.length >= 1) {
|
||||
@@ -49,7 +50,7 @@ export class eWalletButton extends Component {
|
||||
}
|
||||
}
|
||||
if (selectedProgram) {
|
||||
const eWalletProduct = this.env.pos.db.get_product_by_id(
|
||||
const eWalletProduct = this.pos.globalState.db.get_product_by_id(
|
||||
selectedProgram.trigger_product_ids[0]
|
||||
);
|
||||
this.pos.addProductFromUi(eWalletProduct, {
|
||||
@@ -109,6 +110,6 @@ export class eWalletButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: eWalletButton,
|
||||
condition: function () {
|
||||
return this.env.pos.programs.filter((p) => p.program_type == "ewallet").length > 0;
|
||||
return this.pos.globalState.programs.filter((p) => p.program_type == "ewallet").length > 0;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { patch } from "@web/core/utils/patch";
|
||||
|
||||
patch(OrderSummary.prototype, "pos_loyalty.OrderSummary", {
|
||||
getLoyaltyPoints() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
return order.getLoyaltyPoints();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,9 +11,9 @@ patch(Orderline.prototype, "pos_loyalty.Orderline", {
|
||||
);
|
||||
},
|
||||
_isGiftCardOrEWalletReward() {
|
||||
const coupon = this.env.pos.couponCache[this.props.line.coupon_id];
|
||||
const coupon = this.pos.globalState.couponCache[this.props.line.coupon_id];
|
||||
if (coupon) {
|
||||
const program = this.env.pos.program_by_id[coupon.program_id];
|
||||
const program = this.pos.globalState.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.env.pos.couponCache[this.props.line.coupon_id];
|
||||
const coupon = this.pos.globalState.couponCache[this.props.line.coupon_id];
|
||||
if (coupon) {
|
||||
return this.env.utils.formatCurrency(coupon.balance);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { PartnerLine } from "@point_of_sale/js/Screens/PartnerListScreen/PartnerLine";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
import { formatFloat } from "@web/views/fields/formatters";
|
||||
|
||||
patch(PartnerLine.prototype, "pos_loyalty.PartnerLine", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.pos = usePos();
|
||||
},
|
||||
_getLoyaltyPointsRepr(loyaltyCard) {
|
||||
const program = this.env.pos.program_by_id[loyaltyCard.program_id];
|
||||
const program = this.pos.globalState.program_by_id[loyaltyCard.program_id];
|
||||
if (program.program_type === "ewallet") {
|
||||
return `${program.name}: ${this.env.utils.formatCurrency(loyaltyCard.balance)}`;
|
||||
}
|
||||
|
||||
@@ -41,17 +41,18 @@ 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;
|
||||
if (payload && payload.updated_points) {
|
||||
for (const pointChange of Object.entries(payload.updated_points)) {
|
||||
if (this.env.pos.couponCache[pointChange[0]]) {
|
||||
this.env.pos.couponCache[pointChange[0]].balance = pointChange[1];
|
||||
if (couponCache[pointChange[0]]) {
|
||||
couponCache[pointChange[0]].balance = pointChange[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (payload && payload.removed_coupons) {
|
||||
for (const couponId of payload.removed_coupons) {
|
||||
if (this.env.pos.couponCache[couponId]) {
|
||||
delete this.env.pos.couponCache[couponId];
|
||||
if (couponCache[couponId]) {
|
||||
delete couponCache[couponId];
|
||||
}
|
||||
}
|
||||
this.currentOrder.codeActivatedCoupons =
|
||||
@@ -79,22 +80,21 @@ 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 rewardLines = order._get_reward_lines();
|
||||
const partner = order.get_partner();
|
||||
let couponData = Object.values(order.couponPointChanges).reduce((agg, pe) => {
|
||||
agg[pe.coupon_id] = Object.assign({}, pe, {
|
||||
points:
|
||||
pe.points -
|
||||
order._getPointsCorrection(this.env.pos.program_by_id[pe.program_id]),
|
||||
points: pe.points - order._getPointsCorrection(program_by_id[pe.program_id]),
|
||||
});
|
||||
const program = this.env.pos.program_by_id[pe.program_id];
|
||||
const program = program_by_id[pe.program_id];
|
||||
if (program.is_nominative && partner) {
|
||||
agg[pe.coupon_id].partner_id = partner.id;
|
||||
}
|
||||
return agg;
|
||||
}, {});
|
||||
for (const line of rewardLines) {
|
||||
const reward = this.env.pos.reward_by_id[line.reward_id];
|
||||
const reward = reward_by_id[line.reward_id];
|
||||
if (!couponData[line.coupon_id]) {
|
||||
couponData[line.coupon_id] = {
|
||||
points: 0,
|
||||
@@ -114,7 +114,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
|
||||
// We actually do not care about coupons for 'current' programs that did not claim any reward, they will be lost if not validated
|
||||
couponData = Object.fromEntries(
|
||||
Object.entries(couponData).filter(([key, value]) => {
|
||||
const program = this.env.pos.program_by_id[value.program_id];
|
||||
const program = program_by_id[value.program_id];
|
||||
if (program.applies_on === "current") {
|
||||
return value.line_codes && value.line_codes.length;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
|
||||
]);
|
||||
if (payload.coupon_updates) {
|
||||
for (const couponUpdate of payload.coupon_updates) {
|
||||
let dbCoupon = this.env.pos.couponCache[couponUpdate.old_id];
|
||||
let dbCoupon = couponCache[couponUpdate.old_id];
|
||||
if (dbCoupon) {
|
||||
dbCoupon.id = couponUpdate.id;
|
||||
dbCoupon.balance = couponUpdate.points;
|
||||
@@ -142,14 +142,14 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
|
||||
couponUpdate.points
|
||||
);
|
||||
}
|
||||
delete this.env.pos.couponCache[couponUpdate.old_id];
|
||||
this.env.pos.couponCache[couponUpdate.id] = dbCoupon;
|
||||
delete couponCache[couponUpdate.old_id];
|
||||
couponCache[couponUpdate.id] = dbCoupon;
|
||||
}
|
||||
}
|
||||
// Update the usage count since it is checked based on local data
|
||||
if (payload.program_updates) {
|
||||
for (const programUpdate of payload.program_updates) {
|
||||
const program = this.env.pos.program_by_id[programUpdate.program_id];
|
||||
const program = program_by_id[programUpdate.program_id];
|
||||
if (program) {
|
||||
program.total_order_count = programUpdate.usages;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
!selectedLine.manual_reward &&
|
||||
(key === "Backspace" || key === "Delete")
|
||||
) {
|
||||
const reward = this.env.pos.reward_by_id[selectedLine.reward_id];
|
||||
const reward = this.pos.globalState.reward_by_id[selectedLine.reward_id];
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
title: this.env._t("Deactivating reward"),
|
||||
body: sprintf(
|
||||
@@ -87,13 +87,14 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
|
||||
}
|
||||
if (selectedLine.is_reward_line && val === "remove") {
|
||||
this.currentOrder.disabledRewards.add(selectedLine.reward_id);
|
||||
const coupon = this.env.pos.couponCache[selectedLine.coupon_id];
|
||||
const { couponCache } = this.pos.globalState;
|
||||
const coupon = couponCache[selectedLine.coupon_id];
|
||||
if (
|
||||
coupon &&
|
||||
coupon.id > 0 &&
|
||||
this.currentOrder.codeActivatedCoupons.find((c) => c.code === coupon.code)
|
||||
) {
|
||||
delete this.env.pos.couponCache[selectedLine.coupon_id];
|
||||
delete couponCache[selectedLine.coupon_id];
|
||||
this.currentOrder.codeActivatedCoupons.splice(
|
||||
this.currentOrder.codeActivatedCoupons.findIndex((coupon) => {
|
||||
return coupon.id === selectedLine.coupon_id;
|
||||
|
||||
@@ -43,12 +43,12 @@ patch(TicketScreen.prototype, "pos_loyalty.TicketScreen", {
|
||||
);
|
||||
},
|
||||
_isEWalletGiftCard(orderline) {
|
||||
const linkedProgramIds = this.env.pos.productId2ProgramIds[orderline.product.id];
|
||||
const linkedProgramIds = this.pos.globalState.productId2ProgramIds[orderline.product.id];
|
||||
if (linkedProgramIds) {
|
||||
return linkedProgramIds.length > 0;
|
||||
}
|
||||
if (orderline.is_reward_line) {
|
||||
const reward = this.env.pos.reward_by_id[orderline.reward_id];
|
||||
const reward = this.pos.globalState.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="env.pos.get_order()" />
|
||||
<t t-set="_order" t-value="pos.globalState.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="env.pos.getLoyaltyCards(props.partner)" />
|
||||
<t t-set="_loyaltyCards" t-value="pos.globalState.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.env.pos.getOnlinePaymentMethods().length !== 0) {
|
||||
if (this.pos.globalState.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.env.pos.get_order().get_paymentlines();
|
||||
var lines = this.pos.globalState.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.env.pos.get_order().get_paymentlines();
|
||||
var lines = this.pos.globalState.get_order().get_paymentlines();
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (
|
||||
@@ -191,7 +191,8 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
},
|
||||
// Handler to manage the card reader string
|
||||
credit_code_transaction(parsed_result, old_deferred, retry_nr) {
|
||||
var order = this.env.pos.get_order();
|
||||
const { globalState } = this.pos.globalState;
|
||||
const order = globalState.get_order();
|
||||
if (order.get_due(order.selected_paymentline) < 0) {
|
||||
this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Refunds not supported"),
|
||||
@@ -202,12 +203,11 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.env.pos.getOnlinePaymentMethods().length === 0) {
|
||||
if (globalState.getOnlinePaymentMethods().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var decodedMagtek = self.env.pos.decodeMagtek(parsed_result.code);
|
||||
var decodedMagtek = globalState.decodeMagtek(parsed_result.code);
|
||||
|
||||
if (!decodedMagtek) {
|
||||
this.popup.add(ErrorPopup, {
|
||||
@@ -219,13 +219,13 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
return;
|
||||
}
|
||||
|
||||
var swipe_pending_line = self._get_swipe_pending_line();
|
||||
var swipe_pending_line = this._get_swipe_pending_line();
|
||||
var purchase_amount = 0;
|
||||
|
||||
if (swipe_pending_line) {
|
||||
purchase_amount = swipe_pending_line.get_amount();
|
||||
} else {
|
||||
purchase_amount = self.env.pos.get_order().get_due();
|
||||
purchase_amount = globalState.get_order().get_due();
|
||||
}
|
||||
|
||||
var transaction = {
|
||||
@@ -233,7 +233,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
encrypted_block: decodedMagtek["encrypted_block"],
|
||||
transaction_type: "Credit",
|
||||
transaction_code: "Sale",
|
||||
invoice_no: self.env.pos.get_order().uid.replace(/-/g, ""),
|
||||
invoice_no: globalState.get_order().uid.replace(/-/g, ""),
|
||||
purchase: purchase_amount,
|
||||
payment_method_id: parsed_result.payment_method_id,
|
||||
};
|
||||
@@ -255,15 +255,15 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
// FIXME POSREF timeout
|
||||
this.orm
|
||||
.call("pos_mercury.mercury_transaction", "do_payment", [transaction])
|
||||
.then(function (data) {
|
||||
.then((data) => {
|
||||
// if not receiving a response from Vantiv, we should retry
|
||||
if (data === "timeout") {
|
||||
self.retry_mercury_transaction(
|
||||
this.retry_mercury_transaction(
|
||||
def,
|
||||
null,
|
||||
retry_nr,
|
||||
true,
|
||||
self.credit_code_transaction,
|
||||
this.credit_code_transaction,
|
||||
[parsed_result, def, retry_nr + 1]
|
||||
);
|
||||
return;
|
||||
@@ -271,26 +271,26 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
|
||||
if (data === "not setup") {
|
||||
def.resolve({
|
||||
message: self.env._t("Please setup your Vantiv merchant account."),
|
||||
message: this.env._t("Please setup your Vantiv merchant account."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (data === "internal error") {
|
||||
def.resolve({
|
||||
message: self.env._t("Odoo error while processing transaction."),
|
||||
message: this.env._t("Odoo error while processing transaction."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var response = self.env.pos.decodeMercuryResponse(data);
|
||||
var response = globalState.decodeMercuryResponse(data);
|
||||
response.payment_method_id = parsed_result.payment_method_id;
|
||||
|
||||
if (response.status === "Approved") {
|
||||
// AP* indicates a duplicate request, so don't add anything for those
|
||||
if (
|
||||
response.message === "AP*" &&
|
||||
self._does_credit_payment_line_exist(
|
||||
this._does_credit_payment_line_exist(
|
||||
response.authorize,
|
||||
decodedMagtek["number"],
|
||||
response.card_type,
|
||||
@@ -303,13 +303,13 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
});
|
||||
} else {
|
||||
// If the payment is approved, add a payment line
|
||||
var order = self.env.pos.get_order();
|
||||
var order = globalState.get_order();
|
||||
|
||||
if (swipe_pending_line) {
|
||||
order.select_paymentline(swipe_pending_line);
|
||||
} else {
|
||||
order.add_paymentline(
|
||||
self.payment_methods_by_id[parsed_result.payment_method_id]
|
||||
this.payment_methods_by_id[parsed_result.payment_method_id]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
|
||||
if (response.message === "PARTIAL AP") {
|
||||
def.resolve({
|
||||
message: self.env._t("Partially approved"),
|
||||
message: this.env._t("Partially approved"),
|
||||
auto_close: false,
|
||||
});
|
||||
} else {
|
||||
@@ -347,12 +347,12 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
else {
|
||||
if (lookUpCodeTransaction["TimeoutError"][response.error]) {
|
||||
// recoverable error
|
||||
self.retry_mercury_transaction(
|
||||
this.retry_mercury_transaction(
|
||||
def,
|
||||
response,
|
||||
retry_nr,
|
||||
true,
|
||||
self.credit_code_transaction,
|
||||
this.credit_code_transaction,
|
||||
[parsed_result, def, retry_nr + 1]
|
||||
);
|
||||
} else {
|
||||
@@ -364,13 +364,13 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
self.retry_mercury_transaction(
|
||||
.catch(() => {
|
||||
this.retry_mercury_transaction(
|
||||
def,
|
||||
null,
|
||||
retry_nr,
|
||||
false,
|
||||
self.credit_code_transaction,
|
||||
this.credit_code_transaction,
|
||||
[parsed_result, def, retry_nr + 1]
|
||||
);
|
||||
});
|
||||
@@ -379,7 +379,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
return;
|
||||
},
|
||||
credit_code_action(parsed_result) {
|
||||
var online_payment_methods = this.env.pos.getOnlinePaymentMethods();
|
||||
var online_payment_methods = this.pos.globalState.getOnlinePaymentMethods();
|
||||
|
||||
if (online_payment_methods.length === 1) {
|
||||
parsed_result.payment_method_id = online_payment_methods[0].item;
|
||||
@@ -408,7 +408,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
}
|
||||
},
|
||||
remove_paymentline_by_ref(line) {
|
||||
this.env.pos.get_order().remove_paymentline(line);
|
||||
this.pos.globalState.get_order().remove_paymentline(line);
|
||||
this.numberBuffer.reset();
|
||||
},
|
||||
do_reversal(line, is_voidsale, old_deferred, retry_nr) {
|
||||
@@ -467,7 +467,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
return;
|
||||
}
|
||||
|
||||
var response = self.env.pos.decodeMercuryResponse(data);
|
||||
var response = self.pos.globalState.decodeMercuryResponse(data);
|
||||
|
||||
if (!is_voidsale) {
|
||||
if (response.status != "Approved" || response.message != "REVERSED") {
|
||||
@@ -520,7 +520,7 @@ patch(PaymentScreen.prototype, "pos_mercury.PaymentScreen", {
|
||||
* @override
|
||||
*/
|
||||
addNewPaymentLine(paymentMethod) {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
const res = this._super(...arguments);
|
||||
if (res && paymentMethod.pos_mercury_config_id) {
|
||||
order.selected_paymentline.mercury_swipe_pending = true;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { getLimits, useMovable, constrain } from "@point_of_sale/app/movable_hook";
|
||||
import { onWillUnmount, useEffect, useRef, Component } from "@odoo/owl";
|
||||
import { Table } from "./table";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
const MIN_TABLE_SIZE = 30; // px
|
||||
|
||||
@@ -16,7 +17,7 @@ export class EditableTable extends Component {
|
||||
};
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
useEffect(this._setElementStyle.bind(this));
|
||||
this.root = useRef("root");
|
||||
this.handles = {
|
||||
@@ -43,7 +44,7 @@ export class EditableTable extends Component {
|
||||
}
|
||||
|
||||
onMoveStart() {
|
||||
if (this.env.pos.floorPlanStyle == 'kanban') {
|
||||
if (this.pos.globalState.floorPlanStyle == "kanban") {
|
||||
return;
|
||||
}
|
||||
this.startTable = { ...this.props.table };
|
||||
@@ -59,7 +60,7 @@ export class EditableTable extends Component {
|
||||
}
|
||||
|
||||
onMove({ dx, dy }) {
|
||||
if (this.env.pos.floorPlanStyle == 'kanban') {
|
||||
if (this.pos.globalState.floorPlanStyle == "kanban") {
|
||||
return;
|
||||
}
|
||||
const { minX, minY, maxX, maxY } = getLimits(this.root.el, this.props.limit.el);
|
||||
@@ -70,12 +71,12 @@ export class EditableTable extends Component {
|
||||
this.props.selectedTables[index].position_h = constrain(position_h + dx, minX, maxX);
|
||||
this.props.selectedTables[index].position_v = constrain(position_v + dy, minY, maxY);
|
||||
}
|
||||
|
||||
|
||||
this._setElementStyle();
|
||||
}
|
||||
|
||||
onResizeHandleMove([moveX, moveY], { dx, dy }) {
|
||||
if (this.env.pos.floorPlanStyle == 'kanban') {
|
||||
if (this.pos.globalState.floorPlanStyle == "kanban") {
|
||||
return;
|
||||
}
|
||||
// Working with min/max x and y makes constraints much easier to apply uniformly
|
||||
@@ -127,14 +128,18 @@ export class EditableTable extends Component {
|
||||
|
||||
_setElementStyle() {
|
||||
const table = this.props.table;
|
||||
if (this.env.pos.floorPlanStyle == 'kanban') {
|
||||
if (this.pos.globalState.floorPlanStyle == "kanban") {
|
||||
const floor = table.floor;
|
||||
const index = floor.tables.indexOf(table);
|
||||
const minWidth = 100 + 20;
|
||||
const nbrHorizontal = Math.floor(window.innerWidth / minWidth);
|
||||
const widthTable = (window.innerWidth - nbrHorizontal*10) / nbrHorizontal;
|
||||
const position_h = widthTable * (index % nbrHorizontal) + 5 + (index % nbrHorizontal) * 10;
|
||||
const position_v = widthTable * Math.floor(index / nbrHorizontal) + 5 + Math.floor(index / nbrHorizontal) * 10;
|
||||
const widthTable = (window.innerWidth - nbrHorizontal * 10) / nbrHorizontal;
|
||||
const position_h =
|
||||
widthTable * (index % nbrHorizontal) + 5 + (index % nbrHorizontal) * 10;
|
||||
const position_v =
|
||||
widthTable * Math.floor(index / nbrHorizontal) +
|
||||
5 +
|
||||
Math.floor(index / nbrHorizontal) * 10;
|
||||
|
||||
Object.assign(this.root.el.style, {
|
||||
left: `${position_h}px`,
|
||||
@@ -145,7 +150,7 @@ export class EditableTable extends Component {
|
||||
"line-height": `${widthTable}px`,
|
||||
"border-radius": table.shape === "round" ? "1000px" : "3px",
|
||||
"font-size": widthTable >= 150 ? "32px" : "16px",
|
||||
"opacity": "0.7",
|
||||
opacity: "0.7",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="table-seats">
|
||||
<t t-esc="props.table.seats" />
|
||||
</span>
|
||||
<t t-if="env.pos.floorPlanStyle != 'kanban'" t-foreach="handles" t-as="handle" t-key="handle">
|
||||
<t t-if="pos.globalState.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>
|
||||
|
||||
@@ -28,7 +28,7 @@ export class FloorScreen extends Component {
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.orm = useService("orm");
|
||||
const floor = this.props.floor ? this.props.floor : this.env.pos.floors[0];
|
||||
const floor = this.props.floor || this.pos.globalState.floors[0];
|
||||
this.state = useState({
|
||||
selectedFloorId: floor ? floor.id : null,
|
||||
selectedTableIds: [],
|
||||
@@ -36,11 +36,11 @@ export class FloorScreen extends Component {
|
||||
floorMapScrollTop: 0,
|
||||
isColorPicker: false,
|
||||
});
|
||||
if (!this.env.pos.floorPlanStyle) {
|
||||
if (!this.pos.globalState.floorPlanStyle) {
|
||||
if (this.env.isMobile) {
|
||||
this.env.pos.floorPlanStyle = "kanban";
|
||||
this.pos.globalState.floorPlanStyle = "kanban";
|
||||
} else {
|
||||
this.env.pos.floorPlanStyle = "default";
|
||||
this.pos.globalState.floorPlanStyle = "default";
|
||||
}
|
||||
}
|
||||
this.floorMapRef = useRef("floor-map-ref");
|
||||
@@ -53,7 +53,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.env.pos.isEditMode && this.env.pos.floors.length > 0) {
|
||||
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
|
||||
this.addFloorRef.el.style.display = "none";
|
||||
} else {
|
||||
this.addFloorRef.el.style.display = "initial";
|
||||
@@ -64,7 +64,7 @@ export class FloorScreen extends Component {
|
||||
this.pos.openCashControl();
|
||||
this.floorMapRef.el.style.background = this.state.floorBackground;
|
||||
this.addFloorRef.el.style.background = this.state.floorBackground;
|
||||
if (!this.env.pos.isEditMode && this.env.pos.floors.length > 0) {
|
||||
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
|
||||
this.addFloorRef.el.style.display = "none";
|
||||
} else {
|
||||
this.addFloorRef.el.style.display = "initial";
|
||||
@@ -249,20 +249,21 @@ export class FloorScreen extends Component {
|
||||
delete tableCopy.floor;
|
||||
const tableId = await this.orm.call("restaurant.table", "create_from_ui", [tableCopy]);
|
||||
table.id = tableId;
|
||||
this.env.pos.tables_by_id[tableId] = table;
|
||||
this.pos.globalState.tables_by_id[tableId] = table;
|
||||
}
|
||||
async _renameFloor(floorId, newName) {
|
||||
await this.orm.call("restaurant.floor", "rename_floor", [floorId, newName]);
|
||||
}
|
||||
async _tableLongpolling() {
|
||||
if (this.env.pos.isEditMode) {
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.isEditMode) {
|
||||
return;
|
||||
}
|
||||
const result = await this.orm.call("pos.config", "get_tables_order_count", [
|
||||
this.env.pos.config.id,
|
||||
globalState.config.id,
|
||||
]);
|
||||
for (const table of result) {
|
||||
if (!(table.id in this.env.pos.tables_by_id)) {
|
||||
if (!(table.id in globalState.tables_by_id)) {
|
||||
//We enter this condition if, after loading our PoS, a table is deleted on a PoS and not on another.
|
||||
//This will lead in receiving information on orders from a table that we do not have.
|
||||
//If we do not have a table in the PoS but still receive information about it, we reload the tables
|
||||
@@ -270,13 +271,13 @@ export class FloorScreen extends Component {
|
||||
const result = await this.orm.call("pos.session", "get_pos_ui_restaurant_floor", [
|
||||
[odoo.pos_session_id],
|
||||
]);
|
||||
if (this.env.pos.config.module_pos_restaurant) {
|
||||
this.env.pos.floors = result;
|
||||
this.env.pos.loadRestaurantFloor();
|
||||
if (globalState.config.module_pos_restaurant) {
|
||||
globalState.floors = result;
|
||||
globalState.loadRestaurantFloor();
|
||||
}
|
||||
}
|
||||
const table_obj = this.env.pos.tables_by_id[table.id];
|
||||
const unsynced_orders = this.env.pos.getTableOrders(table_obj.id).filter(
|
||||
const table_obj = globalState.tables_by_id[table.id];
|
||||
const unsynced_orders = globalState.getTableOrders(table_obj.id).filter(
|
||||
(o) =>
|
||||
o.server_id === undefined &&
|
||||
(o.orderlines.length !== 0 || o.paymentlines.length !== 0) &&
|
||||
@@ -288,7 +289,7 @@ export class FloorScreen extends Component {
|
||||
}
|
||||
get activeFloor() {
|
||||
return this.state.selectedFloorId
|
||||
? this.env.pos.floors_by_id[this.state.selectedFloorId]
|
||||
? this.pos.globalState.floors_by_id[this.state.selectedFloorId]
|
||||
: null;
|
||||
}
|
||||
get activeTables() {
|
||||
@@ -300,12 +301,12 @@ export class FloorScreen extends Component {
|
||||
get selectedTables() {
|
||||
const tables = [];
|
||||
this.state.selectedTableIds.forEach((id) => {
|
||||
tables.push(this.env.pos.tables_by_id[id]);
|
||||
tables.push(this.pos.globalState.tables_by_id[id]);
|
||||
});
|
||||
return tables;
|
||||
}
|
||||
get nbrFloors() {
|
||||
return this.env.pos.floors.length;
|
||||
return this.pos.globalState.floors.length;
|
||||
}
|
||||
movePinch(hypot) {
|
||||
const delta = hypot / this.scalehypot;
|
||||
@@ -333,8 +334,8 @@ export class FloorScreen extends Component {
|
||||
this.state.selectedTableIds = [];
|
||||
}
|
||||
toggleEditMode() {
|
||||
this.env.pos.toggleEditMode();
|
||||
if (!this.env.pos.isEditMode && this.env.pos.floors.length > 0) {
|
||||
this.pos.globalState.toggleEditMode();
|
||||
if (!this.pos.globalState.isEditMode && this.pos.globalState.floors.length > 0) {
|
||||
this.addFloorRef.el.style.display = "none";
|
||||
} else {
|
||||
this.addFloorRef.el.style.display = "initial";
|
||||
@@ -342,7 +343,8 @@ export class FloorScreen extends Component {
|
||||
this.state.selectedTableIds = [];
|
||||
}
|
||||
async onSelectTable(table, ev) {
|
||||
if (this.env.pos.isEditMode) {
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.isEditMode) {
|
||||
if (ev.ctrlKey || ev.metaKey) {
|
||||
this.state.selectedTableIds.push(table.id);
|
||||
} else {
|
||||
@@ -350,11 +352,11 @@ export class FloorScreen extends Component {
|
||||
this.state.selectedTableIds.push(table.id);
|
||||
}
|
||||
} else {
|
||||
if (this.env.pos.orderToTransfer) {
|
||||
await this.env.pos.transferTable(table);
|
||||
if (globalState.orderToTransfer) {
|
||||
await globalState.transferTable(table);
|
||||
} else {
|
||||
try {
|
||||
await this.env.pos.setTable(table);
|
||||
await globalState.setTable(table);
|
||||
} catch (e) {
|
||||
if (!(e.message instanceof ConnectionLostError)) {
|
||||
throw e;
|
||||
@@ -363,7 +365,7 @@ export class FloorScreen extends Component {
|
||||
Promise.reject(e);
|
||||
}
|
||||
}
|
||||
const order = this.env.pos.get_order();
|
||||
const order = globalState.get_order();
|
||||
this.pos.showScreen(order.get_screen_data().name);
|
||||
}
|
||||
}
|
||||
@@ -377,15 +379,16 @@ 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",
|
||||
this.env.pos.config.id,
|
||||
globalState.config.id,
|
||||
]);
|
||||
this.env.pos.floors_by_id[floor.id] = floor;
|
||||
this.env.pos.floors.push(floor);
|
||||
globalState.floors_by_id[floor.id] = floor;
|
||||
globalState.floors.push(floor);
|
||||
this.selectFloor(floor);
|
||||
this.env.pos.isEditMode = true;
|
||||
globalState.isEditMode = true;
|
||||
}
|
||||
async createTable() {
|
||||
const newTable = await this._createTableHelper();
|
||||
@@ -396,16 +399,17 @@ 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,
|
||||
this.env.pos.config.id,
|
||||
globalState.config.id,
|
||||
]);
|
||||
this.env.pos.floors_by_id[newFloor.id] = newFloor;
|
||||
this.env.pos.floors.push(newFloor);
|
||||
globalState.floors_by_id[newFloor.id] = newFloor;
|
||||
globalState.floors.push(newFloor);
|
||||
this.selectFloor(newFloor);
|
||||
for (const table of tables) {
|
||||
await this._createTableHelper(table, true);
|
||||
@@ -491,7 +495,6 @@ export class FloorScreen extends Component {
|
||||
selectedTable.shape = form;
|
||||
await this._save(selectedTable);
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
async setTableColor(color) {
|
||||
const selectedTables = this.selectedTables;
|
||||
@@ -499,7 +502,6 @@ export class FloorScreen extends Component {
|
||||
selectedTable.color = color;
|
||||
await this._save(selectedTable);
|
||||
});
|
||||
this.render();
|
||||
this.state.isColorPicker = false;
|
||||
}
|
||||
async setFloorColor(color) {
|
||||
@@ -514,6 +516,7 @@ 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}`,
|
||||
@@ -528,28 +531,28 @@ export class FloorScreen extends Component {
|
||||
const originalSelectedFloorId = this.activeFloor.id;
|
||||
await this.orm.call("restaurant.floor", "deactivate_floor", [
|
||||
originalSelectedFloorId,
|
||||
this.env.pos.pos_session.id,
|
||||
globalState.pos_session.id,
|
||||
]);
|
||||
const floor = this.env.pos.floors_by_id[originalSelectedFloorId];
|
||||
const orderList = [...this.env.pos.get_order_list()];
|
||||
const floor = globalState.floors_by_id[originalSelectedFloorId];
|
||||
const orderList = [...globalState.get_order_list()];
|
||||
for (const order of orderList) {
|
||||
if (floor.table_ids.includes(order.tableId)) {
|
||||
this.env.pos.removeOrder(order, false);
|
||||
globalState.removeOrder(order, false);
|
||||
}
|
||||
}
|
||||
floor.table_ids.forEach((tableId) => {
|
||||
delete this.env.pos.tables_by_id[tableId];
|
||||
delete globalState.tables_by_id[tableId];
|
||||
});
|
||||
delete this.env.pos.floors_by_id[originalSelectedFloorId];
|
||||
this.env.pos.floors = this.env.pos.floors.filter(
|
||||
delete globalState.floors_by_id[originalSelectedFloorId];
|
||||
globalState.floors = globalState.floors.filter(
|
||||
(floor) => floor.id != originalSelectedFloorId
|
||||
);
|
||||
this.env.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {};
|
||||
if (this.env.pos.floors.length > 0) {
|
||||
this.selectFloor(this.env.pos.floors[0]);
|
||||
globalState.TICKET_SCREEN_STATE.syncedOrders.cache = {};
|
||||
if (globalState.floors.length > 0) {
|
||||
this.selectFloor(globalState.floors[0]);
|
||||
} else {
|
||||
this.env.pos.isEditMode = false;
|
||||
this.env.pos.floorPlanStyle = "default";
|
||||
globalState.isEditMode = false;
|
||||
globalState.floorPlanStyle = "default";
|
||||
this.state.floorBackground = null;
|
||||
}
|
||||
return;
|
||||
@@ -568,17 +571,17 @@ export class FloorScreen extends Component {
|
||||
]);
|
||||
if (!response) {
|
||||
//remove order not send to server
|
||||
for (const order of this.env.pos.get_order_list()) {
|
||||
for (const order of globalState.get_order_list()) {
|
||||
if (order.tableId == id) {
|
||||
this.env.pos.removeOrder(order, false);
|
||||
globalState.removeOrder(order, false);
|
||||
}
|
||||
}
|
||||
this.env.pos.tables_by_id[id].active = false;
|
||||
globalState.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 this.env.pos.tables_by_id[id];
|
||||
delete globalState.tables_by_id[id];
|
||||
} else {
|
||||
await this.popup.add(ErrorPopup, {
|
||||
title: this.env._t("Delete Error"),
|
||||
@@ -600,7 +603,7 @@ export class FloorScreen extends Component {
|
||||
if (equalsCheck(this.state.selectedTableIds, originalSelectedTableIds)) {
|
||||
this.state.selectedTableIds = [];
|
||||
}
|
||||
this.env.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {};
|
||||
globalState.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="env.pos.isEditMode" selectedTables="selectedTables" nbrFloors="nbrFloors"
|
||||
<EditBar t-if="pos.globalState.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="env.pos.floors" t-as="floor" t-key="floor.id">
|
||||
<t t-foreach="pos.globalState.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="env.pos.floors.length > 0">
|
||||
<t t-if="pos.globalState.floors.length > 0">
|
||||
<div t-if="isFloorEmpty" class="empty-floor" t-ref="map">
|
||||
<span>No tables available,<br/>ADD your first table</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class Table extends Component {
|
||||
static template = "pos_restaurant.Table";
|
||||
@@ -22,17 +23,24 @@ export class Table extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get style() {
|
||||
const table = this.props.table;
|
||||
|
||||
if (this.env.pos.floorPlanStyle == "kanban") {
|
||||
if (this.pos.globalState.floorPlanStyle == "kanban") {
|
||||
const floor = table.floor;
|
||||
const index = floor.tables.indexOf(table);
|
||||
const minWidth = 100 + 20;
|
||||
const nbrHorizontal = Math.floor(window.innerWidth / minWidth);
|
||||
const widthTable = (window.innerWidth - nbrHorizontal*10) / nbrHorizontal;
|
||||
const position_h = widthTable * (index % nbrHorizontal) + 5 + (index % nbrHorizontal) * 10;
|
||||
const position_v = widthTable * Math.floor(index / nbrHorizontal) + 5 + Math.floor(index / nbrHorizontal) * 10;
|
||||
const widthTable = (window.innerWidth - nbrHorizontal * 10) / nbrHorizontal;
|
||||
const position_h =
|
||||
widthTable * (index % nbrHorizontal) + 5 + (index % nbrHorizontal) * 10;
|
||||
const position_v =
|
||||
widthTable * Math.floor(index / nbrHorizontal) +
|
||||
5 +
|
||||
Math.floor(index / nbrHorizontal) * 10;
|
||||
return `
|
||||
width: ${widthTable}px;
|
||||
height: ${widthTable}px;
|
||||
@@ -57,20 +65,20 @@ export class Table extends Component {
|
||||
}
|
||||
}
|
||||
get fill() {
|
||||
const customerCount = this.env.pos.getCustomerCount(this.props.table.id);
|
||||
const customerCount = this.pos.globalState.getCustomerCount(this.props.table.id);
|
||||
return Math.min(1, Math.max(0, customerCount / this.props.table.seats));
|
||||
}
|
||||
get orderCount() {
|
||||
const table = this.props.table;
|
||||
return table.order_count !== undefined
|
||||
? table.order_count
|
||||
: this.env.pos
|
||||
: this.pos.globalState
|
||||
.getTableOrders(table.id)
|
||||
.filter((o) => o.orderlines.length !== 0 || o.paymentlines.length !== 0).length;
|
||||
}
|
||||
get orderCountClass() {
|
||||
const countClass = { "order-count": true };
|
||||
if (this.env.pos.orderPreparationCategories.size) {
|
||||
if (this.pos.globalState.orderPreparationCategories.size) {
|
||||
const notifications = this._getNotifications();
|
||||
countClass["notify-printing"] = notifications.printing;
|
||||
countClass["notify-skipped"] = notifications.skipped;
|
||||
@@ -78,10 +86,12 @@ export class Table extends Component {
|
||||
return countClass;
|
||||
}
|
||||
get customerCountDisplay() {
|
||||
return `${this.env.pos.getCustomerCount(this.props.table.id)}/${this.props.table.seats}`;
|
||||
return `${this.pos.globalState.getCustomerCount(this.props.table.id)}/${
|
||||
this.props.table.seats
|
||||
}`;
|
||||
}
|
||||
_getNotifications() {
|
||||
const orders = this.env.pos.getTableOrders(this.props.table.id);
|
||||
const orders = this.pos.globalState.getTableOrders(this.props.table.id);
|
||||
|
||||
let hasChangesCount = 0;
|
||||
let hasSkippedCount = 0;
|
||||
|
||||
@@ -11,7 +11,7 @@ export class BackToFloorButton extends Component {
|
||||
this.pos = usePos();
|
||||
}
|
||||
get table() {
|
||||
return this.env.pos.table;
|
||||
return this.pos.globalState.table;
|
||||
}
|
||||
get floor() {
|
||||
return this.table ? this.table.floor : null;
|
||||
|
||||
@@ -11,22 +11,23 @@ patch(Navbar.prototype, "pos_restaurant.Navbar", {
|
||||
* is floor screen, then the order count should be based on all the orders.
|
||||
*/
|
||||
get orderCount() {
|
||||
if (!this.env.pos || !this.env.pos.config) {
|
||||
return 0;
|
||||
}
|
||||
if (this.env.pos.config.module_pos_restaurant && this.env.pos.table) {
|
||||
return this.env.pos.getTableOrders(this.env.pos.table.id).length;
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.config.module_pos_restaurant && globalState.table) {
|
||||
return globalState.getTableOrders(globalState.table.id).length;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
_shouldLoadOrders() {
|
||||
return this._super() || (this.env.pos.config.module_pos_restaurant && !this.env.pos.table);
|
||||
return (
|
||||
this._super() ||
|
||||
(this.pos.globalState.config.module_pos_restaurant && !this.pos.globalState.table)
|
||||
);
|
||||
},
|
||||
onSwitchButtonClick() {
|
||||
this.env.pos.floorPlanStyle =
|
||||
this.env.pos.floorPlanStyle == "kanban" ? "default" : "kanban";
|
||||
this.pos.globalState.floorPlanStyle =
|
||||
this.pos.globalState.floorPlanStyle == "kanban" ? "default" : "kanban";
|
||||
},
|
||||
toggleEditMode() {
|
||||
this.env.pos.toggleEditMode();
|
||||
this.pos.globalState.toggleEditMode();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ patch(PaymentScreen.prototype, "pos_restaurant.PaymentScreen", {
|
||||
},
|
||||
get nextScreen() {
|
||||
const order = this.currentOrder;
|
||||
if (!this.env.pos.config.set_tip_after_payment || order.is_tipped) {
|
||||
if (!this.pos.globalState.config.set_tip_after_payment || order.is_tipped) {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
// Take the first payment method as the main payment.
|
||||
|
||||
+4
-3
@@ -4,16 +4,17 @@ import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductSc
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { TextAreaPopup } from "@point_of_sale/js/Popups/TextAreaPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class OrderlineNoteButton extends Component {
|
||||
static template = "OrderlineNoteButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
get selectedOrderline() {
|
||||
return this.env.pos.get_order().get_selected_orderline();
|
||||
return this.pos.globalState.get_order().get_selected_orderline();
|
||||
}
|
||||
async click() {
|
||||
if (!this.selectedOrderline) {
|
||||
@@ -34,6 +35,6 @@ export class OrderlineNoteButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: OrderlineNoteButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.iface_orderline_notes;
|
||||
return this.pos.globalState.config.iface_orderline_notes;
|
||||
},
|
||||
});
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ export class PrintBillButton extends Component {
|
||||
this.pos = usePos();
|
||||
}
|
||||
_isDisabled() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
return order.get_orderlines().length === 0;
|
||||
}
|
||||
click() {
|
||||
@@ -23,6 +23,6 @@ export class PrintBillButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: PrintBillButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.iface_printbill;
|
||||
return this.pos.globalState.config.iface_printbill;
|
||||
},
|
||||
});
|
||||
|
||||
+2
-3
@@ -8,11 +8,10 @@ export class SplitBillButton extends Component {
|
||||
static template = "SplitBillButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
}
|
||||
_isDisabled() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
return (
|
||||
order
|
||||
.get_orderlines()
|
||||
@@ -27,6 +26,6 @@ export class SplitBillButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: SplitBillButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.iface_splitbill;
|
||||
return this.pos.globalState.config.iface_splitbill;
|
||||
},
|
||||
});
|
||||
|
||||
+5
-4
@@ -6,16 +6,17 @@ import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class TableGuestsButton extends Component {
|
||||
static template = "TableGuestsButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get nGuests() {
|
||||
return this.currentOrder ? this.currentOrder.getCustomerCount() : 0;
|
||||
@@ -42,7 +43,7 @@ export class TableGuestsButton extends Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.env.pos.get_order().setCustomerCount(guestCount);
|
||||
this.currentOrder.setCustomerCount(guestCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +51,6 @@ export class TableGuestsButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: TableGuestsButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.module_pos_restaurant;
|
||||
return this.pos.globalState.config.module_pos_restaurant;
|
||||
},
|
||||
});
|
||||
|
||||
+2
-3
@@ -8,11 +8,10 @@ export class TransferOrderButton extends Component {
|
||||
static template = "TransferOrderButton";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
}
|
||||
async click() {
|
||||
this.env.pos.setCurrentOrderToTransfer();
|
||||
this.pos.globalState.setCurrentOrderToTransfer();
|
||||
this.pos.showScreen("FloorScreen");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +19,6 @@ export class TransferOrderButton extends Component {
|
||||
ProductScreen.addControlButton({
|
||||
component: TransferOrderButton,
|
||||
condition: function () {
|
||||
return this.env.pos.config.module_pos_restaurant;
|
||||
return this.pos.globalState.config.module_pos_restaurant;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ReceiptScreen } from "@point_of_sale/js/Screens/ReceiptScreen/ReceiptScreen";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { onWillUnmount } from "@odoo/owl";
|
||||
import { FloorScreen } from "@pos_restaurant/app/floor_screen/floor_screen";
|
||||
@@ -13,30 +12,29 @@ patch(ReceiptScreen, "pos_restaurant.ReceiptScreen", {
|
||||
patch(ReceiptScreen.prototype, "pos_restaurant.ReceiptScreen", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.pos = usePos();
|
||||
onWillUnmount(() => {
|
||||
// When leaving the receipt screen to the floor screen the order is paid and can be removed
|
||||
if (this.pos.mainScreen.component === FloorScreen) {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
this.pos.globalState.removeOrder(this.currentOrder);
|
||||
}
|
||||
});
|
||||
},
|
||||
//@override
|
||||
_addNewOrder() {
|
||||
if (!this.env.pos.config.module_pos_restaurant) {
|
||||
if (!this.pos.globalState.config.module_pos_restaurant) {
|
||||
this._super(...arguments);
|
||||
}
|
||||
},
|
||||
isResumeVisible() {
|
||||
if (this.env.pos.config.module_pos_restaurant && this.env.pos.table) {
|
||||
return this.env.pos.getTableOrders(this.env.pos.table.id).length > 1;
|
||||
if (this.pos.globalState.config.module_pos_restaurant && this.pos.globalState.table) {
|
||||
return this.pos.globalState.getTableOrders(this.pos.globalState.table.id).length > 1;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
//@override
|
||||
get nextScreen() {
|
||||
if (this.env.pos.config.module_pos_restaurant) {
|
||||
const table = this.env.pos.table;
|
||||
if (this.pos.globalState.config.module_pos_restaurant) {
|
||||
const table = this.pos.globalState.table;
|
||||
return { name: "FloorScreen", props: { floor: table ? table.floor : null } };
|
||||
} else {
|
||||
return this._super(...arguments);
|
||||
|
||||
@@ -5,35 +5,28 @@ import { Order } from "@point_of_sale/js/models";
|
||||
import { SplitOrderline } from "./SplitOrderline";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { Component, useState, onMounted } from "@odoo/owl";
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
export class SplitBillScreen extends Component {
|
||||
static template = "SplitBillScreen";
|
||||
static components = { SplitOrderline };
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.splitlines = useState(this._initSplitLines(this.env.pos.get_order()));
|
||||
this.splitlines = useState(this._initSplitLines(this.pos.globalState.get_order()));
|
||||
this.newOrderLines = {};
|
||||
this.newOrder = undefined;
|
||||
this._isFinal = false;
|
||||
onMounted(() => {
|
||||
// Should create the new order outside of the constructor because
|
||||
// sequence_number of pos_session is modified. which will trigger
|
||||
// rerendering which will rerender this screen and will be infinite loop.
|
||||
this.newOrder = new Order(
|
||||
{},
|
||||
{
|
||||
pos: this.env.pos,
|
||||
temporary: true,
|
||||
}
|
||||
);
|
||||
this.render();
|
||||
});
|
||||
this.newOrder = new Order(
|
||||
{},
|
||||
{
|
||||
pos: this.pos.globalState,
|
||||
temporary: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get orderlines() {
|
||||
return this.currentOrder.get_orderlines();
|
||||
@@ -59,13 +52,14 @@ export class SplitBillScreen extends Component {
|
||||
|
||||
this.newOrder.set_screen_data({ name: "PaymentScreen" });
|
||||
|
||||
const { globalState } = this.pos;
|
||||
// for the kitchen printer we assume that everything
|
||||
// has already been sent to the kitchen before splitting
|
||||
// the bill. So we save all changes both for the old
|
||||
// order and for the new one. This is not entirely correct
|
||||
// but avoids flooding the kitchen with unnecessary orders.
|
||||
// Not sure what to do in this case.
|
||||
if (this.env.pos.orderPreparationCategories.size) {
|
||||
if (globalState.orderPreparationCategories.size) {
|
||||
this.currentOrder.updateLastOrderChange();
|
||||
this.newOrder.updateLastOrderChange();
|
||||
}
|
||||
@@ -75,9 +69,9 @@ export class SplitBillScreen extends Component {
|
||||
this.currentOrder.setCustomerCount(newCustomerCount || 1);
|
||||
this.currentOrder.set_screen_data({ name: "ProductScreen" });
|
||||
|
||||
const reactiveNewOrder = this.env.pos.makeOrderReactive(this.newOrder);
|
||||
this.env.pos.orders.add(reactiveNewOrder);
|
||||
this.env.pos.selectedOrder = reactiveNewOrder;
|
||||
const reactiveNewOrder = globalState.makeOrderReactive(this.newOrder);
|
||||
globalState.orders.add(reactiveNewOrder);
|
||||
globalState.selectedOrder = reactiveNewOrder;
|
||||
}
|
||||
this.pos.showScreen("PaymentScreen");
|
||||
}
|
||||
@@ -97,7 +91,7 @@ export class SplitBillScreen extends Component {
|
||||
|
||||
let totalQuantity = 0;
|
||||
|
||||
this.env.pos
|
||||
this.pos.globalState
|
||||
.get_order()
|
||||
.get_orderlines()
|
||||
.forEach(function (orderLine) {
|
||||
@@ -143,7 +137,7 @@ export class SplitBillScreen extends Component {
|
||||
}
|
||||
}
|
||||
_isFullPayOrder() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
let full = true;
|
||||
const splitlines = this.splitlines;
|
||||
const groupedLines = _.groupBy(order.get_orderlines(), (line) => line.get_product().id);
|
||||
@@ -167,7 +161,7 @@ export class SplitBillScreen extends Component {
|
||||
return full;
|
||||
}
|
||||
_setQuantityOnCurrentOrder() {
|
||||
const order = this.env.pos.get_order();
|
||||
const order = this.pos.globalState.get_order();
|
||||
for (var id in this.splitlines) {
|
||||
var split = this.splitlines[id];
|
||||
var line = this.currentOrder.get_orderline(parseInt(id));
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Component, useState } from "@odoo/owl";
|
||||
patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
_getScreenToStatusMap() {
|
||||
return Object.assign(this._super(...arguments), {
|
||||
PaymentScreen: this.env.pos.config.set_tip_after_payment
|
||||
PaymentScreen: this.pos.globalState.config.set_tip_after_payment
|
||||
? "OPEN"
|
||||
: this._super(...arguments).PaymentScreen,
|
||||
TipScreen: "TIPPING",
|
||||
@@ -22,7 +22,7 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
},
|
||||
//@override
|
||||
_getSearchFields() {
|
||||
if (!this.env.pos.config.module_pos_restaurant) {
|
||||
if (!this.pos.globalState.config.module_pos_restaurant) {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
return Object.assign({}, this._super(...arguments), {
|
||||
@@ -34,23 +34,23 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
});
|
||||
},
|
||||
async _setOrder(order) {
|
||||
if (!this.env.pos.config.module_pos_restaurant || this.env.pos.table) {
|
||||
this._super(order);
|
||||
} else {
|
||||
// we came from the FloorScreen
|
||||
const orderTable = order.getTable();
|
||||
await this.env.pos.setTable(orderTable, order.uid);
|
||||
this.close();
|
||||
const { globalState } = this.pos;
|
||||
if (!globalState.config.module_pos_restaurant || globalState.table) {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
// we came from the FloorScreen
|
||||
const orderTable = order.getTable();
|
||||
await globalState.setTable(orderTable, order.uid);
|
||||
this.close();
|
||||
},
|
||||
get allowNewOrders() {
|
||||
return this.env.pos.config.module_pos_restaurant
|
||||
? Boolean(this.env.pos.table)
|
||||
return this.pos.globalState.config.module_pos_restaurant
|
||||
? Boolean(this.pos.globalState.table)
|
||||
: this._super(...arguments);
|
||||
},
|
||||
_getOrderList() {
|
||||
if (this.env.pos.table) {
|
||||
return this.env.pos.getTableOrders(this.env.pos.table.id);
|
||||
if (this.pos.globalState.table) {
|
||||
return this.pos.globalState.getTableOrders(this.pos.globalState.table.id);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
@@ -58,7 +58,7 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
// set tip in each order
|
||||
for (const order of this.getFilteredOrderList()) {
|
||||
const tipAmount = parseFloat(order.uiState.TipScreen.inputTipAmount || "0");
|
||||
const serverId = this.env.pos.validated_orders_name_server_id_map[order.name];
|
||||
const serverId = this.pos.globalState.validated_orders_name_server_id_map[order.name];
|
||||
if (!serverId) {
|
||||
console.warn(
|
||||
`${order.name} is not yet sync. Sync it to server before setting a tip.`
|
||||
@@ -73,27 +73,29 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
},
|
||||
//@override
|
||||
_selectNextOrder(currentOrder) {
|
||||
if (this.env.pos.config.module_pos_restaurant && this.env.pos.table) {
|
||||
if (this.pos.globalState.config.module_pos_restaurant && this.pos.globalState.table) {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
},
|
||||
//@override
|
||||
async onDeleteOrder(order) {
|
||||
const _super = this._super;
|
||||
if (this.env.pos.config.module_pos_restaurant) {
|
||||
this.env.pos.setOrderToRemove(order);
|
||||
if (!this.env.pos.table) {
|
||||
await this.env.pos._removeOrdersFromServer();
|
||||
const { globalState } = this.pos;
|
||||
if (globalState.config.module_pos_restaurant) {
|
||||
globalState.setOrderToRemove(order);
|
||||
if (!globalState.table) {
|
||||
await globalState._removeOrdersFromServer();
|
||||
}
|
||||
}
|
||||
await _super(...arguments);
|
||||
},
|
||||
async setTip(order, serverId, amount) {
|
||||
const { globalState } = this.pos;
|
||||
try {
|
||||
const paymentline = order.get_paymentlines()[0];
|
||||
if (paymentline.payment_method.payment_terminal) {
|
||||
paymentline.amount += amount;
|
||||
this.env.pos.set_order(order, { silent: true });
|
||||
globalState.set_order(order, { silent: true });
|
||||
await paymentline.payment_method.payment_terminal.send_payment_adjust(
|
||||
paymentline.cid
|
||||
);
|
||||
@@ -108,10 +110,10 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
const tip_line = order.selected_orderline;
|
||||
await this.orm.call("pos.order", "set_tip", [serverId, tip_line.export_as_JSON()]);
|
||||
}
|
||||
if (order === this.env.pos.get_order()) {
|
||||
if (order === globalState.get_order()) {
|
||||
this._selectNextOrder(order);
|
||||
}
|
||||
this.env.pos.removeOrder(order);
|
||||
globalState.removeOrder(order);
|
||||
return true;
|
||||
} catch {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
@@ -126,7 +128,7 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
},
|
||||
_getOrderStates() {
|
||||
const result = this._super(...arguments);
|
||||
if (this.env.pos.config.set_tip_after_payment) {
|
||||
if (this.pos.globalState.config.set_tip_after_payment) {
|
||||
result.delete("PAYMENT");
|
||||
result.set("OPEN", { text: this.env._t("Open"), indented: true });
|
||||
result.set("TIPPING", { text: this.env._t("Tipping"), indented: true });
|
||||
@@ -135,15 +137,15 @@ patch(TicketScreen.prototype, "pos_restaurant.TicketScreen", {
|
||||
},
|
||||
async onDoRefund() {
|
||||
const order = this.getSelectedSyncedOrder();
|
||||
if (this.env.pos.config.module_pos_restaurant && order) {
|
||||
this.env.pos.setTable(
|
||||
order.table ? order.table : Object.values(this.env.pos.tables_by_id)[0]
|
||||
if (this.pos.globalState.config.module_pos_restaurant && order) {
|
||||
this.pos.globalState.setTable(
|
||||
order.table ? order.table : Object.values(this.pos.globalState.tables_by_id)[0]
|
||||
);
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
isDefaultOrderEmpty(order) {
|
||||
if (this.env.pos.config.module_pos_restaurant) {
|
||||
if (this.pos.globalState.config.module_pos_restaurant) {
|
||||
return false;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
|
||||
@@ -37,7 +37,7 @@ export class TipScreen extends Component {
|
||||
return this._totalAmount;
|
||||
}
|
||||
get currentOrder() {
|
||||
return this.env.pos.get_order();
|
||||
return this.pos.globalState.get_order();
|
||||
}
|
||||
get percentageTips() {
|
||||
return [
|
||||
@@ -48,8 +48,8 @@ export class TipScreen extends Component {
|
||||
}
|
||||
async validateTip() {
|
||||
const amount = parseFloat(this.state.inputTipAmount) || 0;
|
||||
const order = this.env.pos.get_order();
|
||||
const serverId = this.env.pos.validated_orders_name_server_id_map[order.name];
|
||||
const order = this.pos.globalState.get_order();
|
||||
const serverId = this.pos.globalState.validated_orders_name_server_id_map[order.name];
|
||||
|
||||
if (!serverId) {
|
||||
this.popup.add(ErrorPopup, {
|
||||
@@ -84,7 +84,7 @@ export class TipScreen extends Component {
|
||||
order.set_tip(amount);
|
||||
order.finalized = true;
|
||||
|
||||
const paymentline = this.env.pos.get_order().get_paymentlines()[0];
|
||||
const paymentline = this.pos.globalState.get_order().get_paymentlines()[0];
|
||||
if (paymentline.payment_method.payment_terminal) {
|
||||
paymentline.amount += amount;
|
||||
await paymentline.payment_method.payment_terminal.send_payment_adjust(paymentline.cid);
|
||||
@@ -96,16 +96,16 @@ export class TipScreen extends Component {
|
||||
this.goNextScreen();
|
||||
}
|
||||
goNextScreen() {
|
||||
this.env.pos.removeOrder(this.currentOrder);
|
||||
if (!this.env.pos.config.module_pos_restaurant) {
|
||||
this.env.pos.add_new_order();
|
||||
this.pos.globalState.removeOrder(this.currentOrder);
|
||||
if (!this.pos.globalState.config.module_pos_restaurant) {
|
||||
this.pos.globalState.add_new_order();
|
||||
}
|
||||
const { name, props } = this.nextScreen;
|
||||
this.pos.showScreen(name, props);
|
||||
}
|
||||
get nextScreen() {
|
||||
if (this.env.pos.config.module_pos_restaurant) {
|
||||
const table = this.env.pos.table;
|
||||
if (this.pos.globalState.config.module_pos_restaurant) {
|
||||
const table = this.pos.globalState.table;
|
||||
return { name: "FloorScreen", props: { floor: table ? table.floor : null } };
|
||||
} else {
|
||||
return { name: "ProductScreen" };
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
|
||||
<t t-name="PaymentScreen" t-inherit="point_of_sale.PaymentScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('button') and hasclass('next')]" position="attributes">
|
||||
<attribute name="t-att-hidden">env.pos.config.set_tip_after_payment and !currentOrder.is_paid()</attribute>
|
||||
<attribute name="t-att-hidden">pos.globalState.config.set_tip_after_payment and !currentOrder.is_paid()</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('button') and hasclass('back')]/span[hasclass('back_text')]" position="replace">
|
||||
<t t-if="env.pos.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<t t-if="pos.globalState.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<span class="back_text">Keep Open</span>
|
||||
</t>
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('button') and hasclass('next')]/span[hasclass('next_text')]" position="replace">
|
||||
<t t-if="env.pos.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<t t-if="pos.globalState.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<span class="back_text">Close Tab</span>
|
||||
</t>
|
||||
<t t-else="">$0</t>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('before-footer')]" position="after">
|
||||
<t t-if="props.isBill and env.pos.config.set_tip_after_payment">
|
||||
<t t-if="props.isBill and pos.globalState.config.set_tip_after_payment">
|
||||
<div class="tip-form">
|
||||
<div class="title">For convenience, we are providing the following gratuity calculations:</div>
|
||||
<div class="percentage-options">
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
<t t-name="TicketScreen" t-inherit="point_of_sale.TicketScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('header-row')]//div[@name='delete']" position="before">
|
||||
<div t-if="env.pos.config.module_pos_restaurant" class="col" name="table">Table</div>
|
||||
<div t-if="pos.globalState.config.module_pos_restaurant" class="col" name="table">Table</div>
|
||||
<div t-if="_state.ui.filter == 'TIPPING'" class="col end narrow" name="tip">Tip</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('order-row')]//div[@name='delete']" position="before">
|
||||
<div t-if="env.pos.config.module_pos_restaurant" class="col" name="table">
|
||||
<div t-if="pos.globalState.config.module_pos_restaurant" class="col" name="table">
|
||||
<t t-if="order.tableId">
|
||||
<div t-if="env.isMobile">Table</div>
|
||||
<div><t t-esc="getTable(order)"></t></div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<div class="item input">
|
||||
<input type="text" t-model="state.inputTipAmount" t-att-data-amount="state.inputTipAmount" />
|
||||
<div class="currency">
|
||||
<t t-esc="env.pos.getCurrencySymbol()" />
|
||||
<t t-esc="pos.globalState.getCurrencySymbol()" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-2
@@ -3,6 +3,7 @@
|
||||
import { useAutofocus, useService } from "@web/core/utils/hooks";
|
||||
import { orderManagement } from "@point_of_sale/js/PosContext";
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
// NOTE: These are constants so that they are only instantiated once
|
||||
// and they can be used efficiently by the OrderManagementControlPanel.
|
||||
@@ -23,12 +24,12 @@ export class SaleOrderManagementControlPanel extends Component {
|
||||
static template = "SaleOrderManagementControlPanel";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.saleOrderFetcher = useService("sale_order_fetcher");
|
||||
this.orderManagementContext = useState(orderManagement);
|
||||
useAutofocus();
|
||||
|
||||
const currentPartner = this.env.pos.get_order().get_partner();
|
||||
const currentPartner = this.pos.globalState.get_order().get_partner();
|
||||
if (currentPartner) {
|
||||
this.orderManagementContext.searchString = currentPartner.name;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
|
||||
import { SaleOrderList } from "./SaleOrderList";
|
||||
import { SaleOrderManagementControlPanel } from "./SaleOrderManagementControlPanel";
|
||||
import { onMounted, useRef, useState } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
/**
|
||||
* ID getter to take into account falsy many2one value.
|
||||
@@ -33,6 +34,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.pos = usePos();
|
||||
this.popup = useService("popup");
|
||||
this.orm = useService("orm");
|
||||
this.root = useRef("root");
|
||||
@@ -59,10 +61,10 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
this.saleOrderFetcher.setNPerPage(val);
|
||||
this.saleOrderFetcher.fetch();
|
||||
}
|
||||
_getSaleOrderOrigin(order) {
|
||||
_getSaleOrderOrigin(order) {
|
||||
for (const line of order.get_orderlines()) {
|
||||
if (line.sale_order_origin_id) {
|
||||
return line.sale_order_origin_id
|
||||
return line.sale_order_origin_id;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -91,6 +93,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
this.saleOrderFetcher.fetch();
|
||||
}
|
||||
async onClickSaleOrder(clickedOrder) {
|
||||
const { globalState } = this.pos;
|
||||
const { confirmed, payload: selectedOption } = await this.popup.add(SelectionPopup, {
|
||||
title: this.env._t("What do you want to do?"),
|
||||
list: [
|
||||
@@ -109,7 +112,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
let currentPOSOrder = this.env.pos.get_order();
|
||||
let currentPOSOrder = globalState.get_order();
|
||||
const sale_order = await this._getSaleOrder(clickedOrder.id);
|
||||
clickedOrder.shipping_date = sale_order.shipping_date;
|
||||
|
||||
@@ -123,22 +126,22 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
getId(linkedSO.partner_invoice_id) !== getId(sale_order.partner_invoice_id) ||
|
||||
getId(linkedSO.partner_shipping_id) !== getId(sale_order.partner_shipping_id)
|
||||
) {
|
||||
currentPOSOrder = this.env.pos.add_new_order();
|
||||
currentPOSOrder = globalState.add_new_order();
|
||||
this.notification.add(this.env._t("A new order has been created."), 4000);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.env.pos.load_new_partners();
|
||||
await globalState.load_new_partners();
|
||||
} catch {
|
||||
// FIXME Universal catch seems ill advised
|
||||
}
|
||||
const order_partner = this.env.pos.db.get_partner_by_id(sale_order.partner_id[0]);
|
||||
const order_partner = globalState.db.get_partner_by_id(sale_order.partner_id[0]);
|
||||
if (order_partner) {
|
||||
currentPOSOrder.set_partner(order_partner);
|
||||
} else {
|
||||
try {
|
||||
await this.env.pos._loadPartners([sale_order.partner_id[0]]);
|
||||
await globalState._loadPartners([sale_order.partner_id[0]]);
|
||||
} catch {
|
||||
const title = this.env._t("Customer loading error");
|
||||
const body = sprintf(
|
||||
@@ -148,11 +151,11 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
await this.popup.add(ErrorPopup, { title, body });
|
||||
}
|
||||
currentPOSOrder.set_partner(
|
||||
this.env.pos.db.get_partner_by_id(sale_order.partner_id[0])
|
||||
globalState.db.get_partner_by_id(sale_order.partner_id[0])
|
||||
);
|
||||
}
|
||||
const orderFiscalPos = sale_order.fiscal_position_id
|
||||
? this.env.pos.fiscal_positions.find(
|
||||
? globalState.fiscal_positions.find(
|
||||
(position) => position.id === sale_order.fiscal_position_id[0]
|
||||
)
|
||||
: false;
|
||||
@@ -160,7 +163,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
currentPOSOrder.fiscal_position = orderFiscalPos;
|
||||
}
|
||||
const orderPricelist = sale_order.pricelist_id
|
||||
? this.env.pos.pricelists.find(
|
||||
? globalState.pricelists.find(
|
||||
(pricelist) => pricelist.id === sale_order.pricelist_id[0]
|
||||
)
|
||||
: false;
|
||||
@@ -172,7 +175,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
// settle the order
|
||||
const lines = sale_order.order_line;
|
||||
const product_to_add_in_pos = lines
|
||||
.filter((line) => !this.env.pos.db.get_product_by_id(line.product_id[0]))
|
||||
.filter((line) => !globalState.db.get_product_by_id(line.product_id[0]))
|
||||
.map((line) => line.product_id[0]);
|
||||
if (product_to_add_in_pos.length) {
|
||||
const { confirmed } = await this.popup.add(ConfirmPopup, {
|
||||
@@ -184,7 +187,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
cancelText: this.env._t("No"),
|
||||
});
|
||||
if (confirmed) {
|
||||
await this.env.pos._addProducts(product_to_add_in_pos);
|
||||
await globalState._addProducts(product_to_add_in_pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,16 +205,16 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (!this.env.pos.db.get_product_by_id(line.product_id[0])) {
|
||||
if (!globalState.db.get_product_by_id(line.product_id[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const new_line = new Orderline(
|
||||
{},
|
||||
{
|
||||
pos: this.env.pos,
|
||||
order: this.env.pos.get_order(),
|
||||
product: this.env.pos.db.get_product_by_id(line.product_id[0]),
|
||||
pos: globalState,
|
||||
order: globalState.get_order(),
|
||||
product: globalState.db.get_product_by_id(line.product_id[0]),
|
||||
description: line.name,
|
||||
price: line.price_unit,
|
||||
tax_ids: orderFiscalPos ? undefined : line.tax_id,
|
||||
@@ -224,8 +227,8 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
|
||||
if (
|
||||
new_line.get_product().tracking !== "none" &&
|
||||
(this.env.pos.picking_type.use_create_lots ||
|
||||
this.env.pos.picking_type.use_existing_lots) &&
|
||||
(globalState.picking_type.use_create_lots ||
|
||||
globalState.picking_type.use_existing_lots) &&
|
||||
line.lot_names.length > 0
|
||||
) {
|
||||
// Ask once when `useLoadedLots` is undefined, then reuse it's value on the succeeding lines.
|
||||
@@ -253,39 +256,33 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
new_line.setQuantityFromSOL(line);
|
||||
new_line.set_unit_price(line.price_unit);
|
||||
new_line.set_discount(line.discount);
|
||||
this.env.pos.get_order().add_orderline(new_line);
|
||||
globalState.get_order().add_orderline(new_line);
|
||||
}
|
||||
} else {
|
||||
// apply a downpayment
|
||||
if (this.env.pos.config.down_payment_product_id) {
|
||||
let lines = sale_order.order_line;
|
||||
const tab = [];
|
||||
lines = lines.filter((line) => {
|
||||
return (
|
||||
line.product_id[0] !== this.env.pos.config.down_payment_product_id[0]
|
||||
);
|
||||
if (globalState.config.down_payment_product_id) {
|
||||
const lines = sale_order.order_line.filter((line) => {
|
||||
return line.product_id[0] !== globalState.config.down_payment_product_id[0];
|
||||
});
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
tab[i] = {
|
||||
product_name: lines[i].product_id[1],
|
||||
product_uom_qty: lines[i].product_uom_qty,
|
||||
price_unit: lines[i].price_unit,
|
||||
total: lines[i].price_total,
|
||||
};
|
||||
}
|
||||
let down_payment_product = this.env.pos.db.get_product_by_id(
|
||||
this.env.pos.config.down_payment_product_id[0]
|
||||
const tab = lines.map((line) => ({
|
||||
product_name: line.product_id[1],
|
||||
product_uom_qty: line.product_uom_qty,
|
||||
price_unit: line.price_unit,
|
||||
total: line.price_total,
|
||||
}));
|
||||
let down_payment_product = globalState.db.get_product_by_id(
|
||||
globalState.config.down_payment_product_id[0]
|
||||
);
|
||||
if (!down_payment_product) {
|
||||
await this.env.pos._addProducts([
|
||||
this.env.pos.config.down_payment_product_id[0],
|
||||
await globalState._addProducts([
|
||||
globalState.config.down_payment_product_id[0],
|
||||
]);
|
||||
down_payment_product = this.env.pos.db.get_product_by_id(
|
||||
this.env.pos.config.down_payment_product_id[0]
|
||||
down_payment_product = globalState.db.get_product_by_id(
|
||||
globalState.config.down_payment_product_id[0]
|
||||
);
|
||||
}
|
||||
const down_payment_tax =
|
||||
this.env.pos.taxes_by_id[down_payment_product.taxes_id] || false;
|
||||
globalState.taxes_by_id[down_payment_product.taxes_id] || false;
|
||||
let down_payment;
|
||||
if (down_payment_tax) {
|
||||
down_payment = down_payment_tax.price_include
|
||||
@@ -302,7 +299,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
const popupSubtitle = this.env._t("Due balance: %s");
|
||||
if (selectedOption == "dpAmount") {
|
||||
popupTitle = this.env._t("Down Payment");
|
||||
popupInputSuffix = this.env.pos.currency.symbol;
|
||||
popupInputSuffix = globalState.currency.symbol;
|
||||
} else {
|
||||
popupTitle = this.env._t("Down Payment");
|
||||
popupInputSuffix = "%";
|
||||
@@ -361,8 +358,8 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
const new_line = new Orderline(
|
||||
{},
|
||||
{
|
||||
pos: this.env.pos,
|
||||
order: this.env.pos.get_order(),
|
||||
pos: globalState,
|
||||
order: globalState.get_order(),
|
||||
product: down_payment_product,
|
||||
price: down_payment,
|
||||
price_automatically_set: true,
|
||||
@@ -371,7 +368,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
}
|
||||
);
|
||||
new_line.set_unit_price(down_payment);
|
||||
this.env.pos.get_order().add_orderline(new_line);
|
||||
globalState.get_order().add_orderline(new_line);
|
||||
} else {
|
||||
const title = this.env._t("No down payment product");
|
||||
const body = this.env._t(
|
||||
@@ -400,7 +397,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
|
||||
"amount_unpaid",
|
||||
"picking_ids",
|
||||
"partner_shipping_id",
|
||||
"partner_invoice_id"
|
||||
"partner_invoice_id",
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="pos_six.Navbar" t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('end-nav-button')]" position="before">
|
||||
<BalanceButton t-if="this.env.pos.payment_methods.some((pm) => pm.use_payment_terminal === 'six')" />
|
||||
<BalanceButton t-if="pos.globalState.payment_methods.some((pm) => pm.use_payment_terminal === 'six')" />
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
|
||||
export class BalanceButton extends Component {
|
||||
static template = "BalanceButton";
|
||||
|
||||
setup() {
|
||||
this.pos = usePos();
|
||||
}
|
||||
sendBalance() {
|
||||
this.env.pos.payment_methods.map((pm) => {
|
||||
this.pos.globalState.payment_methods.forEach((pm) => {
|
||||
if (pm.use_payment_terminal === "six") {
|
||||
pm.payment_terminal.send_balance();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user