Add ZPL label generator and print service with tests
Implement ZPL generator for creating Zebra printer labels from article data (description, intercambio, barcode) with support for multiple copies and description truncation. Add TCP-based print service for sending ZPL data to network printers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
28
src/services/print-service.js
Normal file
28
src/services/print-service.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const net = require('net');
|
||||
|
||||
function printZpl(zplData, printerIp, printerPort = 9100) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = new net.Socket();
|
||||
|
||||
client.connect(printerPort, printerIp, () => {
|
||||
client.write(zplData, () => {
|
||||
client.end();
|
||||
});
|
||||
});
|
||||
|
||||
client.on('close', () => {
|
||||
resolve({ success: true });
|
||||
});
|
||||
|
||||
client.on('error', (err) => {
|
||||
reject(new Error(`Print error: ${err.message}`));
|
||||
});
|
||||
|
||||
client.setTimeout(10000, () => {
|
||||
client.destroy();
|
||||
reject(new Error('Print timeout: could not connect to printer'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { printZpl };
|
||||
19
src/services/zpl-generator.js
Normal file
19
src/services/zpl-generator.js
Normal file
@@ -0,0 +1,19 @@
|
||||
function generateLabel(article, quantity = 1) {
|
||||
const desc = (article.descripcion || '').substring(0, 40);
|
||||
const intercambio = article.intercambio || 'S/N';
|
||||
const code = article.noIdentificacion || '';
|
||||
|
||||
const singleLabel = [
|
||||
'^XA',
|
||||
`^FO30,20^A0N,28,28^FD${desc}^FS`,
|
||||
`^FO30,60^A0N,22,22^FDInt: ${intercambio}^FS`,
|
||||
`^FO30,95^BY2^BCN,50,Y,N,N^FD${code}^FS`,
|
||||
'^XZ',
|
||||
].join('\n');
|
||||
|
||||
if (quantity <= 1) return singleLabel;
|
||||
|
||||
return Array(quantity).fill(singleLabel).join('\n');
|
||||
}
|
||||
|
||||
module.exports = { generateLabel };
|
||||
52
tests/zpl-generator.test.js
Normal file
52
tests/zpl-generator.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { generateLabel } from '../src/services/zpl-generator.js';
|
||||
|
||||
describe('ZPL Generator', () => {
|
||||
it('should generate valid ZPL with name, intercambio, and barcode', () => {
|
||||
const zpl = generateLabel({
|
||||
descripcion: 'Balata ceramica delantera',
|
||||
intercambio: 'INT-9999',
|
||||
noIdentificacion: 'BK-001',
|
||||
});
|
||||
|
||||
expect(zpl).toContain('^XA');
|
||||
expect(zpl).toContain('^XZ');
|
||||
expect(zpl).toContain('Balata ceramica delantera');
|
||||
expect(zpl).toContain('INT-9999');
|
||||
expect(zpl).toContain('BK-001');
|
||||
});
|
||||
|
||||
it('should handle missing intercambio', () => {
|
||||
const zpl = generateLabel({
|
||||
descripcion: 'Filtro de aceite',
|
||||
intercambio: null,
|
||||
noIdentificacion: 'FT-200',
|
||||
});
|
||||
|
||||
expect(zpl).toContain('Filtro de aceite');
|
||||
expect(zpl).toContain('S/N');
|
||||
expect(zpl).toContain('FT-200');
|
||||
});
|
||||
|
||||
it('should truncate long descriptions', () => {
|
||||
const zpl = generateLabel({
|
||||
descripcion: 'Este es un nombre de articulo extremadamente largo que no cabe en la etiqueta',
|
||||
intercambio: 'X-1',
|
||||
noIdentificacion: 'LONG-001',
|
||||
});
|
||||
|
||||
expect(zpl).toContain('^XA');
|
||||
expect(zpl).toContain('^XZ');
|
||||
});
|
||||
|
||||
it('should generate multiple labels for quantity > 1', () => {
|
||||
const zpl = generateLabel({
|
||||
descripcion: 'Balata',
|
||||
intercambio: 'INT-1',
|
||||
noIdentificacion: 'BK-001',
|
||||
}, 3);
|
||||
|
||||
const labelCount = (zpl.match(/\^XA/g) || []).length;
|
||||
expect(labelCount).toBe(3);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user