feat(pos/workshop): add 80mm thermal ticket printing for service orders
- Add generate_service_order_ticket() in thermal_printer.py with ESC/POS commands for 58mm and 80mm printers. - Add POST /pos/api/service-orders/:id/print endpoint returning raw bytes or JSON for browser rendering. - Extend printer.js with printServiceOrder() using WebUSB/Web Serial. - Add Imprimir orden button in workshop.js detail modal. - Update FASES_IMPLEMENTADAS.md.
This commit is contained in:
@@ -369,3 +369,61 @@ def delete_catalog_item(item_id):
|
||||
return jsonify({'message': 'Catalog item deactivated'})
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── Thermal printing ─────────────────────────────
|
||||
|
||||
|
||||
@service_order_bp.route('/<int:so_id>/print', methods=['POST'])
|
||||
@require_auth()
|
||||
def print_service_order_ticket(so_id):
|
||||
"""Generate a printable ticket for a service order.
|
||||
|
||||
Body (optional): {printer_type: 'escpos_raw' | 'browser', width: 58 | 80}
|
||||
- escpos_raw: returns raw ESC/POS bytes (application/octet-stream)
|
||||
- browser: returns the data dict as JSON for browser-side rendering
|
||||
"""
|
||||
from flask import Response
|
||||
from services.thermal_printer import generate_service_order_ticket
|
||||
|
||||
body = request.get_json(silent=True) or {}
|
||||
printer_type = body.get('printer_type', 'escpos_raw')
|
||||
width = int(body.get('width', 80))
|
||||
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
|
||||
order = get_service_order(conn, so_id)
|
||||
if not order:
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'error': 'Service order not found'}), 404
|
||||
|
||||
# Fetch business info from config
|
||||
business_info = {'name': 'NEXUS AUTOPARTS', 'rfc': '', 'address': ''}
|
||||
try:
|
||||
cur.execute(
|
||||
"SELECT key, value FROM config WHERE key IN ('business_name','rfc','address')"
|
||||
)
|
||||
for rw in cur.fetchall():
|
||||
if rw[0] == 'business_name':
|
||||
business_info['name'] = rw[1]
|
||||
else:
|
||||
business_info[rw[0]] = rw[1]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if printer_type == 'browser':
|
||||
return jsonify(order)
|
||||
|
||||
raw = generate_service_order_ticket(order, business_info, width=width)
|
||||
return Response(
|
||||
raw,
|
||||
mimetype='application/octet-stream',
|
||||
headers={
|
||||
'Content-Disposition': f'attachment; filename=orden_{order.get("order_number", so_id)}.bin'
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user