From 84249a356512899338d3c9a48ffb054f8f70d53f Mon Sep 17 00:00:00 2001 From: josedario87 Date: Tue, 25 Nov 2025 13:31:02 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20Campo=20model=20en=20impresoras=20y=20g?= =?UTF-8?q?u=C3=ADas=20de=20formato=20MCP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar campo model a interface Printer - Nuevas tools: get_printing_guide, list_models - Guías de formato para TM-U220 (matricial) y TM-T88 (térmica) - list_printers ahora incluye el modelo --- server/utils/mcp.ts | 162 +++++++++++++++++++++++++++++++++++++++ server/utils/printers.ts | 4 + 2 files changed, 166 insertions(+) diff --git a/server/utils/mcp.ts b/server/utils/mcp.ts index 848c6f8..2ce8c7c 100644 --- a/server/utils/mcp.ts +++ b/server/utils/mcp.ts @@ -7,6 +7,106 @@ import { getAllPrinters, getSelectedPrinter, getPrinterById } from './printers' import { buildFromOperations } from './eposBuilder' import { buildSoapEnvelope, sendToPrinter, parsePrinterResponse } from './printer' +// Guías de impresión por modelo +export const PRINTING_GUIDES: Record = { + 'TM-U220': { + model: 'Epson TM-U220', + type: 'Matricial (impact)', + columns: 40, + columnsDoubleWidth: 20, + supportedChars: 'ASCII básico (A-Z, a-z, 0-9, símbolos comunes). Evitar Unicode especial.', + rules: [ + 'Máximo 40 caracteres por línea (20 con width:2)', + 'Usar solo caracteres ASCII básicos', + 'Evitar caracteres Unicode: ╔═║╚╝ y similares', + 'Usar = - _ * para separadores', + 'Siempre usar feed entre elementos para legibilidad', + 'feed:1 entre items, feed:2 entre secciones', + 'feed:4 antes del cut final', + 'Los headers con width:2 deben ser cortos (max 20 chars)' + ], + operations: [ + '{ op: "text", value: "texto" } - Imprime texto', + '{ op: "feed", lines: N } - Avanza N líneas', + '{ op: "cut" } - Corta el papel', + '{ op: "align", align: "left|center|right" } - Alineación', + '{ op: "style", bold: true, underline: true, width: 1-2, height: 1-2 } - Estilos' + ], + example: `[ + { "op": "align", "align": "center" }, + { "op": "style", "bold": true, "width": 2, "height": 2 }, + { "op": "text", "value": "TITULO" }, + { "op": "style", "bold": false, "width": 1, "height": 1 }, + { "op": "feed", "lines": 2 }, + { "op": "text", "value": "================================" }, + { "op": "feed", "lines": 1 }, + { "op": "align", "align": "left" }, + { "op": "text", "value": "Contenido aquí" }, + { "op": "feed", "lines": 4 }, + { "op": "cut" } +]` + }, + 'TM-T88': { + model: 'Epson TM-T88 (series)', + type: 'Térmica', + columns: 48, + columnsDoubleWidth: 24, + supportedChars: 'ASCII extendido, algunos caracteres Unicode soportados', + rules: [ + 'Máximo 48 caracteres por línea (24 con width:2)', + 'Soporta más caracteres que matriciales', + 'Impresión más rápida y silenciosa', + 'Usar feed para espaciado', + 'Soporta imágenes y códigos QR' + ], + operations: [ + '{ op: "text", value: "texto" } - Imprime texto', + '{ op: "feed", lines: N } - Avanza N líneas', + '{ op: "cut" } - Corta el papel', + '{ op: "align", align: "left|center|right" } - Alineación', + '{ op: "style", bold: true, underline: true, width: 1-2, height: 1-2 } - Estilos', + '{ op: "qrcode", data: "texto", size: 4 } - Código QR', + '{ op: "barcode", data: "123456", type: "ean13" } - Código de barras' + ], + example: `[ + { "op": "align", "align": "center" }, + { "op": "style", "bold": true, "width": 2, "height": 2 }, + { "op": "text", "value": "RECIBO" }, + { "op": "style", "bold": false, "width": 1, "height": 1 }, + { "op": "feed", "lines": 1 }, + { "op": "qrcode", "data": "https://example.com", "size": 4 }, + { "op": "feed", "lines": 2 }, + { "op": "cut" } +]` + } +} + +// Obtener guía por modelo (búsqueda flexible) +export function getPrintingGuide(model: string): typeof PRINTING_GUIDES[string] | null { + // Búsqueda exacta + if (PRINTING_GUIDES[model]) return PRINTING_GUIDES[model] + + // Búsqueda parcial (case-insensitive) + const modelLower = model.toLowerCase() + for (const [key, guide] of Object.entries(PRINTING_GUIDES)) { + if (key.toLowerCase().includes(modelLower) || modelLower.includes(key.toLowerCase())) { + return guide + } + } + + // Default a TM-U220 si no se encuentra + return PRINTING_GUIDES['TM-U220'] +} + // Definición de las tools MCP export const MCP_TOOLS = [ { @@ -147,6 +247,29 @@ REGLAS IMPORTANTES: }, required: ['templateId'] } + }, + { + name: 'printercentral_get_printing_guide', + description: 'Obtiene la guía de formato para un modelo de impresora específico. Incluye límites de caracteres, reglas de formato, operaciones disponibles y ejemplos.', + inputSchema: { + type: 'object' as const, + properties: { + model: { + type: 'string', + description: 'Modelo de impresora (ej: "TM-U220", "TM-T88"). Si no se especifica, retorna todas las guías disponibles.' + } + }, + required: [] as string[] + } + }, + { + name: 'printercentral_list_models', + description: 'Lista todos los modelos de impresora soportados con sus características básicas.', + inputSchema: { + type: 'object' as const, + properties: {}, + required: [] as string[] + } } ] @@ -179,6 +302,7 @@ export async function handleToolCall(toolName: string, args: Record name: p.name, host: p.host, deviceId: p.deviceId, + model: p.model || 'No especificado', isDefault: p.isDefault, isSelected: selected?.id === p.id })) @@ -403,6 +527,44 @@ export async function handleToolCall(toolName: string, args: Record } } + case 'printercentral_get_printing_guide': { + const { model } = args + + if (model) { + const guide = getPrintingGuide(model) + return { + content: [{ + type: 'text', + text: JSON.stringify(guide, null, 2) + }] + } + } else { + // Retornar todas las guías + return { + content: [{ + type: 'text', + text: JSON.stringify(PRINTING_GUIDES, null, 2) + }] + } + } + } + + case 'printercentral_list_models': { + const models = Object.entries(PRINTING_GUIDES).map(([key, guide]) => ({ + id: key, + model: guide.model, + type: guide.type, + columns: guide.columns, + columnsDoubleWidth: guide.columnsDoubleWidth + })) + return { + content: [{ + type: 'text', + text: JSON.stringify(models, null, 2) + }] + } + } + default: return { content: [{ diff --git a/server/utils/printers.ts b/server/utils/printers.ts index 5b467e4..fc384ac 100644 --- a/server/utils/printers.ts +++ b/server/utils/printers.ts @@ -8,6 +8,7 @@ export interface Printer { name: string host: string deviceId: string + model?: string // Modelo de impresora (ej: "TM-U220", "TM-T88VI") timeout: number isDefault: boolean createdAt: string @@ -94,6 +95,7 @@ export async function createPrinter(data: { name: string host: string deviceId: string + model?: string timeout?: number isDefault?: boolean }): Promise { @@ -105,6 +107,7 @@ export async function createPrinter(data: { name: data.name, host: data.host, deviceId: data.deviceId, + model: data.model, timeout: data.timeout || 60000, isDefault: data.isDefault || store.printers.length === 0, // Primera impresora es default createdAt: now, @@ -131,6 +134,7 @@ export async function updatePrinter(id: string, data: Partial<{ name: string host: string deviceId: string + model: string timeout: number isDefault: boolean }>): Promise {