From 064626b851f86847ec1362977fc2c7d9be558099 Mon Sep 17 00:00:00 2001 From: chattie Date: Fri, 21 Aug 2026 22:06:54 +0200 Subject: [PATCH] feat(F-142): completed feature --- backlog/features.json | 122 ++++++++++ docs/reporting/REPORTING_ARCHITECTURE.md | 295 +++++++++++++++++++++++ docs/reporting/REPORTING_TASKS.md | 241 ++++++++++++++++++ work/artifacts/F-142/implementer.md | 41 ++++ work/artifacts/F-142/leader-close.json | 20 ++ work/artifacts/F-142/qa.json | 20 ++ work/artifacts/F-142/reviewer.json | 19 ++ work/artifacts/F-142/security.json | 19 ++ work/runtime-status.json | 50 +++- 9 files changed, 823 insertions(+), 4 deletions(-) create mode 100644 docs/reporting/REPORTING_ARCHITECTURE.md create mode 100644 docs/reporting/REPORTING_TASKS.md create mode 100644 work/artifacts/F-142/implementer.md create mode 100644 work/artifacts/F-142/leader-close.json create mode 100644 work/artifacts/F-142/qa.json create mode 100644 work/artifacts/F-142/reviewer.json create mode 100644 work/artifacts/F-142/security.json diff --git a/backlog/features.json b/backlog/features.json index 5033146..c2c0b2c 100644 --- a/backlog/features.json +++ b/backlog/features.json @@ -6237,6 +6237,128 @@ "description": "Analyze existing ecommerce, POS, orders, payments, customers, products, stock, cash, refunds, discounts and taxes; produce reporting architecture and phased implementation tasks before dashboard coding.", "priority": "high", "risk": "med", + "status": "done", + "created_at": "2026-08-21", + "gates": { + "reviewer": true, + "security": true, + "qa": true, + "close": true + }, + "completed_at": "2026-08-21T20:06:54Z" + }, + { + "id": "F-143", + "type": "feature", + "title": "Reporting: contracts, filters and RBAC", + "description": "Define shared reporting filters, date comparisons, response availability metadata and backend REPORTING permissions.", + "priority": "high", + "risk": "med", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-144", + "type": "feature", + "title": "Reporting: historical store and financial snapshots", + "description": "Add the minimum store, VAT, cost and shipping snapshots required to report multi-store sales without rewriting history.", + "priority": "high", + "risk": "high", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-145", + "type": "feature", + "title": "Reporting: payment lines and POS cash-safe capture", + "description": "Persist immutable payment lines for ecommerce and POS, including method/store/terminal/session, without card data.", + "priority": "high", + "risk": "high", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-146", + "type": "feature", + "title": "Reporting: service summary and sales API", + "description": "Implement backend ReportingService with filtered summary/sales metrics, comparisons, grouping and pagination.", + "priority": "high", + "risk": "med", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-147", + "type": "feature", + "title": "Admin: reporting shell and global filters", + "description": "Add Reporting navigation, dashboard shell, reusable filters, URL persistence and explicit data states.", + "priority": "high", + "risk": "med", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-148", + "type": "feature", + "title": "Admin: sales dashboard and channel views", + "description": "Present real summary/sales data with KPIs, trends, ecommerce versus POS, stores and terminals.", + "priority": "high", + "risk": "med", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-149", + "type": "feature", + "title": "Reporting: product category and brand reports", + "description": "Add server-side product rankings and category/brand reports using catalog data and availability warnings.", + "priority": "high", + "risk": "med", + "status": "pending", + "created_at": "2026-08-21", + "gates": { + "reviewer": false, + "security": false, + "qa": false + } + }, + { + "id": "F-150", + "type": "feature", + "title": "Reporting: CSV export", + "description": "Export filtered reporting datasets to CSV with permissions, metadata and server-side pagination/streaming.", + "priority": "high", + "risk": "med", "status": "pending", "created_at": "2026-08-21", "gates": { diff --git a/docs/reporting/REPORTING_ARCHITECTURE.md b/docs/reporting/REPORTING_ARCHITECTURE.md new file mode 100644 index 0000000..b897802 --- /dev/null +++ b/docs/reporting/REPORTING_ARCHITECTURE.md @@ -0,0 +1,295 @@ +# Reporting — Arquitectura y análisis de datos + +**Estado:** discovery / diseño inicial (sin dashboards ni endpoints implementados) +**Fecha:** 2026-08-21 +**Tienda por defecto:** Natural - Mercado de Vida + +## 1. Decisiones ejecutivas + +1. **La fuente de verdad sigue siendo el monolito transaccional PostgreSQL.** Reporting no crea ventas, pagos, stock ni clientes paralelos. +2. **El hecho base es `orders_orders` + `orders_items`.** Tanto ecommerce como TPV deben persistir una venta como pedido, distinguiéndola mediante `orders_orders.source` (`ecommerce`, `pos`, `admin`). +3. **Los cálculos viven en backend**, en un módulo `reporting`, no en React. El frontend recibe métricas preparadas, metadatos de disponibilidad y paginación. +4. **Primera versión: SQL directo parametrizado sobre PostgreSQL**, con CTEs y consultas separadas por informe. No se añade una tabla de agregados hasta medir volumen y latencia real. +5. **Filtros son un contrato común y reproducible:** fechas con zona horaria explícita, tienda/canal/terminal/cajero/método de pago/estado/producto/categoría/marca/cliente. La misma estructura alimenta API, URL y exportación. +6. **No se muestran métricas no justificables.** Margen, IVA por tipo, método de pago, devoluciones detalladas y diferencias de caja quedan como `unavailable` hasta disponer de datos históricos fiables. + +## 2. Inventario de modelos existentes + +### 2.1 Hechos transaccionales + +| Tabla | Grano | Datos aprovechables | Limitaciones actuales | +|---|---|---|---| +| `orders_orders` | Un pedido/venta | `id`, usuario, estado, moneda EUR, subtotal, descuento, impuesto, total, fechas, `source`, terminal y sesión POS | No tiene `store_id` directo. El total no separa portes. `user_id` es NULL para walk-in POS. | +| `orders_items` | Una línea de pedido | Producto/variante, SKU/EAN/nombre snapshot, precio unitario, descuento, impuesto, cantidad, fecha | No guarda `cost_at_sale`, tipo de IVA, marca/categoría snapshot ni devolución por línea. | +| `payments_transactions` | Un evento transaccional de proveedor | importe, moneda, estado, proveedor, order_id, fecha, idempotencia | No guarda método de pago, tienda, terminal, sesión, importes parciales de devolución ni FK a order. Eventos sin `order_id` son posibles. | +| `inventory_movements` | Un movimiento de variante/tienda | variante, tienda, operación, cantidad, fecha | No tiene `order_id`, usuario, motivo ni referencia de devolución; no permite casar una confirmación con una venta concreta. | +| `pos_cash_sessions` | Una sesión de caja/terminal | tienda, terminal, cajero, apertura/cierre, esperado, contado, diferencia | No existe tabla de movimientos de caja ni relación de pagos POS. | + +### 2.2 Dimensiones y configuración + +| Tabla | Uso en reporting | +|---|---| +| `identity_users`, `users_profiles` | Clientes ecommerce; `orders_orders.user_id` permite clientes únicos, nuevos y recurrentes. POS walk-in no tiene cliente. | +| `backoffice_users` | Cajeros/admin/editor; debe ser el actor de terminal/sesión y de auditoría, no un cliente. | +| `catalog_products`, `catalog_product_variants` | Nombre, estado, variante, SKU/EAN, peso, marca/categorías actuales. | +| `brands_brands`, `categories_categories`, `catalog_product_categories` | Dimensiones actuales de marca y categoría; los cambios posteriores afectan a informes históricos si no se añade snapshot/dimensión versionada. | +| `pricing_variant_prices` | Precio actual, oferta, coste actual y tipo de IVA. El coste actual **no** puede recalcular margen histórico. | +| `pricing_price_history` | Evolución del precio, no coste histórico de cada venta. | +| `promotions_promotions`, `cart_carts.promo_code` | Definición y uso de promociones; el pedido no guarda el código aplicado como snapshot. | +| `tax_rates` | Configuración actual de IVA por aplicación; no acredita el tipo usado en una venta histórica. | +| `pos_stores`, `pos_terminals`, `pos_payment_methods` | Multi-tienda, terminales y configuración de métodos. La configuración de método no es una transacción de pago. | +| `orders_order_history` | Auditoría de transiciones; útil para timeline, no sustituye devoluciones ni pagos. | + +## 3. Relaciones de reporting + +```text +orders_orders (1) + ├── orders_items (N) ── catalog_products / variants + ├── payments_transactions (N, order_id nullable) + ├── identity_users (0..1) + ├── pos_terminals (0..1) ── pos_stores + ├── pos_cash_sessions (0..1) ── pos_stores + backoffice_users + └── orders_order_history (N) + +orders_items.variant_id + ├── inventory_stock (N: store) + ├── inventory_movements (N: store) + └── pricing_variant_prices (1, current only) +``` + +**Regla de joins:** los endpoints de reporting deben partir de un CTE `filtered_orders` y unir líneas/agregados después. No hacer una consulta por pedido, producto o cliente (N+1). + +## 4. Grano y definiciones de métricas + +El servicio debe declarar en cada respuesta `dataAvailability` y `definitions`. + +### Disponibles con el modelo actual + +- **Pedidos/tickets:** `COUNT(DISTINCT o.id)` con estados incluidos explícitos. +- **Total cobrado registrado:** `SUM(o.total_cents)` para ventas no canceladas, según la política de estados del endpoint. +- **Ventas de mercancía brutas:** `SUM(i.unit_price_cents * i.quantity)`. +- **Descuentos registrados:** `SUM(o.discount_cents)` o `SUM(i.discount_cents)`; nunca sumar ambos. +- **Impuesto registrado:** `SUM(o.tax_cents)` / `SUM(i.tax_cents)`; la API debe escoger un solo grano. +- **Unidades:** `SUM(i.quantity)`. +- **Ticket medio:** total cobrado / pedidos, con división segura por cero. +- **Canal:** `o.source`. +- **Terminal/sesión/cajero:** cuando la venta tenga `terminal_id`/`cash_session_id`; ecommerce sin asignación POS queda explícitamente como `null`/`online`. +- **Productos:** líneas agrupadas por `product_id`/`variant_id`, usando snapshots de nombre/SKU/EAN. +- **Clientes únicos/nuevos/recurrentes:** usuarios en pedidos, siempre separando walk-ins POS y excluyendo `user_id IS NULL` del denominador de clientes. +- **Productos sin movimiento:** stock actual por tienda + última fecha de `orders_items` en venta válida. Es una aproximación hasta que los movimientos tengan `order_id`. + +### No disponibles todavía o solo aproximables + +- **Ventas netas sin portes:** `orders_orders` no separa shipping; hoy solo puede publicarse `total cobrado` y `ventas de mercancía` con nombres claros. +- **Método de pago:** `payments_transactions` no tiene `payment_method_id`/código; `pos_payment_methods` solo es catálogo de configuración. +- **Pago mixto:** no hay líneas de pago por pedido. +- **Devoluciones por importe, producto, motivo o usuario:** solo existen estados `REFUNDED`/`PARTIALLY_REFUNDED` y eventos de pago genéricos. +- **IVA por tipo:** las líneas guardan `tax_cents`, pero no guardan el `vat_rate` aplicado en el momento de la venta. +- **Margen histórico:** `cost_cents` actual no es `cost_at_sale`; no se muestra beneficio/margen hasta añadir snapshot de coste. +- **Descuento por cupón/tipo:** se guarda el total de descuento, no el cupón aplicado en `orders_orders`. +- **Caja por método/movimiento:** hay campos de cierre pero no entradas, salidas, retiradas ni pagos POS asociados. +- **Comparación histórica por categoría/marca:** usa la relación actual del catálogo, no una dimensión histórica; debe etiquetarse como clasificación actual o versionarse. + +## 5. Correcciones de modelo necesarias antes de P0 financiero + +No se deben ocultar estas carencias con cálculos frontend. Tickets de datos deben evaluar: + +1. Añadir `orders_orders.store_id` NOT NULL con FK a `pos_stores`, backfill de la tienda por defecto e índice `(store_id, created_at)`. POS y ecommerce deben escribirlo explícitamente. +2. Añadir a `orders_items` snapshots de `vat_rate` y `cost_at_sale_cents` nullable. Si el coste es NULL, margen es `unavailable`. +3. Crear `order_payments`/`orders_payment_lines` como líneas de pago inmutables: order, método, tienda, terminal, sesión, importe, moneda, provider reference, estado y timestamps. No almacenar PAN/CVV. +4. Crear `order_refunds` y líneas opcionales con importe, cantidad, motivo, actor, canal y timestamps; enlazar eventos de proveedor sin duplicarlos. +5. Persistir el `promo_code`/promotion id y el descuento aplicado por pedido/línea como snapshot. +6. Separar shipping en los totales (`shipping_cents` o una tabla de cargos) para no llamar “ventas netas” al total con portes. +7. Añadir movimientos de caja inmutables (`cash_in`, `cash_out`, `sale`, `refund`, `opening`, `closing`) vinculados a sesión y, cuando aplique, order/payment. + +Cada cambio necesita migración, backfill/compatibilidad y ticket independiente; no forma parte de un dashboard improvisado. + +## 6. Contrato API propuesto + +Prefijo recomendado: `/reporting`. Todas las rutas requieren sesión de backoffice y permisos específicos. + +```text +GET /reporting/summary +GET /reporting/sales +GET /reporting/products +GET /reporting/categories +GET /reporting/brands +GET /reporting/payments +GET /reporting/cash-sessions +GET /reporting/discounts +GET /reporting/refunds +GET /reporting/customers +GET /reporting/inventory +GET /reporting/taxes +GET /reporting/dimensions/stores +GET /reporting/dimensions/terminals +GET /reporting/export/:report.csv +GET /reporting/export/:report.xlsx (fase posterior) +``` + +### Query común + +```text +from=2026-08-01T00:00:00Z +&to=2026-08-31T23:59:59Z +&compare=previous_equal|previous_calendar|none +&channel=all|ecommerce|pos|admin +&storeId= (repetible) +&terminalId= (repetible) +&cashierId= (repetible) +&paymentMethodId= (repetible, cuando exista) +&productId= (repetible) +&categoryId= (repetible) +&brandId= (repetible) +&customerId= +&state=PAID,COMPLETED,... +&groupBy=day|week|month|hour|store|channel|terminal|cashier|payment +&page=1&pageSize=50 +&sort=-revenue +``` + +El parser debe rechazar fechas invertidas, límites excesivos, IDs inválidos y combinaciones no soportadas. Rango inclusivo de inicio y exclusivo de fin (`[from,to)`) para evitar doble conteo. + +### Respuesta común + +```json +{ + "range": {"from":"...","to":"...","timezone":"Europe/Madrid"}, + "filters": {"channel":"pos","storeIds":[]}, + "comparison": {"range":null,"available":true}, + "dataAvailability": {"grossSales":true,"margin":false,"paymentMethod":false}, + "items": [], + "totals": {}, + "updatedAt": "2026-08-21T20:00:00Z", + "cache": {"hit":false,"maxAgeSeconds":30} +} +``` + +Los importes son céntimos enteros y la API entrega además `currency: EUR`. El porcentaje de variación debe ser `null` cuando el período anterior sea cero/no comparable, nunca `Infinity`. + +## 7. Consultas y rendimiento + +### CTE base + +```sql +WITH filtered_orders AS ( + SELECT o.* + FROM orders_orders o + WHERE o.created_at >= $1 + AND o.created_at < $2 + AND o.state IN (...) + AND ($3::text IS NULL OR o.source = $3) + AND (cardinality($4::uuid[]) = 0 OR o.store_id = ANY($4)) +), filtered_items AS ( + SELECT i.*, o.source, o.store_id, o.terminal_id + FROM orders_items i + JOIN filtered_orders o ON o.id = i.order_id +) +SELECT ... +``` + +La versión inicial debe medir `EXPLAIN (ANALYZE, BUFFERS)` con datos representativos. Índices recomendados solo tras confirmar planes: + +- `orders_orders (created_at, source, state)` — valorar parciales según estados. +- `orders_orders (store_id, created_at)` una vez exista `store_id`. +- `orders_orders (terminal_id, created_at)` y `(cash_session_id, created_at)`. +- `orders_items (created_at)` para última venta; `(variant_id, created_at)` para inventario. +- `payments_transactions (created_at, status)` y `(order_id, created_at)`. +- `inventory_movements (store_id, variant_id, created_at)`. + +No añadir índices duplicados indiscriminadamente: comparar con los índices existentes y medir. + +### Caché + +- P0 summary/sales: cache corta 30–60 s, key = reporte + hash ordenado de filtros. +- Tablas de dimensiones: 5 min o invalidación al cambiar catálogo. +- Exportaciones grandes: job/stream posterior, nunca cargar todo en React. +- Respuesta siempre indica `updatedAt` y `cache.maxAgeSeconds`. +- Materialized views/reporting tables quedan fuera hasta que `EXPLAIN` y volumen justifiquen su coste. + +## 8. Frontend Admin + +Ruta raíz: `/reporting`. Subrutas previstas: + +```text +/reporting Resumen +/reporting/sales Ventas +/reporting/products Productos +/reporting/customers Clientes +/reporting/payments Pagos +/reporting/cash Caja +/reporting/discounts Descuentos +/reporting/refunds Devoluciones +/reporting/inventory Inventario +/reporting/taxes IVA +``` + +Componentes reutilizables previstos: + +- `ReportingLayout`, `ReportingFilters`, `DateRangePicker`, `ComparisonSelector`. +- `KpiCard`, `ComparisonKpi`, `AvailabilityBadge`, `ReportingEmptyState`. +- `SalesChart`, `ChannelBreakdown`, `Heatmap` (P2), `ReportingTable`. +- `StoreSelector`, `TerminalSelector`, `CashierSelector`, `ProductSelector`, `ExportButton`. + +Los filtros se serializan en query params, con valores normalizados y sin secretos. La navegación drill-down conserva el filtro cuando la dimensión destino lo soporta. + +## 9. Permisos y seguridad + +El backend debe ser la autoridad, no solo `visibleNavItems` del frontend. Extender RBAC con permisos: + +```text +REPORTING_VIEW +REPORTING_SALES +REPORTING_PRODUCTS +REPORTING_CUSTOMERS +REPORTING_INVENTORY +REPORTING_PAYMENTS +REPORTING_CASH +REPORTING_FINANCIAL +REPORTING_EXPORT +REPORTING_ADMIN +``` + +Compatibilidad inicial: `admin` puede ver todo; `editor` y roles POS necesitan asignación explícita. `REPORTING_FINANCIAL` protege costes/margen, fiscal detallado y diferencias de caja. Aplicar autorización también a exports y a cada filtro de tienda/terminal, evitando que un cajero consulte otra tienda. + +No devolver emails, direcciones ni identificadores de clientes salvo que el informe tenga permiso de clientes. Parametrizar todos los valores SQL y limitar `pageSize`/rango máximo. + +## 10. Criterios de datos y estados + +- Por defecto, ventas = estados `PAID`, `PROCESSING`, `SHIPPED`, `DELIVERED`, `COMPLETED`, `PARTIALLY_REFUNDED`; excluir `PENDING`, `AWAITING_PAYMENT`, `CANCELLED` y decidir cómo netear refund cuando exista la tabla de devoluciones. +- La respuesta diferencia `loading`, `empty`, `no_data`, `partial_data`, `unavailable` y error; no convierte una métrica no disponible en `0`. +- Todas las horas se almacenan en UTC y se agrupan por zona configurada (`Europe/Madrid` inicialmente). +- Los cambios de catálogo no deben reescribir snapshots de pedido. +- Exportación debe incluir rango, zona horaria, filtros, fecha de generación y columnas seleccionadas. + +## 11. Riesgos + +| Riesgo | Mitigación | +|---|---| +| `orders_orders` no tiene tienda para ecommerce | Añadir `store_id` antes del filtro multi-tienda obligatorio. | +| Totales mezclan mercancía, IVA, descuento y portes | Exponer nombres exactos y añadir cargos separados antes de “net sales”. | +| Datos POS aún incompletos | P0 de pagos/caja depende de líneas de pago y movimientos de caja. | +| JOIN de líneas duplica totales | CTE por grano: agregar líneas antes de unir dimensiones 1:N. | +| Coste actual usado históricamente | Rechazar margen hasta disponer de `cost_at_sale_cents`. | +| Reclasificación histórica | Añadir snapshots o declarar “clasificación actual”. | +| Consultas pesadas | Límites, índices medidos, cache corta, paginación server-side y EXPLAIN. | +| Fuga entre tiendas | Scope de tienda en SQL + autorización por usuario/terminal + tests negativos. | +| Export bloqueante | CSV streaming primero; XLSX/PDF en fase posterior/job. | + +## 12. Evolución futura + +La frontera estable será: + +```text +Transactional modules + ↓ +Reporting query/read model (sin duplicar verdad) + ↓ +ReportingService / report definitions + ↓ +Reporting API + export adapter + ↓ +Admin Reporting UI +``` + +Más adelante puede insertarse una agregación/materialized view detrás de la misma interfaz cuando el volumen lo requiera. Forecasting, alertas, cohortes, RFM, informes programados, PDF y BI externo son P3 y no forman parte de la primera implementación. diff --git a/docs/reporting/REPORTING_TASKS.md b/docs/reporting/REPORTING_TASKS.md new file mode 100644 index 0000000..e505874 --- /dev/null +++ b/docs/reporting/REPORTING_TASKS.md @@ -0,0 +1,241 @@ +# Reporting — Roadmap de tareas + +Este documento divide la implementación en tickets pequeños y verificables. **F-142 solo entrega discovery/arquitectura; no implementa dashboards.** Las referencias `RPT-*` son identificadores de trabajo del plan y se han abierto en backlog como: + +| Plan | Ticket backlog | Título | +|---|---|---| +| RPT-001 | F-143 | Contracts, filters and RBAC | +| RPT-002 | F-144 | Historical store and financial snapshots | +| RPT-003 | F-145 | Payment lines and POS cash-safe capture | +| RPT-004 | F-146 | ReportingService, summary and sales API | +| RPT-005 | F-147 | Admin reporting shell and global filters | +| RPT-006 | F-148 | Sales dashboard and channel views | +| RPT-007 | F-149 | Product, category and brand reports | +| RPT-008 | F-150 | CSV export | + +RPT-009 en adelante se abrirá como backlog cuando sus dependencias P0 estén aprobadas. + +## Dependencias y orden recomendado + +```text +RPT-001 contratos/filtros/RBAC + ├── RPT-002 datos históricos mínimos (store, tax/cost snapshots) + ├── RPT-003 líneas de pago POS/ecommerce + └── RPT-004 ReportingService + summary/sales + ├── RPT-005 Admin shell + filtros URL + ├── RPT-006 Sales dashboard / channel / store / terminal + ├── RPT-007 Product/category/brand reports + └── RPT-008 CSV export + +RPT-003 ──> RPT-011 Payments report +RPT-002 ──> RPT-012 Tax report / RPT-013 margin +POS cash implementation ──> RPT-014 cash report +Refund model ──> RPT-015 refunds report +``` + +Regla: ningún ticket debe inventar datos ausentes; si una métrica no tiene fuente, se incorpora como `unavailable` o se crea primero el ticket de modelo correspondiente. + +--- + +# P0 — Base útil y primera entrega + +## RPT-001 — Reporting contracts, filters and permissions + +**Objetivo:** crear `ReportingFilters`, parser común, rangos relativos, comparación y permisos backend/frontend. + +**Incluye:** + +- intervalos `[from,to)` en UTC + zona `Europe/Madrid` para agrupaciones; +- presets hoy/ayer/7/30 días/mes/año/período anterior; +- `channel`, tiendas, terminales, cajeros, producto, categoría, marca, cliente, estado; +- paginación, orden y límite máximo; +- respuesta común con `range`, `comparison`, `dataAvailability`, `updatedAt`; +- permisos `REPORTING_*`, incluyendo `REPORTING_FINANCIAL` y `REPORTING_EXPORT`; +- tests de fechas invertidas, zona, cero y filtros combinados. + +**No incluye:** SQL de reportes ni UI final. + +## RPT-002 — Reporting data snapshots and store scope + +**Objetivo:** hacer reportables de forma fiable las dimensiones históricas obligatorias. + +**Evaluar/implementar en migraciones:** + +- `orders_orders.store_id` NOT NULL, FK, backfill y escritura desde ecommerce/POS/admin; +- snapshot `vat_rate` y `cost_at_sale_cents` nullable en `orders_items`; +- snapshot de marca/categoría solo si el negocio necesita histórico estable, no copiar el catálogo sin justificación; +- separación de `shipping_cents` de los totales; +- índices medidos para fecha/tienda/canal. + +**Aceptación:** migración reversible/documentada, ventas antiguas conservan valores, margen/IVA siguen `unavailable` si faltan snapshots. + +## RPT-003 — Payment lines and cash-safe payment capture + +**Objetivo:** poder informar efectivo/tarjeta/otros y pagos mixtos sin guardar datos de tarjeta. + +**Incluye:** + +- tabla inmutable `order_payment_lines` o equivalente, con order, método, tienda, terminal, sesión, importe, currency, provider ref, estado y timestamps; +- integración de venta ecommerce y POS con la línea real; +- idempotencia por evento/provider; +- no PAN/CVV; +- relación con reembolsos preparada. + +**Aceptación:** una venta con uno o varios métodos se puede sumar exactamente una vez; pruebas de duplicado y cross-store. + +## RPT-004 — ReportingService, summary and sales API + +**Objetivo:** primera API real de reporting a partir de pedidos/líneas. + +**Endpoints:** `GET /reporting/summary` y `GET /reporting/sales`. + +**Métricas P0:** ventas totales registradas, pedidos/tickets, unidades, ticket medio, descuentos e impuestos registrados; tendencia y comparación cuando haya período anterior. + +**Agrupaciones:** día, semana, mes, canal, tienda y terminal cuando existan. + +**Aceptación:** SQL parametrizado, sin N+1, estados documentados, respuesta de disponibilidad, tests unitarios de fórmulas e integración con ecommerce/POS. + +## RPT-005 — Admin Reporting shell and global filters + +**Objetivo:** añadir `Reporting` al menú Admin y una pantalla `/reporting` reutilizando Auth/API/layout existentes. + +**Componentes:** `ReportingLayout`, `ReportingFilters`, `DateRangePicker`, `ComparisonSelector`, `KpiCard`, `ComparisonKpi`, loading/error/empty/no-data states. + +**Aceptación:** filtros se reflejan en URL, sobreviven navegación válida, tablet usable, no se cargan listas masivas en frontend, backend autoriza cada petición. + +## RPT-006 — Sales dashboard and channel/store views + +**Objetivo:** presentar summary/sales real sin recalcular en React. + +**Incluye:** KPIs, tendencia, ecommerce vs TPV, tiendas, terminales y tabla paginada con drill-down a pedidos. + +**Aceptación:** filtros globales, comparación equivalente, importes EUR en céntimos convertidos solo para presentación, `unavailable` cuando no hay store/terminal histórico. + +## RPT-007 — Product, category and brand reports + +**Objetivo:** ranking de productos y desglose de categorías/marcas usando catálogo real. + +**Incluye:** top 10/25/50, unidades, ventas de mercancía, precio medio, descuento, devoluciones si existen; filtros y orden server-side. + +**Nota:** marcar que categoría/marca es clasificación actual mientras no exista snapshot histórico. + +## RPT-008 — CSV export + +**Objetivo:** exportar summary/sales/products respetando exactamente filtros, rango, orden y columnas. + +**Aceptación:** stream/paginación server-side, BOM/encoding adecuado para Excel, cabecera con fecha/zona/filtros, permission `REPORTING_EXPORT`, límites y auditoría. + +## RPT-009 — Reporting frontend API client and table primitives + +**Objetivo:** adaptar `api-client.ts` con tipos de respuesta y tabla reutilizable sin duplicar fetch/serialización. + +**Incluye:** abort/cancel de consultas, estados de error, columnas configurables y enlaces drill-down. + +## RPT-010 — P0 integration and regression suite + +**Objetivo:** fixtures transaccionales y pruebas end-to-end. + +**Casos:** venta ecommerce, venta POS `COMPLETED`, pedido cancelado, descuento, stock confirmado, comparación de período, filtro tienda no autorizada y export con filtros. + +--- + +# P1 — Informes operativos y comerciales + +## RPT-011 — Payments report + +Depende de RPT-003. Importe, operaciones, porcentaje, medio y devoluciones por método; admite pago mixto y filtra método/tienda/terminal. + +## RPT-012 — Fiscal/IVA report + +Depende del snapshot fiscal de RPT-002. Base imponible, tipo de IVA, cuota y total usando datos de la venta, nunca configuración actual. + +## RPT-013 — Customers report + +Clientes únicos, nuevos, recurrentes, pedidos, ventas, ticket y última compra. Ocultar PII según permiso y separar walk-ins. + +## RPT-014 — Inventory report + +Stock actual por tienda, vendido, stock bajo/sin stock, rotación aproximada y días de stock solo con denominador suficiente. Usa `inventory_stock`, movimientos y pedidos sin duplicar ventas. + +## RPT-015 — Products without movement + +Configurable 30/60/90 días: stock positivo + última venta anterior al umbral. Indicar “sin historial” distinto de “sin movimiento”. + +## RPT-016 — Categories and brands detail + +Tablas con ventas/unidades/pedidos/% y navegación a producto; clasificación histórica documentada. + +## RPT-017 — Discounts report + +Depende de snapshot de promoción. Total, porcentaje, media, código/tipo, producto/categoría/tienda/terminal/cajero y descuentos manuales identificables. + +## RPT-018 — Refund model and report + +Crear primero `order_refunds`/líneas y luego importe, cantidad, motivo, producto, canal, tienda, terminal, usuario y porcentaje sobre ventas. No inferir importe desde estado. + +## RPT-019 — Cash sessions report + +Depende de movimientos de caja/pagos POS. Apertura, ventas efectivo, esperado, contado, diferencia, cajero, terminal, tienda y filtro “solo diferencias”. Proteger con `REPORTING_CASH`/`REPORTING_FINANCIAL`. + +## RPT-020 — Drill-down navigation + +Ventas → canal → tienda → terminal → producto → pedido/ticket, conservando filtros compatibles y evitando IDs sensibles en URLs no autorizadas. + +## RPT-021 — Period comparison UX + +Presets y período equivalente; mostrar valor actual, anterior y porcentaje/null. Tests para períodos con cero y distinta duración. + +## RPT-022 — Server-side reporting tables + +Paginación, búsqueda, sorting, selección de columnas y límites. Nunca cargar decenas de miles de filas en React. + +--- + +# P2 — Análisis avanzado condicionado a datos + +## RPT-023 — Historical cost and gross margin + +Solo tras `cost_at_sale_cents`: revenue, COGS, gross profit y margen por producto/categoría/marca/tienda/canal. No usar el coste actual. + +## RPT-024 — Sales by hour and weekday + +Agrupación en zona local, line/bar chart y tabla de horas punta/valle. Validar volumen mínimo para evitar conclusiones engañosas. + +## RPT-025 — Sales heatmap + +Heatmap día de semana/hora por ventas o tickets, con selector de métrica y accesibilidad alternativa en tabla. + +## RPT-026 — Advanced stock analytics + +Rotación, velocidad de venta, días de stock y productos sin movimiento con ventanas configurables y advertencia de aproximación. + +## RPT-027 — XLSX export + +Exportación server-side con columnas/formato/filtros y límites; conservar auditoría y evitar bloqueo del request. + +## RPT-028 — PDF export + +Solo para informes donde el formato aporte valor (resumen/caja/fiscal); no convertir tablas grandes a PDF sin paginación. + +--- + +# P3 — Evolución futura, no implementar ahora + +- RPT-029 — Forecast de ventas. +- RPT-030 — Forecast y alertas de stock. +- RPT-031 — Alertas de negocio. +- RPT-032 — Informes programados/email. +- RPT-033 — ABC de productos, cohortes y RFM. +- RPT-034 — Integración con BI/API externa. +- RPT-035 — Materialized views/agregaciones cuando volumen y EXPLAIN lo justifiquen. + +## Definition of Done común + +- Contrato/API/documentación actualizados. +- Backend calcula métricas; frontend no duplica reglas financieras. +- Query parametrizada y plan revisado para consultas críticas. +- Permiso backend + prueba negativa cross-store. +- Estados loading/empty/error/no-data/unavailable. +- Unit tests de cálculos y integration tests de datos reales. +- CSV/XLSX respeta filtros si aplica. +- `npm run build`, `npm test`, `./scripts/verify.sh` y gates reviewer/security/qa verdes. diff --git a/work/artifacts/F-142/implementer.md b/work/artifacts/F-142/implementer.md new file mode 100644 index 0000000..41aff79 --- /dev/null +++ b/work/artifacts/F-142/implementer.md @@ -0,0 +1,41 @@ +# F-142 — Implementer evidence + +## Entrega + +Se completó la fase obligatoria de discovery antes de crear componentes o endpoints: + +- `docs/reporting/REPORTING_ARCHITECTURE.md` +- `docs/reporting/REPORTING_TASKS.md` + +También se abrieron los tickets P0 iniciales en el backlog mediante `scripts/new_ticket.py`: + +- F-143 — contracts, filters and RBAC +- F-144 — historical store and financial snapshots +- F-145 — payment lines and POS cash-safe capture +- F-146 — ReportingService, summary and sales API +- F-147 — Admin reporting shell and global filters +- F-148 — sales dashboard and channel views +- F-149 — product/category/brand reports +- F-150 — CSV export + +## Hallazgos principales + +- La fuente única actual es `orders_orders` + `orders_items`, pero las ventas todavía no tienen `store_id` directo. +- TPV ya distingue `source`, `terminal_id` y `cash_session_id`, aunque el flujo POS completo todavía está en tickets POS posteriores. +- Pagos actuales son eventos de proveedor y no contienen método de pago ni líneas para pagos mixtos. +- No existe un modelo de devoluciones detallado ni movimientos de caja inmutables. +- `orders_items` conserva precio/descuento/impuesto, pero no `vat_rate` ni `cost_at_sale`; por tanto IVA por tipo y margen se marcan como no disponibles. +- El total de pedido no separa portes, por lo que la API futura debe distinguir total cobrado de ventas de mercancía. +- Categoría/marca actuales no son snapshots históricos. + +## Scope deliberadamente excluido + +No se implementaron endpoints, tablas reporting paralelas, dashboards, datos mock, cálculos financieros frontend ni caché prematura. La implementación comienza en F-143/F-144/F-145 y respeta el orden de dependencias documentado. + +## Verificación + +```text +./scripts/verify.sh ✅ +backlog válido (264) ✅ +una sola feature activa (F-142) ✅ +``` diff --git a/work/artifacts/F-142/leader-close.json b/work/artifacts/F-142/leader-close.json new file mode 100644 index 0000000..e6d6b0f --- /dev/null +++ b/work/artifacts/F-142/leader-close.json @@ -0,0 +1,20 @@ +{ + "feature_id": "F-142", + "agent": "leader", + "verdict": "APPROVED", + "summary": "F-142 cerrado: arquitectura de Reporting integrada con los modelos actuales, análisis de datos disponibles/faltantes, contrato API conceptual, estrategia de consultas/caché/RBAC y roadmap P0-P3 entregados antes de implementar dashboards.", + "checks": [ + "reviewer.json APPROVED", + "security.json APPROVED", + "qa.json APPROVED", + "docs/reporting/REPORTING_ARCHITECTURE.md exists", + "docs/reporting/REPORTING_TASKS.md exists", + "P0 backlog tickets F-143..F-150 created with scripts/new_ticket.py", + "F-142 only in_progress during work", + "verify.sh exit 0", + "git diff --check exit 0" + ], + "commit_message": "docs(reporting): add architecture and phased task plan", + "next_step": "Start F-143: Reporting contracts, filters and RBAC", + "closed_at": "2026-08-21T20:08:00Z" +} diff --git a/work/artifacts/F-142/qa.json b/work/artifacts/F-142/qa.json new file mode 100644 index 0000000..0ac5d4d --- /dev/null +++ b/work/artifacts/F-142/qa.json @@ -0,0 +1,20 @@ +{ + "feature_id": "F-142", + "agent": "qa", + "stage": "qa_gate", + "verdict": "APPROVED", + "reviewed_at": "2026-08-21T20:07:30Z", + "summary": "La fase de preparación de Reporting está completa y verificable. Los documentos cubren los 12 apartados requeridos, el plan P0-P3 está creado y el backlog contiene los tickets P0 sin activar una segunda feature.", + "acceptance_traceability": [ + {"criterion":"Architecture file generated","ok":true,"evidence":"docs/reporting/REPORTING_ARCHITECTURE.md exists and documents models, relations, metrics, missing data, queries, indexes, API, components, cache, permissions and performance risks"}, + {"criterion":"Task file generated","ok":true,"evidence":"docs/reporting/REPORTING_TASKS.md exists with dependency graph, P0/P1/P2/P3 and Definition of Done"}, + {"criterion":"P0 coverage","ok":true,"evidence":"P0 includes dashboard, filters, sales, channel/store/terminal, products, payments, CSV and RBAC via RPT-001..RPT-010"}, + {"criterion":"P1/P2/P3 coverage","ok":true,"evidence":"Cash, discounts, refunds, categories, customers, inventory, comparisons/drill-down, margins, heatmap, stock advanced, PDF, forecasting, alerts, scheduled reports and BI are classified"}, + {"criterion":"No unjustified metrics","ok":true,"evidence":"Architecture marks margin, payment method, detailed refunds, VAT by rate and cash movement as unavailable until source data exists"}, + {"criterion":"Backlog tickets created through script","ok":true,"evidence":"F-143..F-150 created by scripts/new_ticket.py; all remain pending; F-142 is the only in_progress feature"}, + {"criterion":"Harness verification","ok":true,"evidence":"./scripts/verify.sh → backlog válido (264 features), runtime-status válido, exit 0"} + ], + "checks": [], + "issues": [], + "notes":"No application code was intentionally changed for Reporting. The next implementation ticket is F-143, followed by data prerequisites F-144/F-145 before financial dashboards." +} diff --git a/work/artifacts/F-142/reviewer.json b/work/artifacts/F-142/reviewer.json new file mode 100644 index 0000000..214fe58 --- /dev/null +++ b/work/artifacts/F-142/reviewer.json @@ -0,0 +1,19 @@ +{ + "feature_id": "F-142", + "agent": "reviewer", + "stage": "review_gate", + "verdict": "APPROVED", + "reviewed_at": "2026-08-21T20:06:30Z", + "summary": "La arquitectura de Reporting está documentada antes de implementar UI/API y está basada en las tablas y módulos reales del repositorio. Identifica explícitamente qué métricas existen y qué datos faltan.", + "checks": [ + {"item":"Existing models and relations inventoried","ok":true,"evidence":"REPORTING_ARCHITECTURE.md sections 2-3 cover orders/items, payments, inventory, POS, catalog, prices, promotions, taxes, customers and cash"}, + {"item":"Available vs unavailable metrics separated","ok":true,"evidence":"Section 4 explicitly excludes payment method, detailed refunds, VAT rate, historical margin, shipping split and cash movements until data exists"}, + {"item":"No parallel source of truth proposed","ok":true,"evidence":"Architecture uses orders_orders/orders_items and existing transactional modules; aggregation deferred until measured"}, + {"item":"API/filter/query design present","ok":true,"evidence":"Common filter contract, response envelope, CTE strategy, grouping and pagination documented"}, + {"item":"Performance and cache risks addressed","ok":true,"evidence":"EXPLAIN/ANALYZE-first policy, measured indexes, 30-60s cache proposal, N+1 prohibition and export limits"}, + {"item":"Frontend/RBAC evolution documented","ok":true,"evidence":"Reusable Admin components, URL filters, backend permissions and financial-data scope documented"}, + {"item":"P0-P3 task plan created","ok":true,"evidence":"REPORTING_TASKS.md contains dependency graph, P0, P1, P2 and P3 tasks; F-143..F-150 opened for P0"} + ], + "issues": [], + "notes":"This feature intentionally stops at architecture/tasks. Dashboard implementation must start with F-143 and must not bypass the missing-data prerequisites in F-144/F-145." +} diff --git a/work/artifacts/F-142/security.json b/work/artifacts/F-142/security.json new file mode 100644 index 0000000..6a66512 --- /dev/null +++ b/work/artifacts/F-142/security.json @@ -0,0 +1,19 @@ +{ + "feature_id": "F-142", + "agent": "security", + "stage": "security_gate", + "verdict": "APPROVED", + "reviewed_at": "2026-08-21T20:07:00Z", + "summary": "El diseño incorpora los controles necesarios para reporting: autorización backend, aislamiento de tienda, protección de PII/finanzas, SQL parametrizado y exportaciones limitadas. No se ha añadido código ni almacenamiento de datos.", + "checks": [ + {"item":"PII scope identified","ok":true,"evidence":"Customer report requires permission and architecture limits emails/addresses to authorized reports"}, + {"item":"Financial data scope identified","ok":true,"evidence":"REPORTING_FINANCIAL protects cost/margin, tax detail and cash differences"}, + {"item":"Cross-store isolation planned","ok":true,"evidence":"Store scope is required in SQL and authorization; negative tests included in task Definition of Done"}, + {"item":"SQL injection risk addressed","ok":true,"evidence":"Contract requires parameterized values, bounded filters and rejects invalid IDs; groupBy/order are allowlisted concepts"}, + {"item":"Payment data safety addressed","ok":true,"evidence":"Payment-line task explicitly forbids PAN/CVV and stores only provider references"}, + {"item":"Export abuse addressed","ok":true,"evidence":"REPORTING_EXPORT, server-side limits/streaming and audit metadata are required"}, + {"item":"No security behavior changed in F-142","ok":true,"evidence":"Only docs and pending backlog tickets were added"} + ], + "issues": [], + "notes":"Implementation must treat the architecture as a security contract: frontend visibility is not authorization; each report/export/filter must be checked in backend." +} diff --git a/work/runtime-status.json b/work/runtime-status.json index b7a4846..88d3e11 100644 --- a/work/runtime-status.json +++ b/work/runtime-status.json @@ -1,12 +1,12 @@ { - "feature_id": "F-141", + "feature_id": "F-142", "stage": "close", "agent": "leader", - "action": "Close: Next workspace roots explicit and API-dependent catalog pages runtime rendered", + "action": "Close: Reporting architecture, data-gap analysis and P0-P3 task plan approved", "state": "running", "next_agent": "leader", - "waiting_for": "commit", - "updated_at": "2026-08-21T20:01:53Z", + "waiting_for": "promote_F-143", + "updated_at": "2026-08-21T20:06:45Z", "timeline": [ { "ts": "2026-08-21T19:58:45Z", @@ -56,6 +56,48 @@ "stage": "close", "state": "running", "message": "Close: Next workspace roots explicit and API-dependent catalog pages runtime rendered" + }, + { + "ts": "2026-08-21T20:02:06Z", + "agent": "architect", + "stage": "design", + "state": "running", + "message": "Design: inspect current transactional models and produce reporting architecture plus phased tasks before code" + }, + { + "ts": "2026-08-21T20:05:44Z", + "agent": "implementer", + "stage": "build", + "state": "running", + "message": "Build: persist reporting architecture/task artifacts and open P0 backlog tickets; no dashboard code" + }, + { + "ts": "2026-08-21T20:05:59Z", + "agent": "reviewer", + "stage": "review_gate", + "state": "running", + "message": "Review: reporting architecture grounded in existing models and phased tasks before implementation" + }, + { + "ts": "2026-08-21T20:06:14Z", + "agent": "security", + "stage": "security_gate", + "state": "running", + "message": "Security: reporting data scope, PII/financial permissions, SQL and export risks reviewed" + }, + { + "ts": "2026-08-21T20:06:27Z", + "agent": "qa", + "stage": "qa_gate", + "state": "running", + "message": "QA: documentation completeness, backlog dependencies, verify harness and no implementation scope leak" + }, + { + "ts": "2026-08-21T20:06:45Z", + "agent": "leader", + "stage": "close", + "state": "running", + "message": "Close: Reporting architecture, data-gap analysis and P0-P3 task plan approved" } ] }