ERP de taller · Servicio técnico

Taller PCCoreERP de servicio técnico, de la orden de reparación al presupuesto que el cliente aprueba desde el teléfono

ERP de taller para PCCore: órdenes, presupuestos que el cliente aprueba por enlace, PDF por correo o WhatsApp y el historial del sistema anterior migrado.

Tecnologías

  • Next.js 16
  • React 19
  • TypeScript
  • NestJS 11
  • Prisma 7
  • PostgreSQL
  • Tailwind CSS v4
  • TanStack Query
  • Zustand
  • React Hook Form + Zod
  • Turborepo + pnpm
  • PDFKit
  • ExcelJS
  • Resend
  • Cloudflare R2
  • node-firebird
  • Swagger / OpenAPI
  • Jest · Vitest
  • Docker
  • Railway
Panel de inicio del ERP de taller de PCCore en modo demostración, con datos de ejemplo: compromisos de entrega y carga por técnico
  • +10.000

    órdenes migradas del sistema anterior

  • 37

    pantallas en el ERP

  • 40

    módulos en la API

  • 12

    informes exportables

Mi rol

Desarrollo full stack del ERP del taller - la API, el panel web y el paquete compartido -, desde el modelo de datos y la migración del sistema anterior hasta el despliegue en Railway y el endurecimiento de seguridad. Entregado a través de NM Tech Studio.

El problema

PCCore repara computadoras, laptops e impresoras en Durán. El taller trabajaba en SGTaller 3, un sistema de escritorio en Delphi sobre Firebird que guardaba todo su historial - más de 10.000 órdenes, 6.679 clientes y 1.017 artículos - y buena parte de su lógica en procedimientos almacenados y triggers. Era el mismo negocio y los mismos clientes que la tienda online, pero no compartían padrón ni inventario, y saber en qué iba una reparación o cuánto iba a costar obligaba al cliente a llamar al taller.

Qué construí

  • ERP con 37 pantallas - órdenes, presupuestos, clientes, equipos, RMA, agenda, inventario, compras, ventas y caja - sobre una API REST de 40 módulos.
  • Circuito completo de la orden de reparación: ingreso con accesorios recibidos y fecha prometida, presupuesto ligado automático, informe técnico, repuestos que descuentan stock, bitácora, fotos y entrega.
  • Documentos para el cliente: la orden y el presupuesto en PDF, enviados por correo o por WhatsApp, con registro de cada envío para no mandar dos veces lo mismo.
  • Aprobación de presupuestos por enlace: el cliente revisa el detalle desde el teléfono y lo aprueba sin crear una cuenta; queda constancia de la fecha, el total aceptado y la IP.
  • Orden impresa con las condiciones de retiro y un QR que abre el seguimiento de la reparación en la tienda online de PCCore.
  • Inventario con control de stock por artículo, tomas de inventario con ajuste y el catálogo de la tienda importado cada hora como artículos.
  • Agenda de entregas por fecha prometida y 12 informes - por técnico, cliente, marca, tipo de equipo, garantías y período -, exportables a Excel y PDF.
  • Cuatro roles - administrador, técnico, recepción y vendedor - sobre una matriz de permisos por recurso y acción que comparten la API y la web.
  • Migración del sistema anterior desde la propia aplicación: el administrador sube el respaldo de Firebird, el sistema lo analiza y solo reemplaza los datos tras su confirmación y un respaldo previo.

Cómo está estructurado

Un workspace de pnpm con Turborepo que se despliega como dos servicios - la API y el panel -. Comparte PostgreSQL con la tienda online de PCCore, pero conserva su propio esquema, sus migraciones y su autenticación.

  • apps/api: NestJS 11 + Prisma 7, API REST bajo /api/v1 con documentación Swagger fuera de producción; apps/web: Next.js 16 con TanStack Query; packages/shared: los tipos, enums y la matriz de permisos que usan los dos lados.
  • El navegador nunca habla directo con la API: la web la expone a través de una ruta BFF que valida el origen, limita tamaño y tiempo, y reenvía en streaming las subidas grandes. La sesión viaja en una cookie httpOnly.
  • Los documentos se generan en el servidor: el PDF de la orden y del presupuesto se arma y se envía desde la API con PDFKit y Resend, y los informes salen a Excel con ExcelJS y a PDF con PDFKit.
  • Tareas programadas en la API: la importación horaria del catálogo de la tienda y los respaldos con pg_dump a Cloudflare R2, con frecuencia configurable desde Ajustes y una bitácora que registra también las corridas fallidas.
  • Despliegue en Railway con un Dockerfile multi-stage por servicio, contenedores sin root y healthchecks, sobre PostgreSQL 18.
  • Pruebas con Jest en la API y Vitest en la web; CodeQL y un workflow de seguridad con auditoría de dependencias, pruebas y build corren en cada push y pull request, y Dependabot vigila las dependencias.

