feat: Campo model en impresoras y guías de formato MCP
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 36s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 36s
- 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
This commit is contained in:
@@ -7,6 +7,106 @@ import { getAllPrinters, getSelectedPrinter, getPrinterById } from './printers'
|
|||||||
import { buildFromOperations } from './eposBuilder'
|
import { buildFromOperations } from './eposBuilder'
|
||||||
import { buildSoapEnvelope, sendToPrinter, parsePrinterResponse } from './printer'
|
import { buildSoapEnvelope, sendToPrinter, parsePrinterResponse } from './printer'
|
||||||
|
|
||||||
|
// Guías de impresión por modelo
|
||||||
|
export const PRINTING_GUIDES: Record<string, {
|
||||||
|
model: string
|
||||||
|
type: string
|
||||||
|
columns: number
|
||||||
|
columnsDoubleWidth: number
|
||||||
|
supportedChars: string
|
||||||
|
rules: string[]
|
||||||
|
operations: string[]
|
||||||
|
example: string
|
||||||
|
}> = {
|
||||||
|
'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
|
// Definición de las tools MCP
|
||||||
export const MCP_TOOLS = [
|
export const MCP_TOOLS = [
|
||||||
{
|
{
|
||||||
@@ -147,6 +247,29 @@ REGLAS IMPORTANTES:
|
|||||||
},
|
},
|
||||||
required: ['templateId']
|
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<string, any>
|
|||||||
name: p.name,
|
name: p.name,
|
||||||
host: p.host,
|
host: p.host,
|
||||||
deviceId: p.deviceId,
|
deviceId: p.deviceId,
|
||||||
|
model: p.model || 'No especificado',
|
||||||
isDefault: p.isDefault,
|
isDefault: p.isDefault,
|
||||||
isSelected: selected?.id === p.id
|
isSelected: selected?.id === p.id
|
||||||
}))
|
}))
|
||||||
@@ -403,6 +527,44 @@ export async function handleToolCall(toolName: string, args: Record<string, any>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
default:
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface Printer {
|
|||||||
name: string
|
name: string
|
||||||
host: string
|
host: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
|
model?: string // Modelo de impresora (ej: "TM-U220", "TM-T88VI")
|
||||||
timeout: number
|
timeout: number
|
||||||
isDefault: boolean
|
isDefault: boolean
|
||||||
createdAt: string
|
createdAt: string
|
||||||
@@ -94,6 +95,7 @@ export async function createPrinter(data: {
|
|||||||
name: string
|
name: string
|
||||||
host: string
|
host: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
|
model?: string
|
||||||
timeout?: number
|
timeout?: number
|
||||||
isDefault?: boolean
|
isDefault?: boolean
|
||||||
}): Promise<Printer> {
|
}): Promise<Printer> {
|
||||||
@@ -105,6 +107,7 @@ export async function createPrinter(data: {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
host: data.host,
|
host: data.host,
|
||||||
deviceId: data.deviceId,
|
deviceId: data.deviceId,
|
||||||
|
model: data.model,
|
||||||
timeout: data.timeout || 60000,
|
timeout: data.timeout || 60000,
|
||||||
isDefault: data.isDefault || store.printers.length === 0, // Primera impresora es default
|
isDefault: data.isDefault || store.printers.length === 0, // Primera impresora es default
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
@@ -131,6 +134,7 @@ export async function updatePrinter(id: string, data: Partial<{
|
|||||||
name: string
|
name: string
|
||||||
host: string
|
host: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
|
model: string
|
||||||
timeout: number
|
timeout: number
|
||||||
isDefault: boolean
|
isDefault: boolean
|
||||||
}>): Promise<Printer | null> {
|
}>): Promise<Printer | null> {
|
||||||
|
|||||||
Reference in New Issue
Block a user