Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion addons/l10n_in_pos/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
'point_of_sale._assets_pos': [
'l10n_in/static/src/helpers/hsn_summary.js',
'l10n_in_pos/static/src/**/*',
'l10n_in_pos/static/src/customer_display/customer_display_adapter.js',
],
},
'author': 'Odoo S.A.',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { patch } from "@web/core/utils/patch";
import { CustomerDisplayPosAdapter } from "@point_of_sale/customer_display/customer_display_adapter";
import { CustomerDisplayPosAdapter } from "@point_of_sale/app/customer_display/customer_display_adapter";

patch(CustomerDisplayPosAdapter.prototype, {
formatOrderData(order) {
Expand Down
2 changes: 0 additions & 2 deletions addons/point_of_sale/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@
'web/static/src/webclient/actions/**/*',
('remove', 'web/static/src/webclient/actions/reports/layout_assets/**/*'),
('remove', 'web/static/src/webclient/actions/**/*css'),
'point_of_sale/static/src/customer_display/customer_display_adapter.js',
'partner_autocomplete/static/src/**/*',
],
'point_of_sale.base_tests': [
Expand All @@ -225,7 +224,6 @@
"point_of_sale/static/src/app/components/centered_icon/*",
"point_of_sale/static/src/utils.js",
"point_of_sale/static/src/customer_display/**/*",
('remove', 'point_of_sale/static/src/customer_display/customer_display_adapter.js'),
],
'point_of_sale.customer_display_assets_test': [
('include', 'point_of_sale.base_tests'),
Expand Down
9 changes: 3 additions & 6 deletions addons/point_of_sale/controllers/customer_display.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import odoo
from odoo import http
from odoo.http import request
import odoo.exceptions


class PosCustomerDisplay(http.Controller):
@http.route("/pos_customer_display/<id_>/<access_token>", auth="public", type="http", website=True)
def pos_customer_display(self, id_, access_token, **kw):
@http.route("/pos_customer_display/<id_>/<device_uuid>", auth="public", type="http", website=True)
def pos_customer_display(self, id_, device_uuid, **kw):
pos_config_sudo = request.env["pos.config"].sudo().browse(int(id_))
if not odoo.tools.consteq(access_token, pos_config_sudo.access_token) or pos_config_sudo.customer_display_type == "none":
raise odoo.exceptions.AccessDenied()
return request.render(
"point_of_sale.customer_display_index",
{
Expand All @@ -19,6 +15,7 @@ def pos_customer_display(self, id_, access_token, **kw):
},
**request.env["ir.http"].get_frontend_session_info(),
**pos_config_sudo._get_customer_display_data(),
'device_uuid': device_uuid,
},
},
)
19 changes: 3 additions & 16 deletions addons/point_of_sale/models/pos_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ def _get_default_tip_product(self):
tip_product_id = self.env['product.product'].search([('default_code', '=', 'TIPS')], limit=1)
return tip_product_id

def _get_customer_display_types(self):
return [('none', 'None'), ('local', 'The same device'), ('remote', 'Another device'), ('proxy', 'An IOT-connected screen')]

name = fields.Char(string='Point of Sale', required=True, help="An internal identification of the point of sale.")
printer_ids = fields.Many2many('pos.printer', 'pos_config_printer_rel', 'config_id', 'printer_id', string='Order Printers')
is_order_printer = fields.Boolean('Order Printer')
Expand Down Expand Up @@ -109,7 +106,6 @@ def _get_customer_display_types(self):
iface_tax_included = fields.Selection([('subtotal', 'Tax-Excluded Price'), ('total', 'Tax-Included Price')], string="Tax Display", default='total', required=True)
iface_available_categ_ids = fields.Many2many('pos.category', string='Available PoS Product Categories',
help='The point of sale will only display products which are within one of the selected category trees. If no category is specified, all available products will be shown')
customer_display_type = fields.Selection(selection=lambda self: self._get_customer_display_types(), string='Customer Facing Display', help="Show checkout to customers.", default='local')
customer_display_bg_img = fields.Image(string='Background Image', max_width=1920, max_height=1920)
customer_display_bg_img_name = fields.Char(string='Background Image Name')
restrict_price_control = fields.Boolean(string='Restrict Price Modifications to Managers',
Expand Down Expand Up @@ -397,12 +393,6 @@ def _check_header_footer(self, values):
if not self.env.is_admin() and {'is_header_or_footer', 'receipt_header', 'receipt_footer'} & values.keys():
raise AccessError(_('Only administrators can edit receipt headers and footers'))

@api.constrains('customer_display_type', 'proxy_ip', 'is_posbox')
def _check_customer_display_type(self):
for config in self:
if config.customer_display_type == 'proxy' and (not config.is_posbox or not config.proxy_ip):
raise UserError(_("You must set the iot box's IP address to use an IoT-connected screen. You'll find the field under the 'IoT Box' option."))

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
Expand Down Expand Up @@ -772,11 +762,9 @@ def _get_payment_method(self, payment_type):
def _get_special_products(self):
return self.env.ref('point_of_sale.product_product_tip', raise_if_not_found=False) or self.env['product.product']

def update_customer_display(self, order, access_token):
def update_customer_display(self, order, device_uuid):
self.ensure_one()
if not access_token or not secrets.compare_digest(self.access_token, access_token):
return
self._notify("UPDATE_CUSTOMER_DISPLAY", order)
self._notify(f"UPDATE_CUSTOMER_DISPLAY-{device_uuid}", order)

def _get_display_device_ip(self):
self.ensure_one()
Expand All @@ -787,10 +775,9 @@ def _get_customer_display_data(self):
return {
'config_id': self.id,
'access_token': self.access_token,
'type': self.customer_display_type,
'has_bg_img': bool(self.customer_display_bg_img),
'company_id': self.company_id.id,
**({'proxy_ip': self._get_display_device_ip()} if self.customer_display_type != 'none' else {}),
'proxy_ip': self._get_display_device_ip(),
}

@api.model
Expand Down
1 change: 0 additions & 1 deletion addons/point_of_sale/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def _default_pos_config(self):
account_default_pos_receivable_account_id = fields.Many2one(string='Default Account Receivable (PoS)', related='company_id.account_default_pos_receivable_account_id', readonly=False, check_company=True)
barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', related='company_id.nomenclature_id', readonly=False)
is_kiosk_mode = fields.Boolean(string="Is Kiosk Mode", default=False)
pos_customer_display_type = fields.Selection(related="pos_config_id.customer_display_type", readonly=False)
pos_customer_display_bg_img = fields.Image(related='pos_config_id.customer_display_bg_img', readonly=False)
pos_customer_display_bg_img_name = fields.Char(related='pos_config_id.customer_display_bg_img_name', readonly=False)

Expand Down
51 changes: 29 additions & 22 deletions addons/point_of_sale/static/src/app/components/navbar/navbar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { usePos } from "@point_of_sale/app/hooks/pos_hook";
import { useService } from "@web/core/utils/hooks";
import { isDisplayStandalone, isMobileOS } from "@web/core/browser/feature_detection";
import { isDisplayStandalone } from "@web/core/browser/feature_detection";

import { CashierName } from "@point_of_sale/app/components/navbar/cashier_name/cashier_name";
import { ProxyStatus } from "@point_of_sale/app/components/navbar/proxy_status/proxy_status";
Expand All @@ -20,8 +20,9 @@ import { OrderTabs } from "@point_of_sale/app/components/order_tabs/order_tabs";
import { PresetSlotsPopup } from "@point_of_sale/app/components/popups/preset_slots_popup/preset_slots_popup";
import { makeAwaitable } from "@point_of_sale/app/utils/make_awaitable_dialog";
import { _t } from "@web/core/l10n/translation";
import { openCustomerDisplay } from "@point_of_sale/customer_display/utils";

import { openProxyCustomerDisplay } from "@point_of_sale/customer_display/utils";
import { uuidv4 } from "@point_of_sale/utils";
import { QrCodeCustomerDisplay } from "@point_of_sale/app/customer_display/customer_display_qr_code_popup";
const { DateTime } = luxon;

export class Navbar extends Component {
Expand Down Expand Up @@ -109,9 +110,6 @@ export class Navbar extends Component {
this.pos.mobile_pane = "right";
this.pos.scanning = !this.pos.scanning;
}
get customerFacingDisplayButtonIsShown() {
return this.pos.config.customer_display_type !== "none" && !isMobileOS();
}
get showCashMoveButton() {
return this.pos.showCashMoveButton;
}
Expand All @@ -133,25 +131,34 @@ export class Navbar extends Component {
}

openCustomerDisplay() {
if (this.pos.config.customer_display_type === "local") {
window.open(
`/pos_customer_display/${this.pos.config.id}/${this.pos.config.access_token}`,
"newWindow",
"width=800,height=600,left=200,top=200"
const proxyIP = this.pos.getDisplayDeviceIP();
if (proxyIP) {
openProxyCustomerDisplay(
proxyIP,
this.pos.config.access_token,
this.pos.config.id,
this.notification
);
} else {
const getDeviceUuid = () => {
if (!localStorage.getItem("device_uuid")) {
localStorage.setItem("device_uuid", uuidv4());
}
return localStorage.getItem("device_uuid");
};
const customer_display_url = `/pos_customer_display/${
this.pos.config.id
}/${getDeviceUuid()}`;

if (this.ui.isSmall) {
this.dialog.add(QrCodeCustomerDisplay, {
customerDisplayURL: `${this.pos.session._base_url}${customer_display_url}`,
});
return;
}
window.open(customer_display_url, "newWindow", "width=800,height=600,left=200,top=200");
this.notification.add(_t("PoS Customer Display opened in a new window"));
}
if (this.pos.config.customer_display_type === "remote") {
this.notification.add(
_t("Navigate to your PoS Customer Display on the other computer")
);
}
openCustomerDisplay(
this.pos.getDisplayDeviceIP(),
this.pos.config.access_token,
this.pos.config.id,
this.notification
);
}

get showCreateProductButton() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
<i class="fa fa-fw fa-bars"/>
</button>
<t t-set-slot="content">
<div t-if="customerFacingDisplayButtonIsShown" class="d-flex gap-2 p-2 border-bottom">
<ProxyStatus t-if="pos.config.use_proxy" />
<span t-on-click="openCustomerDisplay" class="btn btn-secondary btn-lg w-100 text-center" title="Customer Display">
<div class="d-flex gap-2 p-2 border-bottom">
<ProxyStatus t-if="pos.config.useProxy" />
<span t-on-click="openCustomerDisplay" t-attf-class="{{pos.config.useProxy ? 'd-flex align-items-center justify-content-center' : ''}} btn btn-secondary btn-lg w-100 text-center" title="Customer Display">
<i class="fa fa-fw fa-desktop"/>
</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ export class ClosePosPopup extends Component {
}
async closeSession() {
this.pos._resetConnectedCashier();
if (this.pos.config.customer_display_type === "proxy") {
const proxyIP = this.pos.getDisplayDeviceIP();
const proxyIP = this.pos.getDisplayDeviceIP();
if (proxyIP) {
fetch(`${deduceUrl(proxyIP)}/hw_proxy/customer_facing_display`, {
method: "POST",
headers: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ export class CustomerDisplayPosAdapter {
}

dispatch(pos) {
if (pos.config.customer_display_type === "local") {
this.channel.postMessage(JSON.parse(JSON.stringify(this.data)));
}

if (pos.config.customer_display_type === "remote") {
pos.data.call("pos.config", "update_customer_display", [
[pos.config.id],
this.data,
pos.config.access_token,
]);
}

const proxyIP = pos.getDisplayDeviceIP();
if (proxyIP) {
fetch(`${deduceUrl(proxyIP)}/hw_proxy/customer_facing_display`, {
Expand All @@ -46,6 +34,17 @@ export class CustomerDisplayPosAdapter {
}).catch(() => {
console.log("Failed to send data to customer display");
});
} else {
this.channel.postMessage(JSON.parse(JSON.stringify(this.data)));
pos.data
.call("pos.config", "update_customer_display", [
[pos.config.id],
this.data,
localStorage.getItem("device_uuid"),
])
.catch((error) => {
console.info("Failed to update customer display:", error);
});
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Component } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { qrCodeSrc } from "@point_of_sale/utils";
import { CopyButton } from "@web/core/copy_button/copy_button";

export class QrCodeCustomerDisplay extends Component {
static template = "point_of_sale.QrCodeCustomerDisplay";
static components = { Dialog, CopyButton };
static props = ["close", "customerDisplayURL"];

getQrCode() {
return qrCodeSrc(this.props.customerDisplayURL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">

<t t-name="point_of_sale.QrCodeCustomerDisplay">
<Dialog title.translate="Open Customer Display" footer="false">
<div class="text-center">
<img id="CustomerDisplayqrCode" t-att-src="getQrCode()" alt="Customer QR Code"
class="img-fluid mb-3" style="max-width: 200px; width: 50%;" />

<div class="small mb-3">
<t t-set="customerDisplayURL" t-value="props.customerDisplayURL"/>
<a t-att-href="customerDisplayURL" target="_blank" t-esc="customerDisplayURL" />
<CopyButton content="customerDisplayURL" className="'ms-2 btn-primary'" successText.translate="Link copied to clipboard" icon="'fa-clone'"/>
</div>
</div>
</Dialog>
</t>
</templates>
5 changes: 1 addition & 4 deletions addons/point_of_sale/static/src/app/pos_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { reactive, Component, onMounted, onWillStart } from "@odoo/owl";
import { effect } from "@web/core/utils/reactive";
import { batched } from "@web/core/utils/timing";
import { useOwnDebugContext } from "@web/core/debug/debug_context";
import { CustomerDisplayPosAdapter } from "@point_of_sale/customer_display/customer_display_adapter";
import { CustomerDisplayPosAdapter } from "@point_of_sale/app/customer_display/customer_display_adapter";
import { useIdleTimer } from "./utils/use_idle_timer";
import useTours from "./hooks/use_tours";
import { init as initDebugFormatters } from "./utils/debug-formatter";
Expand Down Expand Up @@ -45,9 +45,6 @@ export class Chrome extends Component {

onWillStart(this.pos._loadFonts);
onMounted(this.props.disableLoader);
if (this.pos.config.customer_display_type === "none") {
return;
}
effect(
batched(({ selectedOrder, scale }) => {
if (selectedOrder) {
Expand Down
14 changes: 6 additions & 8 deletions addons/point_of_sale/static/src/app/services/pos_store.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { WithLazyGetterTrap } from "@point_of_sale/lazy_getter";
import { debounce } from "@web/core/utils/timing";
import DevicesSynchronisation from "../utils/devices_synchronisation";
import { deserializeDateTime, formatDate } from "@web/core/l10n/dates";
import { openCustomerDisplay } from "@point_of_sale/customer_display/utils";
import { openProxyCustomerDisplay } from "@point_of_sale/customer_display/utils";

const { DateTime } = luxon;

Expand Down Expand Up @@ -562,13 +562,11 @@ export class PosStore extends WithLazyGetterTrap {
this.markReady();
this.showScreen(this.firstScreen);
await this.deviceSync.readDataFromServer();
if (this.config.customer_display_type !== "none") {
openCustomerDisplay(
this.getDisplayDeviceIP(),
this.config.access_token,
this.config.id
);
}
openProxyCustomerDisplay(
this.getDisplayDeviceIP(),
this.config.access_token,
this.config.id
);
}

get productListViewMode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@ export const CustomerDisplayDataService = {
dependencies: ["bus_service", "notification"],
async start(env, { bus_service, notification }) {
const data = reactive({});
if (session.type === "local") {
new BroadcastChannel("UPDATE_CUSTOMER_DISPLAY").onmessage = (event) => {
Object.assign(data, event.data);
};
}
if (session.type === "remote") {
getOnNotified(bus_service, session.access_token)(
"UPDATE_CUSTOMER_DISPLAY",
(payload) => {
Object.assign(data, payload);
}
);
}
if (session.proxy_ip) {
const intervalId = setInterval(async () => {
try {
Expand Down Expand Up @@ -55,6 +42,16 @@ export const CustomerDisplayDataService = {
clearInterval(intervalId);
}
}, 1000);
} else {
new BroadcastChannel("UPDATE_CUSTOMER_DISPLAY").onmessage = (event) => {
Object.assign(data, event.data);
};
getOnNotified(bus_service, session.access_token)(
`UPDATE_CUSTOMER_DISPLAY-${session.device_uuid}`,
(payload) => {
Object.assign(data, payload);
}
);
}
return data;
},
Expand Down
2 changes: 1 addition & 1 deletion addons/point_of_sale/static/src/customer_display/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { deduceUrl } from "@point_of_sale/utils";
import { _t } from "@web/core/l10n/translation";

export function openCustomerDisplay(
export function openProxyCustomerDisplay(
displayDeviceIp,
accessToken,
configId,
Expand Down
1 change: 0 additions & 1 deletion addons/point_of_sale/tests/test_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,6 @@ def test_product_combo_price(self):
self.assertEqual(order.lines.filtered(lambda l: l.product_id.type == 'combo').margin_percent, 0)

def test_customer_display_as_public(self):
self.main_pos_config.customer_display_type = 'remote'
self.main_pos_config.customer_display_bg_img = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'
response = self.url_open(f"/web/image/pos.config/{self.main_pos_config.id}/customer_display_bg_img")
self.assertEqual(response.status_code, 200)
Expand Down
Loading