El circuito del taller

La orden de reparación es la entidad central - en el sistema original ya tenía 47 columnas - y todo lo demás cuelga de ella: el presupuesto, los repuestos, las notas, la bitácora y el seguimiento.

  • Ingreso: recepción registra al cliente (o a un cliente eventual, sin ficha), el equipo por número de serie, la falla, los accesorios recibidos, el técnico, la sede y la fecha prometida. Al crearse, la orden se numera, escribe su primer estado en el historial y genera su presupuesto ligado, todo en una sola transacción.
  • Presupuesto: líneas de mano de obra y de materiales con su impuesto; pasa de sin presupuestar a presupuestado, y de ahí a aceptado o no aceptado, con cada cambio en su historial.
  • Envío: el PDF sale por correo, con el detalle también en el cuerpo para quien no abre adjuntos en el teléfono, o por WhatsApp como enlace protegido por token, porque wa.me solo acepta texto. Cada envío queda registrado con canal, destino y usuario.
  • Aprobación: el cliente abre su enlace, revisa y aprueba con un segundo clic. La aprobación se guarda de forma atómica - dos toques seguidos no dejan dos registros - y queda anotada en el historial del presupuesto y en la bitácora de la orden.
  • Reparación: el técnico carga su informe, los repuestos usados, que descuentan stock cuando el artículo lo controla, y sus notas; la orden pasa de sin revisar a en reparación y a terminada, con ramas de garantía y RMA al proveedor.
  • Seguimiento: el QR impreso en la orden abre la página de seguimiento de la tienda, que lee la etapa, el informe, las notas y el presupuesto directamente de la base del taller.
  • Entrega: el botón Entregar guarda la fecha de entrega y la anota en la bitácora; el papel lleva las condiciones de retiro configuradas en Ajustes, y la agenda y el panel muestran lo prometido que sigue pendiente.

Seguridad por capas

El ERP guarda el padrón de miles de clientes y sus presupuestos se aprueban desde un enlace sin sesión, así que la seguridad se reforzó por capas y sin cambiar pantallas ni flujos para quien lo usa.

  • El BFF valida Origin y Sec-Fetch-Site en cada mutación, limita las solicitudes a 32 MB y 120 segundos, y ante una falla de la API devuelve un error genérico con un x-request-id para rastrearlo.
  • Un secreto interno, comparado en tiempo constante, permite que la API rechace cualquier acceso que no llegue por el panel.
  • Bloqueo temporal de la cuenta tras intentos fallidos: mientras dura, el ingreso se rechaza sin llegar a comparar la contraseña.
  • Encabezados de seguridad en la web: CSP, anti-framing, nosniff, política de referencia y HSTS en producción, sin X-Powered-By.
  • Límites en PostgreSQL - tamaño de pool, statement_timeout, query_timeout e idle_in_transaction_session_timeout - para que una consulta pesada no acapare la base.

Por dentro del sitio

  • Taller PCCore: Órdenes de reparación con estado, equipo y acciones (modo demo, datos de ejemplo)
    Órdenes de reparación con estado, equipo y acciones (modo demo, datos de ejemplo).
  • Taller PCCore: Presupuestos con su estado de aprobación (modo demo)
    Presupuestos con su estado de aprobación (modo demo).
  • Taller PCCore: Agenda de entregas prometidas, vencidas y próximas (modo demo)
    Agenda de entregas prometidas, vencidas y próximas (modo demo).
  • Taller PCCore: Artículos e inventario de repuestos (modo demo)
    Artículos e inventario de repuestos (modo demo).
  • Taller PCCore: Reportes por técnico, cliente, garantías y entregas (modo demo)
    Reportes por técnico, cliente, garantías y entregas (modo demo).
  • Taller PCCore: Informes estadísticos de órdenes por estado (modo demo)
    Informes estadísticos de órdenes por estado (modo demo).

El panel de administración

