Catálogo de módulos
Catálogo compacto. Para el orden de construcción, dependencias entre módulos y estado actual, ver el Roadmap de desarrollo. Para las reglas de negocio de cada módulo, ver Dominio.
M0 — Auth + RBAC
Sección titulada «M0 — Auth + RBAC»- Propósito: identidad del usuario, login, permisos por rol y por alcance
(
todo/propio/ruta/BODEGA/ read-only). - Roles: todos (
admin,seller,pos,contador). - Depende de: —
- Doc de dominio: Roles y permisos — RN-ACC-01 a 07.
- Spec de diseño:
/superpowers/specs/2026-08-19-auth-rbac-design.md - Estado: ✅ implementado (20-ago-2026). Las 15 tasks del plan cerradas y verificadas de punta a punta en el browser.
- Qué quedó construido:
api/— 349 tests. Schema conaudit_loginmutable, matriz de permisos con alcances, middleware de sesión y permisos, Better-Auth sobre schema propio, recuperación de contraseña con Resend y Mailpit, rate limit, ABM de usuarios y consulta de auditoría con paginación por cursor.web/— 176 tests. Helper BFF, login, recuperación y cambio de contraseña, shell con menú por rol, y los módulos de usuarios y auditoría.- Colección Bruno versionada: 12 requests, 32 aserciones, corriendo en CI contra un servidor real.
- Reglas de negocio descubiertas al implementar: RN-ACC-06 (siempre queda un admin activo) y RN-ACC-07 (un cambio de roles hace efecto en el acto).
- ADR nuevos: ADR-0004 (bitácora inmutable con dos capas) y ADR-0005 (los alcances fallan cerrados).
- Notas:
- Multi-rol por usuario sin switch — todos los roles asignados están activos simultáneamente.
- Matriz de permisos resuelta (sesión M0 del 19-ago-2026) — ver ADR-0003.
- Patrón BFF-native en web/ desde el inicio — ver ADR-0002.
- Stack: Node 22 + Fastify + Drizzle + Postgres + Better-Auth + Next.js — ver ADR-0001.
- Defensa en profundidad: skill del proyecto + helper module + ESLint rule.
- Alcance se aplica en una sola capa de datos (no repetido por endpoint).
- La UI oculta módulos, la API prohíbe.
- Auditoría consultable desde M0 (
/admin/auditoria,/contador/auditoria).
M1 — Productos y catálogo
Sección titulada «M1 — Productos y catálogo»- Propósito: qué se vende — pacas 600ml/300ml y botellón — con sus unidades por paca y conversiones a litros como dato configurable.
- Roles:
admin(edita),pos/seller/contador(leen). - Depende de: M0.
- Doc de dominio: Productos y catálogo — RN-CAT-01 a 11.
- Spec de diseño:
/superpowers/specs/2026-08-21-m1-productos-design.md - Plan de tasks:
/superpowers/plans/2026-08-21-m1-productos.md— 8 tasks. - Estado: ✅ implementado (22-ago-2026). Las 8 tasks cerradas y verificadas contra un servidor real.
- Notas:
- Una sola tabla nueva (
productos). El precio vive ahí, no en M10. - El piso de precio es un
CHECKen la base, no una validación de servicio. litroses columna generada: no se puede desincronizar de sus entradas.- Extiende la matriz de permisos con
crear,editarydesactivar. - RN-CAT-02 exige stock en cero para desactivar: cerrado en M2 (22-ago-2026), cuando existió la tabla contra la cual verificarlo.
- Una sola tabla nueva (
M2 — Stock de producto terminado
Sección titulada «M2 — Stock de producto terminado»- Propósito: stock por ubicación (
BODEGAúnica), lotes con vencimiento automático a 30 días desde empaque, FIFO, bloqueo de vencidos, alertas de stock crítico. - Roles:
admin,pos,seller(consulta),contador(consulta). - Depende de: M0, M1.
- Doc de dominio: Stock — RN-STK-01, 02, 03, 05, 06, 07 y 08. La 04 es de insumos y pertenece a M3.
- Spec de diseño:
/superpowers/specs/2026-08-22-m2-stock-design.md - Plan de tasks:
/superpowers/plans/2026-08-22-m2-stock.md— 9 tasks, ordenadas por costo de revertir. - Estado: ✅ implementado (22-ago-2026). Las 9 tasks cerradas y verificadas contra la base: el libro explica el saldo y la cadena de auditoría no tiene huecos.
- Notas:
- Sin columna de ubicación: una sola bodega y no se modela (RN-STK-01).
- Libro de movimientos + saldo materializado en el lote. El saldo no se edita.
fecha_vencimientose guarda, no se genera: cambiar la regla no puede reescribir el pasado.- La concurrencia es el problema real: dos pantallas descuentan del mismo saldo.
- Cierra la deuda de RN-CAT-02 que dejó M1: hecho (22-ago-2026).
M3 — Insumos
Sección titulada «M3 — Insumos»- Propósito: control de tapas, sellos termoencogibles y bolsas como stock. Stock mínimo configurable con default 200/200. Alerta de reposición.
- Roles:
admin,pos. - Depende de: M0.
- Doc de dominio: Insumos — RN-INS-01 a 04 (todas ✅ confirmadas el 22-ago-2026).
- Spec de diseño:
/superpowers/specs/2026-08-22-m3-insumos-design.md - Estado: ✅ implementado. El código vive en
api/src/modules/insumosdesde el 26-ago-2026, con su pantalla enweb/. - Notas:
- Las bolsas se compran por kilo y se guardan por unidad (RN-INS-02). La conversión la hace el sistema al recibir la compra o al conteo físico, no en producción.
- El cloro y los filtros NO entran al inventario — son gasto, no stock (RN-INS-04).
- El stock mínimo es configurable desde la administración, igual que el aviso de vencimiento de stock (RN-STK-11) (RN-INS-03).
- Falta la equivalencia kg→unidades para bolsas (pregunta 37): hasta tenerla, el sistema no convierte kilos a unidades solo.
M4 — Producción y cierre del día
Sección titulada «M4 — Producción y cierre del día»- Propósito: tandas de procesamiento, corrida medida por caudal × tiempo, cierre diario que genera lote, mermas, descartes con causa obligatoria, mantenimiento de filtros (sedimentos, carbón, UV).
- Roles:
admin,pos. - Depende de: M0, M1, M2, M3.
- Doc de dominio: Producción y agua — RN-PRD-01 a 24.
- Estado: ✅ implementado. El código vive en
api/src/modules/producciondesde el 26-ago-2026, con su pantalla enweb/. - Notas:
- Tanques separados: 13.000 L cruda + 2 × 2000 L procesada.
- Procesamiento rinde 70% (configurable).
- Cierre diario, no por tanda (decisión del 18-ago-2026).
- Tres efectos atómicos: − agua, − insumos, + producto.
M5 — Clientes
Sección titulada «M5 — Clientes»- Propósito: alta con declaración de verificación de documento (no se guarda foto), ficha con saldo, segmento residencial/comercial, crédito opcional, bloqueo de baja con cifras (deuda, botellones, base).
- Roles:
admin,pos,seller,contador(consulta). - Depende de: M0.
- Doc de dominio: Clientes — RN-CLI-08, RN-CLI-12, RN-CLI-15, RN-CLI-16.
- Estado: ✅ implementado. El código vive en
api/src/modules/clientesdesde el 26-ago-2026, con su pantalla enweb/. M14 lo extendió después con direcciones y contacto. - Notas:
- Documento único por persona (RN-CLI-08).
- Verificación como declaración personal, no consulta externa.
- Tipo
residencial/comercialdefine lista de precios (RN-VEN-12).
M6 — Ventas
Sección titulada «M6 — Ventas»- Propósito: venta de mostrador (POS), pedidos por WhatsApp, devoluciones con motivo, descuentos con código y piso absoluto, captura de intención de factura electrónica (emisión post-MVP con Factus).
- Roles:
admin,pos,seller,contador(consulta). - Depende de: M0, M1, M2, M5 (y M10 si se modela precio flexible desde el inicio).
- Doc de dominio: Ventas — RN-VEN-01 a 16.
- Estado: ✅ implementado. El código vive en
api/src/modules/ventasdesde el 26-ago-2026, con su pantalla enweb/. - Notas:
- Venta confirmada es inmutable (RN-VEN-02). Anular siempre — no hay
PATCH. - Corregir (RN-VEN-16) NO es editar: anula y registra una nueva, atómico y enlazado, y solo lo hace un admin.
- Anulación: solo el autor, motivo obligatorio ≥ 10 caracteres (RN-VEN-08).
- Devolución NO cancela la venta — ajusta inventario y opcionalmente saldo.
- Venta confirmada es inmutable (RN-VEN-02). Anular siempre — no hay
M7 — Retornables (botellones y bases)
Sección titulada «M7 — Retornables (botellones y bases)»- Propósito: entrega de botellón como recarga o primera entrega, devolución,
daño con tarifa fija como venta de concepto
dano_base, préstamo y traza de bases porid_stickerasociado a cliente + dirección. - Roles:
admin,pos,contador(consulta). - Depende de: M0, M5.
- Doc de dominio: Botellones y bases — RN-ENV-* y RN-BAS-*.
- Estado: ✅ implementado. El código vive en
api/src/modules/retornablesdesde el 26-ago-2026, con su pantalla enweb/. - El nombre se resolvió construyéndolo: es
retornablesen los dos repos —api/src/modules/retornablesyweb/src/app/(app)/modulos/retornables—.entregaquedó como nombre de los mockups declaude-design/.
M8 — Rutas y seller mobile ⏸ POST-MVP
Sección titulada «M8 — Rutas y seller mobile ⏸ POST-MVP»- Propósito: app nativa del seller con offline-first, carga de ruta, ventas en ruta, cierre de ruta con cuadre (producto, envases, dinero).
- Roles:
seller,admin,contador(consulta). - Depende de: M0, M5, M6, M7.
- Doc de dominio: Rutas — RN-RUT-* (referencia para el día que se implemente).
- Estado: ⏸ POST-MVP — diferido por decisión
estrategia/mvp-scope-web-first. - Notas:
- En el MVP, el
selleropera desde la web con su rol. - Si el seller también tiene rol
pos, opera indistintamente desde la web del POS.
- En el MVP, el
M9 — Proveedores y compras
Sección titulada «M9 — Proveedores y compras»- Propósito: registro de proveedores y compras de insumos, con pago mixto contado / transferencia / crédito (raro). Sin módulo de cuentas por pagar completo.
- Roles:
admin,pos(registra),contador(consulta). - Depende de: M0, M3.
- Doc de dominio: Proveedores — RN-PRO-01 a 07.
- Estado: ✅ implementado. El código vive en
api/src/modules/proveedoresdesde el 28-ago-2026, con su pantalla enweb/.
M10 — Precios y promociones
Sección titulada «M10 — Precios y promociones»- Propósito: dos listas de precios por SKU (
residencial/comercial) con piso absoluto, códigos de descuento administrativos con vigencia, motivo y piso absoluto. - Roles:
admin,pos,seller,contador(consulta). - Depende de: M0, M1.
- Doc de dominio: Ventas §RN-VEN-12 y §RN-VEN-13 — precio segmentado y códigos.
- Estado: ✅ entregado dentro de M1 y M6 — nunca tuvo módulo propio. El precio vive en el producto (M1) y el piso y los códigos de descuento se validan al vender (M6).
- Notas:
- Piso absoluto se valida en servidor, no en UI (RN-ACC-02).
- Cambios de lista NO reescriben ventas históricas (RN-VEN-04).
M11 — Contador
Sección titulada «M11 — Contador»- Propósito: panel de solo lectura para el
contador: cartera por edad, facturas sin emitir, ventas por concepto, descargas CSV/PDF. - Roles:
contador,admin. - Depende de: M0 + datos reales de M5, M6, M7, M9, M10.
- Doc de dominio: Contador — RN-CON-01 a 09.
- Estado: ✅ implementado. El código vive en
api/src/modules/contadordesde el 30-ago-2026, con su pantalla enweb/. - Notas:
- NO hereda permisos del
admin. - Cada descarga queda en el log de auditoría (RN-ACC-04).
- NO hereda permisos del
M12 — Alertas
Sección titulada «M12 — Alertas»- Propósito: catálogo de alertas operativas (stock crítico, reposición de insumos, vencimiento próximo, autonomía de planta, mantenimiento de filtros). Alimenta al panel de planta y al panel admin.
- Roles:
admin(configura umbrales),pos(consume y dispara). - Depende de: M0, M2, M3, M4.
- Doc de dominio: (a documentar) — referencia: RN-PRD-17.
- Estado: ✅ implementado. El código vive en
api/src/modules/alertasdesde el 6-sep-2026, y los umbrales se configuran desde la administración.
M13 — Auditoría
Sección titulada «M13 — Auditoría»- Propósito: log transversal de quién hizo qué cuándo — toda acción
sensible (anulación, ajuste, descarte, préstamo, cambio de precio, anulación
con faltante, descarga de PDF) queda registrada con
user_id,rol_ejercido,device_typeytimestamp. - Roles:
admin(consulta). - Depende de: M0 + transversal a todos los módulos.
- Doc de dominio: Roles y permisos §RN-ACC-04.
- Estado: ✅ entregado dentro de M0 —
api/src/modules/auditdesde el 20-ago-2026. Es transversal: cada módulo escribe su fila dentro de la misma transacción que hace el cambio (ADR-0007). - Notas:
- Es transversal — se implementa como middleware/log de la capa de datos.
- Con
adminconcentrando todo el poder de corrección, la auditoría es el único control real (ver Roles y permisos).
M14 — Clientes ubicables
Sección titulada «M14 — Clientes ubicables»- Propósito: direcciones estructuradas en la nomenclatura colombiana
(
CL 45 A # 12 B - 34), municipio/departamento del DANE con ortografía correcta, pin arrastrable en mapa para coordenadas, y al menos un canal de contacto por cliente (teléfono obligatorio, dirección que ubique). - Roles:
admin,pos,seller,contador(consulta). - Depende de: M0, M5.
- Doc de dominio: Clientes §RN-CLI-08 a §RN-CLI-19.
- Estado: ✅ implementado (post-demo, 10-sep-2026). Salió de la primera demo con el cliente, no estaba en el roadmap.
- Notas:
direcciones_ubicable: la base exige al menos uno entre estructura, línea libre, indicaciones o coordenadas. Ningún campo de ubicación es obligatorio individualmente.- El pin lo pone la persona, no un servicio de geocodificación: las coordenadas son la base de M8.
- Municipio y departamento vienen del DANE (33 / 1122), en minúscula. Las veredas —que el DANE no lista— se aceptan igual.
M15 — Seguimientos
Sección titulada «M15 — Seguimientos»- Propósito: vista dedicada con la lista de direcciones a las que hay que llamar —días sin recibir, dirección, teléfonos con etiqueta, botón de WhatsApp. La lista completa salió del tablero; el tablero avisa con un pendiente que cuenta cuántas hay y lleva a la vista.
- Roles:
admin,seller,pos,contador. - Depende de: M0, M5.
- Doc de dominio: Clientes §RN-CLI-18.
- Estado: ✅ implementado (21-sep-2026). Es el primer caso del patrón «sección pesada del tablero migra a módulo propio».
- Notas:
- Replanteado el 24-sep-2026. La fila dejó de ser el cliente y pasó a ser la dirección, y la lista se partió en dos canales —botellones y otros productos— con un reloj cada uno. Las dos razones son la misma: la cuenta por cliente y el contador único escondían filas. El detalle está en RN-CLI-18.
GET /clientes/a-llamarconserva la ruta y los permisos (clientes:ver) pero cambió de forma: contesta{ botellones, otros }.- La vista pasó de tarjetas a tabla densa con encabezado fijo: 110 px por tarjeta contra 37 px por fila en escritorio, y con una columna de dirección que antes no existía. Quien la usa venía de un Excel y tenía razón en extrañarlo: para recorrer cuarenta filas eligiendo a quién llamar, una planilla es mejor herramienta que cuarenta tarjetas.
- Abajo de 768 px la tabla se apila (
.aq-tabla-apilada) en vez de scrollear en horizontal. Es lo contrario de lo que hace Auditoría conTh fija, y a propósito: allá la fila se lee comparándola con la de arriba, acá se lee sola. - El tablero conserva el recordatorio:
N direcciones para llamar → Ir a Seguimientos, sumando los dos canales. Sin él, el tablero pierde una señal clara de «hay alguien a quien llamar» y el módulo queda invisible hasta que alguien lo abra por curiosidad.
Resumen de dependencias
Sección titulada «Resumen de dependencias»M0 ─┬─▶ M1 ─┬─▶ M2 ──┐ │ ├─▶ M3 ──┼─▶ M4 │ └─────────┘ ├─▶ M5 ──┬─▶ M6 (necesita M1, M2, M10) │ └─▶ M7 └─▶ M10 (necesita M1)
M8 (POST-MVP) ◀── M0, M5, M6, M7M9 ◀── M0, M3M11 ◀── M0 + datos de M5, M6, M7, M9, M10M12 ◀── M0, M2, M3, M4M13 ◀── M0 (transversal)Para el orden exacto de construcción y los criterios de cadencia, ver el Roadmap de desarrollo.
