summaryrefslogtreecommitdiff
path: root/addons/point_of_sale/static/src/js/ChromeWidgets
diff options
context:
space:
mode:
Diffstat (limited to 'addons/point_of_sale/static/src/js/ChromeWidgets')
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/CashierName.js23
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js87
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js161
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js36
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js36
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js91
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js38
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/SyncNotification.js37
-rw-r--r--addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js41
9 files changed, 550 insertions, 0 deletions
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/CashierName.js b/addons/point_of_sale/static/src/js/ChromeWidgets/CashierName.js
new file mode 100644
index 00000000..02e61967
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/CashierName.js
@@ -0,0 +1,23 @@
+odoo.define('point_of_sale.CashierName', function(require) {
+ 'use strict';
+
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ // Previously UsernameWidget
+ class CashierName extends PosComponent {
+ get username() {
+ const cashier = this.env.pos.get_cashier();
+ if (cashier) {
+ return cashier.name;
+ } else {
+ return '';
+ }
+ }
+ }
+ CashierName.template = 'CashierName';
+
+ Registries.Component.add(CashierName);
+
+ return CashierName;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js
new file mode 100644
index 00000000..38403b58
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js
@@ -0,0 +1,87 @@
+odoo.define('point_of_sale.ClientScreenButton', function(require) {
+ 'use strict';
+
+ const { useState } = owl;
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ // Formerly ClientScreenWidget
+ class ClientScreenButton extends PosComponent {
+ constructor() {
+ super(...arguments);
+ this.state = useState({ status: 'failure' });
+ this._start();
+ }
+ get message() {
+ return {
+ success: '',
+ warning: this.env._t('Connected, Not Owned'),
+ failure: this.env._t('Disconnected'),
+ not_found: this.env._t('Client Screen Unsupported. Please upgrade the IoT Box'),
+ }[this.state.status];
+ }
+ async onClick() {
+ try {
+ const renderedHtml = await this.env.pos.render_html_for_customer_facing_display();
+ const ownership = await this.env.pos.proxy.take_ownership_over_client_screen(
+ renderedHtml
+ );
+ if (typeof ownership === 'string') {
+ ownership = JSON.parse(ownership);
+ }
+ if (ownership.status === 'success') {
+ this.state.status = 'success';
+ } else {
+ this.state.status = 'warning';
+ }
+ if (!this.env.pos.proxy.posbox_supports_display) {
+ this.env.pos.proxy.posbox_supports_display = true;
+ this._start();
+ }
+ } catch (error) {
+ if (typeof error == 'undefined') {
+ this.state.status = 'failure';
+ } else {
+ this.state.status = 'not_found';
+ }
+ }
+ }
+ _start() {
+ const self = this;
+ async function loop() {
+ if (self.env.pos.proxy.posbox_supports_display) {
+ try {
+ const ownership = await self.env.pos.proxy.test_ownership_of_client_screen();
+ if (typeof ownership === 'string') {
+ ownership = JSON.parse(ownership);
+ }
+ if (ownership.status === 'OWNER') {
+ self.state.status = 'success';
+ } else {
+ self.state.status = 'warning';
+ }
+ setTimeout(loop, 3000);
+ } catch (error) {
+ if (error.abort) {
+ // Stop the loop
+ return;
+ }
+ if (typeof error == 'undefined') {
+ self.state.status = 'failure';
+ } else {
+ self.state.status = 'not_found';
+ self.env.pos.proxy.posbox_supports_display = false;
+ }
+ setTimeout(loop, 3000);
+ }
+ }
+ }
+ loop();
+ }
+ }
+ ClientScreenButton.template = 'ClientScreenButton';
+
+ Registries.Component.add(ClientScreenButton);
+
+ return ClientScreenButton;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js b/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js
new file mode 100644
index 00000000..f5158428
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js
@@ -0,0 +1,161 @@
+odoo.define('point_of_sale.DebugWidget', function (require) {
+ 'use strict';
+
+ const { useState } = owl;
+ const { useRef } = owl.hooks;
+ const { getFileAsText } = require('point_of_sale.utils');
+ const { parse } = require('web.field_utils');
+ const NumberBuffer = require('point_of_sale.NumberBuffer');
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ class DebugWidget extends PosComponent {
+ constructor() {
+ super(...arguments);
+ this.state = useState({
+ barcodeInput: '',
+ weightInput: '',
+ isPaidOrdersReady: false,
+ isUnpaidOrdersReady: false,
+ buffer: NumberBuffer.get(),
+ });
+
+ // NOTE: Perhaps this can still be improved.
+ // What we do here is loop thru the `event` elements
+ // then we assign animation that happens when the event is triggered
+ // in the proxy. E.g. if open_cashbox is sent, the open_cashbox element
+ // changes color from '#6CD11D' to '#1E1E1E' for a duration of 2sec.
+ this.eventElementsRef = {};
+ this.animations = {};
+ for (let eventName of ['open_cashbox', 'print_receipt', 'scale_read']) {
+ this.eventElementsRef[eventName] = useRef(eventName);
+ this.env.pos.proxy.add_notification(
+ eventName,
+ (() => {
+ if (this.animations[eventName]) {
+ this.animations[eventName].cancel();
+ }
+ const eventElement = this.eventElementsRef[eventName].el;
+ eventElement.style.backgroundColor = '#6CD11D';
+ this.animations[eventName] = eventElement.animate(
+ { backgroundColor: ['#6CD11D', '#1E1E1E'] },
+ 2000
+ );
+ }).bind(this)
+ );
+ }
+ }
+ mounted() {
+ NumberBuffer.on('buffer-update', this, this._onBufferUpdate);
+ }
+ willUnmount() {
+ NumberBuffer.off('buffer-update', this, this._onBufferUpdate);
+ }
+ toggleWidget() {
+ this.state.isShown = !this.state.isShown;
+ }
+ setWeight() {
+ var weightInKg = parse.float(this.state.weightInput);
+ if (!isNaN(weightInKg)) {
+ this.env.pos.proxy.debug_set_weight(weightInKg);
+ }
+ }
+ resetWeight() {
+ this.state.weightInput = '';
+ this.env.pos.proxy.debug_reset_weight();
+ }
+ barcodeScan() {
+ this.env.pos.barcode_reader.scan(this.state.barcodeInput);
+ }
+ barcodeScanEAN() {
+ const ean = this.env.pos.barcode_reader.barcode_parser.sanitize_ean(
+ this.state.barcodeInput || '0'
+ );
+ this.state.barcodeInput = ean;
+ this.env.pos.barcode_reader.scan(ean);
+ }
+ async deleteOrders() {
+ const { confirmed } = await this.showPopup('ConfirmPopup', {
+ title: this.env._t('Delete Paid Orders ?'),
+ body: this.env._t(
+ 'This operation will permanently destroy all paid orders from the local storage. You will lose all the data. This operation cannot be undone.'
+ ),
+ });
+ if (confirmed) {
+ this.env.pos.db.remove_all_orders();
+ this.env.pos.set_synch('connected', 0);
+ }
+ }
+ async deleteUnpaidOrders() {
+ const { confirmed } = await this.showPopup('ConfirmPopup', {
+ title: this.env._t('Delete Unpaid Orders ?'),
+ body: this.env._t(
+ 'This operation will destroy all unpaid orders in the browser. You will lose all the unsaved data and exit the point of sale. This operation cannot be undone.'
+ ),
+ });
+ if (confirmed) {
+ this.env.pos.db.remove_all_unpaid_orders();
+ window.location = '/';
+ }
+ }
+ _createBlob(contents) {
+ if (typeof contents !== 'string') {
+ contents = JSON.stringify(contents, null, 2);
+ }
+ return new Blob([contents]);
+ }
+ // IMPROVEMENT: Duplicated codes for downloading paid and unpaid orders.
+ // The implementation can be better.
+ preparePaidOrders() {
+ try {
+ this.paidOrdersBlob = this._createBlob(this.env.pos.export_paid_orders());
+ this.state.isPaidOrdersReady = true;
+ } catch (error) {
+ console.warn(error);
+ }
+ }
+ get paidOrdersFilename() {
+ return `${this.env._t('paid orders')} ${moment().format('YYYY-MM-DD-HH-mm-ss')}.json`;
+ }
+ get paidOrdersURL() {
+ var URL = window.URL || window.webkitURL;
+ return URL.createObjectURL(this.paidOrdersBlob);
+ }
+ prepareUnpaidOrders() {
+ try {
+ this.unpaidOrdersBlob = this._createBlob(this.env.pos.export_unpaid_orders());
+ this.state.isUnpaidOrdersReady = true;
+ } catch (error) {
+ console.warn(error);
+ }
+ }
+ get unpaidOrdersFilename() {
+ return `${this.env._t('unpaid orders')} ${moment().format('YYYY-MM-DD-HH-mm-ss')}.json`;
+ }
+ get unpaidOrdersURL() {
+ var URL = window.URL || window.webkitURL;
+ return URL.createObjectURL(this.unpaidOrdersBlob);
+ }
+ async importOrders(event) {
+ const file = event.target.files[0];
+ if (file) {
+ const report = this.env.pos.import_orders(await getFileAsText(file));
+ await this.showPopup('OrderImportPopup', { report });
+ }
+ }
+ refreshDisplay() {
+ this.env.pos.proxy.message('display_refresh', {});
+ }
+ _onBufferUpdate(buffer) {
+ this.state.buffer = buffer;
+ }
+ get bufferRepr() {
+ return `"${this.state.buffer}"`;
+ }
+ }
+ DebugWidget.template = 'DebugWidget';
+
+ Registries.Component.add(DebugWidget);
+
+ return DebugWidget;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js
new file mode 100644
index 00000000..84036ecb
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js
@@ -0,0 +1,36 @@
+odoo.define('point_of_sale.HeaderButton', function(require) {
+ 'use strict';
+
+ const { useState } = owl;
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ // Previously HeaderButtonWidget
+ // This is the close session button
+ class HeaderButton extends PosComponent {
+ constructor() {
+ super(...arguments);
+ this.state = useState({ label: 'Close' });
+ this.confirmed = null;
+ }
+ get translatedLabel() {
+ return this.env._t(this.state.label);
+ }
+ onClick() {
+ if (!this.confirmed) {
+ this.state.label = 'Confirm';
+ this.confirmed = setTimeout(() => {
+ this.state.label = 'Close';
+ this.confirmed = null;
+ }, 2000);
+ } else {
+ this.trigger('close-pos');
+ }
+ }
+ }
+ HeaderButton.template = 'HeaderButton';
+
+ Registries.Component.add(HeaderButton);
+
+ return HeaderButton;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js
new file mode 100644
index 00000000..0bee8880
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js
@@ -0,0 +1,36 @@
+odoo.define('point_of_sale.OrderManagementButton', function (require) {
+ 'use strict';
+
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+ const { isRpcError } = require('point_of_sale.utils');
+
+ class OrderManagementButton extends PosComponent {
+ async onClick() {
+ try {
+ // ping the server, if no error, show the screen
+ await this.rpc({
+ model: 'pos.order',
+ method: 'browse',
+ args: [[]],
+ kwargs: { context: this.env.session.user_context },
+ });
+ this.showScreen('OrderManagementScreen');
+ } catch (error) {
+ if (isRpcError(error) && error.message.code < 0) {
+ this.showPopup('ErrorPopup', {
+ title: this.env._t('Network Error'),
+ body: this.env._t('Cannot access order management screen if offline.'),
+ });
+ } else {
+ throw error;
+ }
+ }
+ }
+ }
+ OrderManagementButton.template = 'OrderManagementButton';
+
+ Registries.Component.add(OrderManagementButton);
+
+ return OrderManagementButton;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js b/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js
new file mode 100644
index 00000000..98c24c02
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js
@@ -0,0 +1,91 @@
+odoo.define('point_of_sale.ProxyStatus', function(require) {
+ 'use strict';
+
+ const { useState } = owl;
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ // Previously ProxyStatusWidget
+ class ProxyStatus extends PosComponent {
+ constructor() {
+ super(...arguments);
+ const initialProxyStatus = this.env.pos.proxy.get('status');
+ this.state = useState({
+ status: initialProxyStatus.status,
+ msg: initialProxyStatus.msg,
+ });
+ this.statuses = ['connected', 'connecting', 'disconnected', 'warning'];
+ this.index = 0;
+ }
+ mounted() {
+ this.env.pos.proxy.on('change:status', this, this._onChangeStatus);
+ }
+ willUnmount() {
+ this.env.pos.proxy.off('change:status', this, this._onChangeStatus);
+ }
+ async onClick() {
+ try {
+ await this.env.pos.connect_to_proxy();
+ } catch (error) {
+ if (error instanceof Error) {
+ throw error;
+ } else {
+ this.showPopup('ErrorPopup', error);
+ }
+ }
+ }
+ _onChangeStatus(posProxy, statusChange) {
+ this._setStatus(statusChange.newValue);
+ }
+ _setStatus(newStatus) {
+ if (newStatus.status === 'connected') {
+ var warning = false;
+ var msg = '';
+ if (this.env.pos.config.iface_scan_via_proxy) {
+ var scannerStatus = newStatus.drivers.scanner
+ ? newStatus.drivers.scanner.status
+ : false;
+ if (scannerStatus != 'connected' && scannerStatus != 'connecting') {
+ warning = true;
+ msg += this.env._t('Scanner');
+ }
+ }
+ if (
+ this.env.pos.config.iface_print_via_proxy ||
+ this.env.pos.config.iface_cashdrawer
+ ) {
+ var printerStatus = newStatus.drivers.printer
+ ? newStatus.drivers.printer.status
+ : false;
+ if (printerStatus != 'connected' && printerStatus != 'connecting') {
+ warning = true;
+ msg = msg ? msg + ' & ' : msg;
+ msg += this.env._t('Printer');
+ }
+ }
+ if (this.env.pos.config.iface_electronic_scale) {
+ var scaleStatus = newStatus.drivers.scale
+ ? newStatus.drivers.scale.status
+ : false;
+ if (scaleStatus != 'connected' && scaleStatus != 'connecting') {
+ warning = true;
+ msg = msg ? msg + ' & ' : msg;
+ msg += this.env._t('Scale');
+ }
+ }
+ msg = msg ? msg + ' ' + this.env._t('Offline') : msg;
+
+ this.state.status = warning ? 'warning' : 'connected';
+ this.state.msg = msg;
+ } else {
+ this.state.status = newStatus.status;
+ this.state.msg = newStatus.msg || '';
+ }
+ }
+ }
+ ProxyStatus.template = 'ProxyStatus';
+
+ Registries.Component.add(ProxyStatus);
+
+ return ProxyStatus;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js
new file mode 100644
index 00000000..e646547e
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js
@@ -0,0 +1,38 @@
+odoo.define('point_of_sale.SaleDetailsButton', function(require) {
+ 'use strict';
+
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ class SaleDetailsButton extends PosComponent {
+ async onClick() {
+ // 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.rpc({
+ model: 'report.point_of_sale.report_saledetails',
+ method: 'get_sale_details',
+ args: [false, false, false, [this.env.pos.pos_session.id]],
+ });
+ const report = this.env.qweb.renderToString(
+ 'SaleDetailsReport',
+ Object.assign({}, saleDetails, {
+ date: new Date().toLocaleString(),
+ pos: this.env.pos,
+ })
+ );
+ const printResult = await this.env.pos.proxy.printer.print_receipt(report);
+ if (!printResult.successful) {
+ await this.showPopup('ErrorPopup', {
+ title: printResult.message.title,
+ body: printResult.message.body,
+ });
+ }
+ }
+ }
+ SaleDetailsButton.template = 'SaleDetailsButton';
+
+ Registries.Component.add(SaleDetailsButton);
+
+ return SaleDetailsButton;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/SyncNotification.js b/addons/point_of_sale/static/src/js/ChromeWidgets/SyncNotification.js
new file mode 100644
index 00000000..5a4e158d
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/SyncNotification.js
@@ -0,0 +1,37 @@
+odoo.define('point_of_sale.SyncNotification', function(require) {
+ 'use strict';
+
+ const { useState } = owl;
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ // Previously SynchNotificationWidget
+ class SyncNotification extends PosComponent {
+ constructor() {
+ super(...arguments);
+ const synch = this.env.pos.get('synch');
+ this.state = useState({ status: synch.status, msg: synch.pending });
+ }
+ mounted() {
+ this.env.pos.on(
+ 'change:synch',
+ (pos, synch) => {
+ this.state.status = synch.status;
+ this.state.msg = synch.pending;
+ },
+ this
+ );
+ }
+ willUnmount() {
+ this.env.pos.on('change:synch', null, this);
+ }
+ onClick() {
+ this.env.pos.push_orders(null, { show_error: true });
+ }
+ }
+ SyncNotification.template = 'SyncNotification';
+
+ Registries.Component.add(SyncNotification);
+
+ return SyncNotification;
+});
diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js
new file mode 100644
index 00000000..d142bbde
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js
@@ -0,0 +1,41 @@
+odoo.define('point_of_sale.TicketButton', function (require) {
+ 'use strict';
+
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+ const { posbus } = require('point_of_sale.utils');
+
+ class TicketButton extends PosComponent {
+ onClick() {
+ if (this.props.isTicketScreenShown) {
+ posbus.trigger('ticket-button-clicked');
+ } else {
+ this.showScreen('TicketScreen');
+ }
+ }
+ willPatch() {
+ posbus.off('order-deleted', this);
+ }
+ patched() {
+ posbus.on('order-deleted', this, this.render);
+ }
+ mounted() {
+ posbus.on('order-deleted', this, this.render);
+ }
+ willUnmount() {
+ posbus.off('order-deleted', this);
+ }
+ get count() {
+ if (this.env.pos) {
+ return this.env.pos.get_order_list().length;
+ } else {
+ return 0;
+ }
+ }
+ }
+ TicketButton.template = 'TicketButton';
+
+ Registries.Component.add(TicketButton);
+
+ return TicketButton;
+});