diff --git a/models/pos_printer.py b/models/pos_printer.py index 38f7adc..4a66b09 100644 --- a/models/pos_printer.py +++ b/models/pos_printer.py @@ -5,6 +5,29 @@ from odoo import models, fields, api, _ _logger = logging.getLogger(__name__) +def _isolated_printer_thread(ip, data): + """ + Completely isolated printer thread. + Does not touch Odoo environment to avoid GIL/Locking issues. + """ + s = None + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # 2 seconds is enough for a local network printer. + # Reducing from 5s to avoid holding resources too long. + s.settimeout(2.0) + s.connect((ip, 9100)) + s.sendall(data) + _logger.info("POS_PRINTER: Successfully printed to %s", ip) + except Exception as e: + _logger.error("POS_PRINTER: Background print failed for IP %s: %s", ip, e) + finally: + if s: + try: + s.close() + except: + pass + class PosPrinter(models.Model): _inherit = 'pos.printer' @@ -28,26 +51,14 @@ class PosPrinter(models.Model): @api.model def print_network_kitchen_receipt(self, printer_id, receipt_data): """ - Receives structural receipt_data from POS frontend and sends ESC/POS string to network printer. - Optimized to use background threading to prevent blocking the POS transaction/RPC. + Receives structural receipt_data and sends to background worker. """ printer = self.browse(printer_id) if not printer or not printer.network_printer_ip: return {'successful': False, 'message': 'Printer IP not configured'} - # Start printing in a background thread to avoid POS timeout - # We don't need a new cursor here as we've already browsed the printer - # and we don't write to the database in the thread. - thread = threading.Thread(target=self._threaded_print, args=(printer.network_printer_ip, receipt_data)) - thread.daemon = True # Ensure thread doesn't block server shutdown - thread.start() - - return {'successful': True, 'message': 'Print job sent to background queue'} - - def _threaded_print(self, ip, receipt_data): - """Perform the actual socket communication in the background.""" + # Build the ESC/POS data in the main thread (fast) try: - # Build basic ESC/POS commands ESC = b'\x1b' GS = b'\x1d' INIT = ESC + b'@' @@ -92,12 +103,12 @@ class PosPrinter(models.Model): data += b"-" * 32 + NEWLINE + NEWLINE + NEWLINE + CUT - # Send to printer - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(5.0) # Slightly longer timeout for background print - s.connect((ip, 9100)) - s.sendall(data) - s.close() - _logger.info("Successfully printed to kitchen printer at %s", ip) + # Start thread + t = threading.Thread(target=_isolated_printer_thread, args=(printer.network_printer_ip, data)) + t.daemon = True + t.start() + + return {'successful': True, 'message': 'Print job sent to background queue'} except Exception as e: - _logger.error("Background kitchen print failed for IP %s: %s", ip, e) + _logger.error("POS_PRINTER: Failed to build print data: %s", e) + return {'successful': False, 'message': str(e)}