feat(herramientas): impresion de etiquetas QR y manifiesto HTML responsive para escaneo de cajas de herramientas

This commit is contained in:
2026-11-01 09:13:39 -05:00
parent 83cbfcefa8
commit e71e531b51
15 changed files with 1252 additions and 22 deletions
+23
View File
@@ -1,4 +1,5 @@
const mysql = require('mysql2/promise');
const crypto = require('crypto');
require('dotenv').config();
async function setup() {
@@ -201,6 +202,7 @@ async function setup() {
company_id INT NOT NULL,
nombre VARCHAR(150) NOT NULL,
codigo VARCHAR(50) NULL,
qr_token VARCHAR(64) NULL,
descripcion TEXT NULL,
categoria VARCHAR(100) DEFAULT 'general',
foto_url VARCHAR(255) NULL,
@@ -209,6 +211,7 @@ async function setup() {
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX (company_id),
INDEX (codigo),
INDEX (qr_token),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
@@ -236,6 +239,7 @@ async function setup() {
herramienta_id INT NULL,
ticket_visita_id INT NULL,
tipo_asignacion ENUM('dotacion_fija', 'visita_fsm') DEFAULT 'dotacion_fija',
qr_token VARCHAR(64) NULL,
fecha_asignacion DATETIME DEFAULT CURRENT_TIMESTAMP,
fecha_devolucion DATETIME NULL,
estado ENUM('asignado', 'devuelto', 'observado') DEFAULT 'asignado',
@@ -249,6 +253,7 @@ async function setup() {
INDEX (company_id),
INDEX (tecnico_id),
INDEX (ticket_visita_id),
INDEX (qr_token),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
FOREIGN KEY (tecnico_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (set_id) REFERENCES herramientas_sets(id) ON DELETE SET NULL,
@@ -264,6 +269,24 @@ async function setup() {
await migrateColumn('tecnico_herramientas_asignaciones', 'foto_entrega_url', 'foto_entrega_url VARCHAR(255) NULL AFTER observaciones_entrega');
await migrateColumn('tecnico_herramientas_asignaciones', 'foto_devolucion_url', 'foto_devolucion_url VARCHAR(255) NULL AFTER observaciones_devolucion');
// Migraciones de tokens QR para escaneo e impresión de etiquetas
await migrateColumn('herramientas_sets', 'qr_token', 'qr_token VARCHAR(64) NULL AFTER codigo');
await migrateColumn('tecnico_herramientas_asignaciones', 'qr_token', 'qr_token VARCHAR(64) NULL AFTER tipo_asignacion');
// Backfill de qr_token para sets que aún no tengan uno generado
const [setsSinToken] = await conn.query('SELECT id FROM herramientas_sets WHERE qr_token IS NULL');
for (const s of setsSinToken) {
const token = crypto.randomBytes(12).toString('hex');
await conn.query('UPDATE herramientas_sets SET qr_token = ? WHERE id = ?', [token, s.id]);
}
// Backfill de qr_token para asignaciones sin token
const [asigSinToken] = await conn.query('SELECT id FROM tecnico_herramientas_asignaciones WHERE qr_token IS NULL');
for (const a of asigSinToken) {
const token = crypto.randomBytes(12).toString('hex');
await conn.query('UPDATE tecnico_herramientas_asignaciones SET qr_token = ? WHERE id = ?', [token, a.id]);
}
// Seed herramientas y sets iniciales para Telcotronics si no existen
const [existingTools] = await conn.query('SELECT COUNT(*) as c FROM herramientas WHERE company_id = 1');
if (existingTools[0].c === 0) {