feat: implement dynamic POS configuration ID resolution and UI updates for printer bypass functionality

This commit is contained in:
Suherdy Yacob 2026-05-20 06:59:57 +07:00
parent cc38ae44c9
commit 0f225a7bfb
5 changed files with 43 additions and 11 deletions

View File

@ -68,6 +68,12 @@
box-shadow: 0 0 8px rgba(220, 53, 69, 0.6); box-shadow: 0 0 8px rgba(220, 53, 69, 0.6);
} }
/* Bypassed Status - Grey */
.bluetooth-status-indicator-bypassed {
background-color: #6c757d;
box-shadow: 0 0 8px rgba(108, 117, 125, 0.4);
}
/* Pulse Animation for Connecting State */ /* Pulse Animation for Connecting State */
@keyframes bluetooth-pulse { @keyframes bluetooth-pulse {
0% { 0% {

View File

@ -265,14 +265,15 @@ export class BluetoothPrinterConfig extends Component {
this._saveConfiguration(); this._saveConfiguration();
} }
/**
* Handle bypass toggle
* @param {Event} event - Change event
*/
onBypassChange(event) { onBypassChange(event) {
this.state.bypassBluetooth = event.target.checked; this.state.bypassBluetooth = event.target.checked;
this.storageManager.setBypassStatus(this.posConfigId, this.state.bypassBluetooth); this.storageManager.setBypassStatus(this.posConfigId, this.state.bypassBluetooth);
// Notify connection status widget about the change immediately
if (this.bluetoothManager && typeof this.bluetoothManager._emit === 'function') {
this.bluetoothManager._emit('connection-status-changed', this.bluetoothManager.getConnectionInfo());
}
if (this.state.bypassBluetooth) { if (this.state.bypassBluetooth) {
this._showNotification('Bluetooth printer bypassed on this device', 'info'); this._showNotification('Bluetooth printer bypassed on this device', 'info');
} else { } else {

View File

@ -4,6 +4,7 @@ import { Component, useState, onWillStart, onWillUnmount } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks"; import { useService } from "@web/core/utils/hooks";
import { BluetoothPrinterConfig } from "./bluetooth_printer_config"; import { BluetoothPrinterConfig } from "./bluetooth_printer_config";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { BluetoothPrinterStorage } from "./storage_manager";
/** /**
* Bluetooth Connection Status Widget * Bluetooth Connection Status Widget
@ -23,6 +24,7 @@ export class BluetoothConnectionStatus extends Component {
setup() { setup() {
this.dialog = useService("dialog"); this.dialog = useService("dialog");
this.notification = useService("notification"); this.notification = useService("notification");
this.storageManager = new BluetoothPrinterStorage();
this.state = useState({ this.state = useState({
status: 'disconnected', status: 'disconnected',
@ -109,6 +111,12 @@ export class BluetoothConnectionStatus extends Component {
this.state.reconnectAttempts = info.reconnectAttempts; this.state.reconnectAttempts = info.reconnectAttempts;
this.state.isReconnecting = info.isReconnecting; this.state.isReconnecting = info.isReconnecting;
this.state.timestamp = info.timestamp; this.state.timestamp = info.timestamp;
// Check if bypassed on this device
const posConfigId = this.props.posConfigId || 1;
if (this.storageManager.getBypassStatus(posConfigId)) {
this.state.status = 'bypassed';
}
} }
/** /**
@ -135,6 +143,8 @@ export class BluetoothConnectionStatus extends Component {
return 'fa fa-bluetooth-b'; return 'fa fa-bluetooth-b';
case 'connecting': case 'connecting':
return 'fa fa-bluetooth'; return 'fa fa-bluetooth';
case 'bypassed':
return 'fa fa-ban';
case 'error': case 'error':
return 'fa fa-exclamation-triangle'; return 'fa fa-exclamation-triangle';
default: default:
@ -158,6 +168,8 @@ export class BluetoothConnectionStatus extends Component {
return this.state.isReconnecting return this.state.isReconnecting
? `Reconnecting... (${this.state.reconnectAttempts}/3)` ? `Reconnecting... (${this.state.reconnectAttempts}/3)`
: 'Connecting...'; : 'Connecting...';
case 'bypassed':
return 'Printer Bypassed (Web Print)';
case 'error': case 'error':
return 'Connection Error'; return 'Connection Error';
default: default:

View File

@ -50,6 +50,11 @@ const originalPrintHtml = PosPrinterService.prototype.printHtml;
// Patch the PosPrinterService to add bluetooth printing functionality // Patch the PosPrinterService to add bluetooth printing functionality
patch(PosPrinterService.prototype, { patch(PosPrinterService.prototype, {
setup(env) {
this.env = env;
super.setup(...arguments);
},
/** /**
* Override the printHtml method to use bluetooth printer * Override the printHtml method to use bluetooth printer
* Falls back to browser print on any failure * Falls back to browser print on any failure
@ -74,15 +79,17 @@ patch(PosPrinterService.prototype, {
console.log('[BluetoothPrint] Web Bluetooth API available'); console.log('[BluetoothPrint] Web Bluetooth API available');
// Check if a Bluetooth printer is configured (from localStorage) // Check if a Bluetooth printer is configured (from localStorage)
// We don't need POS config - we check if user has configured a printer
const storage = new BluetoothPrinterStorage(); const storage = new BluetoothPrinterStorage();
// Get POS config ID dynamically from POS store service
const pos = this.env?.services?.pos;
const posConfigId = pos?.config?.id || 1;
// Check if user has explicitly bypassed bluetooth printer for this device // Check if user has explicitly bypassed bluetooth printer for this device
// We default to config ID 1 since it's hardcoded here as well if (storage.getBypassStatus(posConfigId)) {
if (storage.getBypassStatus(1)) {
console.log('[BluetoothPrint] Bluetooth printer bypassed on this device, using standard print'); console.log('[BluetoothPrint] Bluetooth printer bypassed on this device, using standard print');
try { try {
const result = await originalPrintHtml.call(this, el); const result = await originalPrintHtml.apply(this, arguments);
if (result === false) { if (result === false) {
await this._printViaBrowserDialog(el); await this._printViaBrowserDialog(el);
} }
@ -94,13 +101,13 @@ patch(PosPrinterService.prototype, {
} }
} }
const config = storage.loadConfiguration(1); // Default POS config ID const config = storage.loadConfiguration(posConfigId);
if (!config || !config.deviceId) { if (!config || !config.deviceId) {
console.log('[BluetoothPrint] No Bluetooth printer configured, using standard print'); console.log('[BluetoothPrint] No Bluetooth printer configured, using standard print');
console.log('[BluetoothPrint] Calling originalPrintHtml with:', el); console.log('[BluetoothPrint] Calling originalPrintHtml with:', el);
try { try {
const result = await originalPrintHtml.call(this, el); const result = await originalPrintHtml.apply(this, arguments);
console.log('[BluetoothPrint] originalPrintHtml returned:', result); console.log('[BluetoothPrint] originalPrintHtml returned:', result);
// If original method returned false, it didn't handle the print // If original method returned false, it didn't handle the print
@ -174,7 +181,7 @@ patch(PosPrinterService.prototype, {
} catch (dialogError) { } catch (dialogError) {
console.error('[BluetoothPrint] Browser print dialog also failed:', dialogError); console.error('[BluetoothPrint] Browser print dialog also failed:', dialogError);
// Last resort - try original method // Last resort - try original method
return await originalPrintHtml.call(this, el); return await originalPrintHtml.apply(this, arguments);
} }
} }
}, },

View File

@ -54,6 +54,12 @@ patch(PosStore.prototype, {
return; return;
} }
// Check if user has explicitly bypassed bluetooth printer for this device
if (storageManager.getBypassStatus(this.config.id)) {
console.log('Bluetooth printer is bypassed for this device. Skipping initialization.');
return;
}
// Load printer configuration from local storage // Load printer configuration from local storage
const config = storageManager.loadConfiguration(this.config.id); const config = storageManager.loadConfiguration(this.config.id);