import socket import logging from odoo import models, fields, api, _ _logger = logging.getLogger(__name__) class PosPrinter(models.Model): _inherit = 'pos.printer' printer_type = fields.Selection( selection_add=[('network_escpos', 'Network ESC/POS Printer')], ondelete={'network_escpos': 'set default'} ) network_printer_ip = fields.Char( string='Network Printer IP Address', help="IP address of the generic ESC/POS network printer.", default="0.0.0.0" ) @api.model def _load_pos_data_fields(self, config_id): fields = super()._load_pos_data_fields(config_id) if 'network_printer_ip' not in fields: fields.append('network_printer_ip') return fields @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. receipt_data expected format: { 'order_name': 'Order 0001', 'order_time': '2023-10-01 12:00:00', 'lines': [ { 'name': 'Burger', 'qty': 1, 'note': 'No onions' } ], 'waiter': 'Mitchell Admin', 'table': 'T1' } """ printer = self.browse(printer_id) if not printer or not printer.network_printer_ip: return {'successful': False, 'message': 'Printer IP not configured'} # Build basic ESC/POS commands ESC = b'\x1b' GS = b'\x1d' INIT = ESC + b'@' CUT = GS + b'V\x41\x00' BOLD_ON = ESC + b'E\x01' BOLD_OFF = ESC + b'E\x00' SIZE_DOUBLE = GS + b'!\x11' SIZE_NORMAL = GS + b'!\x00' ALIGN_CENTER = ESC + b'a\x01' ALIGN_LEFT = ESC + b'a\x00' NEWLINE = b'\n' data = INIT data += ALIGN_CENTER + SIZE_DOUBLE + BOLD_ON data += b"KITCHEN RECEIPT" + NEWLINE data += SIZE_NORMAL + BOLD_OFF + NEWLINE if receipt_data.get('table'): data += ALIGN_LEFT + SIZE_DOUBLE data += f"Table: {receipt_data['table']}".encode('utf-8') + NEWLINE data += SIZE_NORMAL data += ALIGN_LEFT data += f"Order: {receipt_data.get('order_name', '')}".encode('utf-8') + NEWLINE data += f"Date: {receipt_data.get('order_time', '')}".encode('utf-8') + NEWLINE if receipt_data.get('waiter'): data += f"Waiter: {receipt_data['waiter']}".encode('utf-8') + NEWLINE data += b"-" * 32 + NEWLINE for line in receipt_data.get('lines', []): qty = line.get('qty', 1) name = line.get('name', '') note = line.get('note', '') data += SIZE_DOUBLE data += f"{qty} x {name}".encode('utf-8') + NEWLINE data += SIZE_NORMAL if note: data += f" NOTE: {note}".encode('utf-8') + NEWLINE data += NEWLINE data += b"-" * 32 + NEWLINE + NEWLINE + NEWLINE + CUT # Send to printer try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3.0) s.connect((printer.network_printer_ip, 9100)) s.sendall(data) s.close() return {'successful': True} except Exception as e: _logger.error("Failed to print to network ESC/POS printer %s: %s", printer.network_printer_ip, e) return {'successful': False, 'message': str(e)}