Repositorio: https://github.com/Adaury/multitec Última versión: v1.0.0 — Motor de IA completo, frontend totalmente conectado
ERP especializado en seguridad electrónica (CCTV, redes LAN, fibra óptica, control de acceso, videoporteros, barreras vehiculares, automatización y soporte técnico). El proyecto es el núcleo del sistema: cliente, levantamiento, ingeniería, presupuesto, cotización, compras, ejecución, bitácora, facturación, ampliaciones y soporte cuelgan siempre de un proyecto.
Construidas hasta ahora:
- Fase 1 (fundación): autenticación con roles (
admin/oficina/tecnico), Clientes, núcleo Proyecto con pestañas (Información / Levantamiento / Ingeniería) y Catálogo con código automático. Las notas del Levantamiento se pueden editar en cualquier momento, y cada foto/nota de voz adjunta se puede borrar individualmente. Login con access token + refresh token (el access token dura 60 min y se renueva solo; el refresh token es revocable server-side — "Salir" lo invalida de verdad, no solo borra el token del navegador). Cada registro principal guarda quién lo creó y cuándo se modificó por última vez (created_by/created_at/updated_at). Logging centralizado a archivo (backend/logs/app.log, con rotación) y manejo global de errores: cualquier excepción no prevista devuelve un mensaje genérico al cliente y el detalle completo queda solo en el log del servidor. Pantalla Usuarios (/usuarios, solo admin): crear usuarios oficina/técnico, cambiar rol, activar/ desactivar, resetear contraseña — un admin no puede desactivarse ni quitarse el rol admin a sí mismo. - Fase 2 (comercial): pestañas Presupuesto (resumen, solo total, líneas de catálogo
o texto libre) y Cotización (detalle con ITBIS 18%, estados pendiente/aprobada/no
aprobada/archivada, auto-archivo tras 7 días sin decisión, historial), con conversión de
presupuesto → cotización y pantallas globales
/presupuestosy/cotizaciones. - Fase 3 (operación): al aprobar una cotización se genera automáticamente la lista de materiales (§18 inventario simple: disponible/pendiente de compra/comprado/ instalado) — pestaña Compras con la "lista inteligente" de pendientes. Pestaña Ejecución con 5 etapas secuenciales (Inicio→Instalación→Configuración→Pruebas→ Entrega) y % de avance. Pestaña Bitácora con entradas cronológicas y fotos.
- Fase 4 (administrativa): pestaña Prefactura generada desde una cotización
aprobada (subtotal/ITBIS/total); pestaña Factura de solo lectura con historial —
la conversión Prefactura→Factura es la única acción del sistema restringida a rol
admin (oficina puede crear/ver prefacturas pero no convertirlas). Al convertir se le
asigna un NCF (Número de Comprobante Fiscal, requisito DGII) tomado automáticamente
de una secuencia autorizada administrable en
/ncf(solo admin) — el tipo por defecto se infiere de si el cliente tiene RNC (B01 crédito fiscal) o no (B02 consumo), editable antes de convertir. Cotización y Factura tienen botones "Ver"/"Descargar" con membrete de la empresa (configurable en.env), datos del cliente/proyecto, líneas, totales y el NCF; la Factura además tiene una versión "Detalle de trabajo (sin precios)" — mismas líneas con descripción/cantidad y el total general del servicio, sin precio unitario, ITBIS ni NCF. La pestaña Factura también muestra una referencia del levantamiento (notas, observaciones y fotos) como respaldo de lo facturado. Pestaña Ampliaciones (siempre atadas al mismo proyecto, con enlace opcional a una cotización). Pestaña Tickets de soporte con historial de estados. - Fase 5 (IA): "🤖 Organizar con IA" en Levantamiento (resume notas + analiza
fotos, guarda en
Survey.ai_summary). Dictado por voz (🎙️) en los campos de texto del Levantamiento vía Web Speech API del navegador — ningún modelo de IA transcribe audio nativamente, así que esto es 100% del lado del cliente, sin costo. "🤖 Generar propuesta técnica" en Ingeniería (borrador editable, no se auto-guarda). "🤖 Sugerir materiales" en Presupuesto (prellenar líneas desde el catálogo real). Pantalla global "Preguntar a la IA" (/preguntar): elige un proyecto y pregunta en lenguaje natural sobre su expediente completo, o elige "🔎 Todos los proyectos" para una búsqueda semántica entre todo el historial de la empresa — usa embeddings locales (nomic-embed-textvía Ollama) para encontrar los proyectos más relevantes a la pregunta y responde citando de cuáles proyectos sacó la información. Los proyectos se indexan automáticamente la primera vez que se usa alguna función de IA sobre ellos. Motor de IA: local y gratis con Ollama (llama3.2para texto,llavapara fotos) — corre en tu propia PC, sin costo por uso ni API key. Si Ollama no está corriendo, cada botón muestra un mensaje claro en vez de fallar — ver Configurar la IA.
Con esto quedan completadas las 5 fases del brief original.
Construido después del brief original:
- NCF en Facturación y PDF de Cotización/Factura (con una versión sin precios).
- Dashboard con reportes/KPIs en la pantalla de inicio (
/, solo admin/oficina) — cotizaciones pendientes, tickets abiertos por técnico, proyectos por estado, y un gráfico de facturación de los últimos 6 meses. - Búsqueda global (ícono 🔍, disponible para los tres roles) que encuentra clientes, proyectos y tickets desde cualquier pantalla.
- Asignación de técnico a tickets desde la propia pestaña Tickets.
- Notificaciones por correo (cotización pendiente, ticket asignado, factura emitida — ver Notificaciones por correo) con su equivalente dentro de la app (campanita 🔔 con contador de no leídas).
- Control de inventario de bodega en el Catálogo (entradas/salidas de stock por producto, con historial).
- Exportar a CSV (Clientes, Proyectos, Facturas, resumen del Dashboard).
- Reporte 607 (Ventas) para la DGII en
/ncf— ver la advertencia de alcance en Reporte 607. - Calendario de visitas técnicas (
/calendario, los tres roles) — agenda simple por día, con técnico y hora opcionales. - Fotos de evidencia en Tickets — subir/borrar fotos individuales, como ya era posible en Levantamiento y Bitácora.
- Portal de cliente (
/portal/{token}, sin login) — el cliente ve el estado de su proyecto, cotizaciones y facturas (con PDF), a través de un enlace con token opaco activable/regenerable/desactivable desde la pestaña Información del proyecto. - Rentabilidad / margen (solo
admin) — venta vs. costo del catálogo en Cotización, Factura, un resumen por proyecto y un KPI en el Dashboard, calculado en vivo, sin exponer el costo aoficina/tecniconi en PDFs.
- Backend: FastAPI + SQLAlchemy + Alembic, Python 3.14. Autenticación JWT (access +
refresh token), roles
admin/oficina/tecnico. - Base de datos: SQLite por defecto en desarrollo (
backend/multitec.db). CambiarDATABASE_URLenbackend/.envpara usar PostgreSQL sin tocar el código. - Frontend: React + Vite + TypeScript + Tailwind CSS v4, PWA (instalable en iPhone y escritorio), mobile-first, estilo inspirado en Apple.
| Clientes / Proyectos | Levantamiento / Ingeniería / Ejecución / Bitácora / Tickets | Presupuestos / Cotizaciones / Compras / Facturación / Ampliaciones / Catálogo | Convertir Prefactura → Factura | |
|---|---|---|---|---|
| admin | leer y escribir | leer y escribir | leer y escribir | ✅ único rol que puede |
| oficina | leer y escribir | leer y escribir | leer y escribir | ❌ |
| tecnico | solo leer | leer y escribir | ❌ sin acceso | ❌ |
tecnico es para el personal de campo: puede ver en qué proyecto trabajar y llenar todo
lo operativo (levantamiento, ejecución, bitácora, tickets), pero no gestiona clientes,
dinero ni facturación.
- Rate limiting en
/api/auth/login(10 intentos/minuto por IP) y/api/auth/refresh(30/minuto), víaslowapi. - Límite de tamaño de subida de fotos/audio (
MAX_UPLOAD_MB, 25 MB por defecto). - Validación de longitud en todos los campos de texto libre expuestos por la API (nombres, notas, descripciones), para que coincidan con los límites de columna en la base de datos y no se puedan enviar payloads arbitrariamente grandes.
- Manejo global de errores: cualquier excepción no capturada devuelve un 500 genérico
al cliente (nunca detalles internos como rutas o queries) y queda registrada completa
en
backend/logs/app.log. - Refresh tokens revocables: se guarda solo un hash SHA-256 en la base de datos
(nunca el token en texto plano); "Salir" revoca el token en el servidor, no solo borra
el
localStorage. - Advertencia automática al arrancar si
JWT_SECRETsigue en su valor por defecto (ver deploy/README.md para la lista completa de qué cambiar antes de producción). - Portal de cliente: los únicos endpoints sin autenticación de todo el backend
(
/api/public/projects/{token}...) — protegidos por un token opaco de 256 bits (secrets.token_urlsafe(32), no el id numérico del proyecto), con el mismo rate limiting que login (30/min por IP) y validación de que cada recurso pedido (p. ej. una factura) realmente pertenezca al proyecto del token.
- Python 3.10+ (probado con 3.14)
- Node.js 20+ (probado con 24)
- (Opcional, para producción) PostgreSQL
cd backend
python -m venv venv
source venv/Scripts/activate # Windows Git Bash. En PowerShell: venv\Scripts\Activate.ps1
pip install -r requirements.txt
cp .env.example .env # ajustar ADMIN_EMAIL / ADMIN_PASSWORD / JWT_SECRET
alembic upgrade head # crea las tablas
python -m app.db.seed # crea el usuario admin inicial
uvicorn app.main:app --reload --port 8000- Swagger interactivo: http://127.0.0.1:8000/docs
- Salud: http://127.0.0.1:8000/api/health
cd backend
pip install -r requirements-dev.txt
pytest -vCorre contra una base SQLite temporal aislada (no toca multitec.db) y no depende de
Ollama — los endpoints de IA se prueban con mocks. Cubre autenticación y roles (incluido
tecnico), refresh tokens, rate limiting, límite de tamaño de subida, manejo global de
errores, clientes, proyectos (código automático + registros iniciales), el flujo completo
presupuesto→cotización→aprobación→materiales (incluyendo que no se dupliquen materiales
al re-aprobar), ejecución por etapas, la restricción "solo admin convierte a factura",
asignación de NCF (por RNC del cliente, override manual, secuencia vencida/agotada/
inactiva), y que las columnas de auditoría (created_by) se llenen correctamente. Corre
automáticamente en cada push/PR vía GitHub Actions.
cd frontend
npm install
npx playwright install chromium
npm run test:e2e # requiere backend en :8000 y frontend en :5173 ya corriendoPrueba la UI real en un navegador: login/logout con revocación de sesión, crear cliente
y proyecto, el flujo completo presupuesto→cotización (ITBIS 18%)→aprobar→materiales, y
gestión de usuarios (crear, correo duplicado, protección contra auto-degradarse). No
depende de Ollama — ningún test usa los botones de IA. La mayoría de los tests reusan
una sola sesión iniciada una vez (e2e/global-setup.ts) para no agotar el rate limit de
login; solo auth.spec.ts arranca sin sesión a propósito. Corre automáticamente en CI
contra un backend real (SQLite fresco + admin sembrado) y un vite dev real.
- Instala Ollama (o vía
winget install --id Ollama.Ollama -een Windows). Queda corriendo como servicio local enhttp://localhost:11434. - Descarga los tres modelos que usa la app:
ollama pull llama3.2 # ~2 GB — texto (propuestas, presupuestos, preguntas) ollama pull llava # ~4.7 GB — visión (análisis de fotos del levantamiento) ollama pull nomic-embed-text # ~274 MB — embeddings (búsqueda semántica entre proyectos)
backend/.envya trae los valores por defecto (OLLAMA_HOST,AI_MODEL=llama3.2,AI_VISION_MODEL=llava,AI_EMBEDDING_MODEL=nomic-embed-text) — no hace falta ninguna API key. Si Ollama no está corriendo o falta un modelo, los botones de IA muestran un mensaje claro en vez de fallar.- Rendimiento: al correr por CPU (sin GPU NVIDIA/CUDA no hay aceleración), cada respuesta puede tardar entre ~10 y ~60+ segundos según la laptop — normal, no es un error. Con GPU NVIDIA, Ollama la usa automáticamente sin cambiar nada en el código.
- GPU AMD (Vulkan) con salida corrupta: en algunas GPU AMD (confirmado en una
Radeon Pro 5300M) el backend Vulkan de Ollama produce JSON truncado o respuestas
vacías por un bug del motor de gramática de llama.cpp. Si la propuesta técnica o la
sugerencia de materiales fallan con errores de JSON, fuerza CPU-only cerrando Ollama
(
taskkill /IM "ollama app.exe" /Fytaskkill /IM ollama.exe /F) y arrancándolo de nuevo con la variableOLLAMA_VULKAN=0puesta antes deollama serve(o definida a nivel de sistema en Windows si el problema persiste tras reiniciar, ya que la app de bandeja de Ollama no la hereda por defecto). - Limitación conocida: las fotos en formato HEIC (nativo de iPhone) no son compatibles con el modelo de visión local — el análisis las omite automáticamente y sigue solo con las fotos en JPEG/PNG/WebP y el texto del levantamiento.
- Alternativa de pago (mejor calidad, más rápida): el proyecto se integró
originalmente con la API de Claude (
claude-haiku-4-5) antes de cambiar a Ollama. Para volver a esa opción, el cambio queda acotado abackend/app/services/ai_client.py+backend/app/core/config.py(ver historial de git) — se necesitaría una API key de https://console.anthropic.com con crédito cargado.
psycopg2-binary ya está en requirements.txt, así que no hace falta instalarlo aparte.
- Instalar PostgreSQL (en Windows:
winget install --id PostgreSQL.PostgreSQL.17 -e). - Crear un usuario y una base de datos dedicados (evita usar el superusuario
postgresdirectamente):CREATE USER multitec WITH PASSWORD 'tu-password'; CREATE DATABASE multitec OWNER multitec;
- En
backend/.env, cambiar:DATABASE_URL=postgresql+psycopg2://multitec:tu-password@localhost:5432/multitec - Ejecutar
alembic upgrade headde nuevo apuntando a la nueva base, ypython -m app.db.seedpara crear el usuario admin (los datos de SQLite no se migran automáticamente — es una base nueva).
Migración probada de punta a punta: las 4 migraciones de Alembic aplican limpio sobre
PostgreSQL sin cambios (son todas create_table, sin ALTER específico de SQLite).
Tres eventos disparan un correo automático:
- Cotización pendiente de aprobar (al crearla, desde Presupuesto→Cotización o directo): a todos los admins activos.
- Ticket asignado: al correo del técnico, cuando se le asigna al crear el ticket o al reasignarlo (no se re-envía si se "reasigna" al mismo técnico que ya tenía).
- Factura emitida: al correo del cliente (si tiene uno registrado), con el PDF de la factura adjunto.
Configurar SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD / SMTP_FROM /
SMTP_USE_TLS en backend/.env. Sin SMTP_HOST configurado (el default), los
correos no se envían de verdad — solo quedan registrados en backend/logs/app.log
("modo consola"), así que no hace falta una cuenta SMTP real para desarrollar. Un fallo
de SMTP nunca revierte ni bloquea la operación que disparó el correo (crear la
cotización, asignar el ticket, emitir la factura) — solo se registra el error en el log.
En /ncf (solo admin/oficina), botón para exportar en CSV las facturas de un mes con las
columnas del formato 607 de la DGII (RNC/cédula del comprador, NCF, fecha, monto
facturado, ITBIS...).
Alcance limitado, léelo antes de usarlo para remitir de verdad:
- No se implementó el formato 606 (Compras) — el sistema no lleva facturas de proveedores con su propio NCF/RNC, haría falta un módulo de compras que no existe.
- No se trackea forma de pago (efectivo/tarjeta/crédito) ni retenciones de ITBIS/ISR, así que esas columnas del 607 salen vacías — hay que completarlas a mano si aplican.
- La estructura de columnas es la mejor aproximación posible a partir de fuentes públicas de la DGII; no se pudo verificar campo por campo contra la plantilla oficial vigente (requiere descargar un ZIP de dgii.gov.do). Verifica el archivo contra la plantilla oficial actual antes de remitirlo — la pantalla lo recuerda explícitamente.
cd frontend
npm install
npm run dev- App: http://localhost:5173 (el dev server hace proxy de
/apiy/uploadshaciahttp://127.0.0.1:8000, así que el backend debe estar corriendo). - Para instalar como PWA en iPhone: abrir en Safari → compartir → "Agregar a pantalla de inicio".
Definido en backend/.env (ADMIN_EMAIL / ADMIN_PASSWORD), creado por
python -m app.db.seed. Cámbialo antes de desplegar a producción.
Para agregar más usuarios (oficina / tecnico), inicia sesión como admin y ve a
Usuarios (/usuarios) — ya no hace falta tocar la base de datos directamente.
Guía completa (servicios de Windows vía NSSM, Caddy como reverse proxy + HTTPS, backups automáticos de PostgreSQL) en deploy/README.md.
multitec/
├─ backend/ FastAPI, modelos, migraciones Alembic, uploads (fotos/audio)
├─ frontend/ React + Vite PWA
└─ deploy/ Scripts y config para correr en Windows Server (ver deploy/README.md)
Todas las fases del brief original están construidas, incluyendo IA local, PostgreSQL, búsqueda semántica entre proyectos y despliegue en Windows Server. Sin pendientes conocidos del brief original — futuras ideas se registran como Issues en el repositorio.
Existe además una visión (no comprometida) de arquitectura multiplataforma — apps nativas de móvil (Flutter), escritorio Windows y macOS sobre el mismo backend — en docs/architecture-vision-multiplatform.md.