Cada módulo resuelve una parte de la operación diaria del negocio, sin salir del panel.

  • Inicio

    Primero lo urgente - compromisos vencidos y entregas del día -, después la carga por técnico y la antigüedad de la cola, y al final el flujo mensual. Los ingresos solo le llegan al administrador, filtrados en el servidor.

  • Órdenes

    Ingreso del equipo con accesorios, falla, técnico, sede y fecha prometida; cambios de estado con historial, bitácora, notas, fotos e ingreso masivo de varios equipos a la vez.

  • Presupuestos

    Mano de obra desde un catálogo de tareas con precio de referencia y materiales del inventario, con impuesto, historial de estados y aprobación del cliente por enlace.

  • Envíos al cliente

    PDF de la orden y del presupuesto por correo o WhatsApp; cada envío queda registrado y el sistema avisa si ese documento ya se mandó antes.

  • Clientes

    Padrón con sucursales y una búsqueda que ignora tildes - 'munoz' encuentra 'MUÑOZ' -; es el mismo padrón que consulta el checkout de la tienda.

  • Equipos

    Cada equipo se identifica por su número de serie y conserva su historia de reparaciones, con ficha técnica cuando se trata de un motor eléctrico.

  • Artículos e inventario

    Stock con control por artículo, repuestos que se descuentan al usarse en una orden, tomas de inventario con ajuste y el catálogo de la tienda importado cada hora.

  • Agenda

    Órdenes sin entregar ordenadas por fecha prometida y separadas en vencidas, para hoy y próximas.

  • Reportes e informes

    12 informes - reparaciones por técnico, cliente, marca o tipo de equipo, entregas, garantías y órdenes ingresadas por período -, exportables a Excel y PDF.

  • Personal

    Técnicos y personal de recepción con su comisión; el panel muestra cuántas órdenes abiertas, listas para retirar y vencidas tiene cada técnico.

  • Usuarios y roles

    Cuatro roles - administrador, técnico, recepción y vendedor - sobre una matriz de permisos por recurso y acción, con bloqueo de la cuenta tras intentos fallidos de ingreso.

  • Ajustes

    Condiciones de retiro impresas en la orden, dirección del seguimiento para el QR, respaldos automáticos a Cloudflare R2 y migración de datos desde un archivo.

Decisiones técnicas

  • El botón del correo abre una página y la aprobación es un segundo clic: los filtros de correo abren cada enlace para revisarlo, y un enlace que aprobara con solo abrirse quedaría aprobado por un robot. Si el taller cambió el total mientras el cliente lo revisaba, la aprobación se rechaza.
  • Los tokens de seguimiento y de aprobación los genera PostgreSQL de forma aleatoria y no se derivan del número de orden, que es correlativo, ni de la cédula. Además son distintos entre sí: el del QR va impreso en papel, y aprobar un gasto pide una llave que solo llega al correo o al WhatsApp del cliente.
  • La matriz de permisos vive en un paquete compartido: la web la usa para mostrar solo lo que cada rol puede ver, y un test de paridad falla si algún decorador de la API se aparta de ella. Lo sensible, como los ingresos del panel, se recorta en el servidor y no con un if en la interfaz.
  • Modo demostración activado con una variable de build: la web no toca la red ni la base, sirve datos generados desde una sola fuente para que tablero, listados y reportes cuadren entre sí, con fechas relativas al día de hoy, y habilita solo los 10 módulos del circuito del taller. Lo que aún está a medio camino queda fuera: un módulo vacío en una demo hace más daño que uno ausente.
  • Una base, dos dueños: el taller comparte PostgreSQL con la tienda de PCCore, pero Prisma solo conoce las tablas del taller y la tienda conserva sus propias migraciones SQL. El vínculo entre producto y artículo es un id con índice único, sin llave foránea declarada entre las dos plataformas.
  • La tienda publica precios con IVA incluido y el taller suma el IVA al presupuestar, así que al importar un producto el precio se guarda neto: un artículo de $650 en la web sigue costando $650 en el mostrador, en lugar de $747,50.

Retos

  • Reconstruir SGTaller 3 - Delphi con Firebird 2.1, 95 tablas, 12 procedimientos almacenados y 132 triggers - sin perder su lógica: la numeración, el historial de estados y el presupuesto automático de cada orden ahora ocurren dentro de transacciones de la API. La auditoría de la migración cuadró las tablas principales fila por fila y los importes de ventas al centavo.
  • Limpiar la herencia de datos: las fechas vacías llegaron de Firebird como 30/12/1899, lo que hacía figurar como vencidas a las 1.226 órdenes abiertas. El panel descarta las fechas anteriores a 1990 como centinelas de la migración.
  • Hacer segura una migración que reemplaza datos desde el navegador: el formato se detecta por la firma del archivo y no por la extensión, se toma un respaldo antes de purgar, la purga es una lista blanca que nunca toca las tablas de la tienda ni los usuarios, y los archivos de cientos de megas pasan en streaming por el BFF.

Resultado

El ERP corre en producción en Railway - la API y el panel del taller - sobre el mismo PostgreSQL que la tienda. El historial del sistema anterior pasa al nuevo desde la propia aplicación, cada orden sale impresa con su QR, y el cliente sigue su reparación y aprueba su presupuesto desde el teléfono, sin llamar y sin crear una cuenta. El repositorio corre CodeQL, auditoría de dependencias y pruebas automáticas en CI, la base se respalda sola en Cloudflare R2, y el modo demostración permite mostrar el sistema con datos de ejemplo, sin exponer a ningún cliente real.

¿Necesitas algo parecido?

Cuéntame qué estás construyendo y te respondo con alcance, stack y una fecha realista.