From acdef6df70bbc5645d74ef426d414f46c0419cd1 Mon Sep 17 00:00:00 2001 From: razyd Date: Sun, 11 Oct 2026 12:04:05 +0200 Subject: [PATCH 01/11] docs(todo): close the entries fixed on 2026-10-11 and add the Aiven step - Remove: maintenance change and history without a transaction, incoherent group payments, manual notifications to inactive users. - Replace "deleting a group deletes its history" with what is left: group mutations and their history outside one transaction. - Add, high priority: apply 20261011_keep_group_history_on_delete.sql in Aiven before deploying the group deletion. - Add: the groups history logs fields that did not change. Why: these four entries sit next to each other, so editing them from four branches made every pull request conflict. They are fixed in opencode/fix-maintenance-history, fix-group-payments, fix-notification-recipients and fix-group-deletion; merge this one last. --- docs/TODO.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 473ba85..77c13d3 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -30,6 +30,13 @@ preview. ### 🔴 Alta +- [ ] **Aplicar en Aiven `20261011_keep_group_history_on_delete.sql` antes de + desplegar el borrado de grupos** — el código escribe una fila de + historial tras borrar el grupo; con la clave foránea todavía en Aiven + ese insert falla y el borrado se deshace (500). Preview y producción + usan Aiven: aplicarla antes de que la rama llegue a compassionate, con + `scripts/apply-migration.sh aiven --dry-run` primero, y + marcarla en `backend/db-mysql/INDEX.md`. Ya aplicada en local. - [ ] **Rotar las credenciales de producción** — contraseña de la BD y claves de Cloudinary, y revocar las claves de servicios que ningún código usa. El historial publicado no las contiene, pero siguen en copias privadas @@ -208,20 +215,15 @@ preview. tipo interno o externo encaje con el resto de campos. _Según la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado por mí._ -- [ ] **Maintenance: cambio e historial sin transacción** — un fallo entre ambos - deja cambios sin registrar. _Comprobado por mí el 2026-09-28._ -- [ ] **Groups: lógica de pagos incoherente** — se acepta un `amount_paid` mayor - que el importe, `paid` sin haber pagado nada, recálculos que no tocan el - estado y porcentajes que suman más de 100. _Según la revisión `security` - L3 del 2026-09-28 (fichero y línea en el informe); no repasado por mí._ -- [ ] **Groups: borrar un grupo borra su historial** — no se registra el borrado - y `group_history` tiene `ON DELETE CASCADE`; ninguna mutación va en - transacción con su registro. _Comprobado por mí el 2026-09-28._ - (transacciones, según el revisor) -- [ ] **Notificaciones manuales: se pueden enviar a usuarios inactivos** — el - formato, los repetidos, la prioridad y los textos ya se validan; que el - usuario exista y esté activo, no. _Según la revisión `security` L3 del - 2026-09-28 (fichero y línea en el informe); no repasado por mí._ +- [ ] **Groups: las mutaciones no van en transacción con su historial** — el + historial se escribe en el controlador, fuera de las transacciones del + repositorio: un fallo entre ambos deja cambios sin registrar. El borrado + del grupo ya va junto a su fila de historial (2026-10-11). _Comprobado + el 2026-10-11._ +- [ ] **Groups: el historial registra campos que no cambiaron** — editar solo + el importe de un grupo dejó 7 filas `updated` (`arrival_date`, + `created_at`, `updated_by`…). La comparación de `logGroupUpdated` trata + como distintos valores iguales (fechas, nulos). _Visto el 2026-10-11._ - [ ] **Scheduling: generar un mes deja vacías las celdas de días fijos y solicitudes aprobadas** — el solver las recibe como bloqueadas, la generación no las reinserta y el borrado previo se las lleva. En Aiven, en From 8496d9fa8119cf1d071d28af3d3869b8846bff8b Mon Sep 17 00:00:00 2001 From: razyd Date: Sun, 11 Oct 2026 12:22:30 +0200 Subject: [PATCH 02/11] docs(todo): close the four scheduling entries reviewed on 2026-10-11 - Remove: generation leaving fixed-day and approved-request cells empty, a published month that could be changed, setSchedulableEmployees losing dates, and the non-deterministic solver. Why: the first three are fixed in opencode/fix-scheduling; the fourth was decided (left as it is) and recorded in docs/scheduling/decisions.md. --- docs/TODO.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 77c13d3..25d1e7e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -224,22 +224,6 @@ preview. el importe de un grupo dejó 7 filas `updated` (`arrival_date`, `created_at`, `updated_by`…). La comparación de `logGroupUpdated` trata como distintos valores iguales (fechas, nulos). _Visto el 2026-10-11._ -- [ ] **Scheduling: generar un mes deja vacías las celdas de días fijos y - solicitudes aprobadas** — el solver las recibe como bloqueadas, la - generación no las reinserta y el borrado previo se las lleva. En Aiven, en - los meses 126 y 130 (generados por el solver) el empleado con días fijos - tiene 0 celdas. _Comprobado por mí el 2026-09-28._ -- [ ] **Scheduling: un mes publicado se puede modificar** — editar celdas y - aprobar restricciones no miran el estado del mes, y `PUT /months/:id` - vuelve a borrador sin pasar por `unpublish`. _Según la revisión `security` - L3 del 2026-09-28 (fichero y línea en el informe); no repasado por mí._ -- [ ] **Scheduling: `setSchedulableEmployees` pierde las fechas de alta y baja** - — borra la tabla entera y la vuelve a llenar. _Comprobado por mí el - 2026-09-28._ -- [ ] **Scheduling: el solver no es determinista** — no recibe semilla y CP-SAT - busca en paralelo: dos ejecuciones del mismo mes pueden dar horarios - distintos. Decidir si se fija semilla y un solo hilo. _Comprobado por mí - el 2026-09-28._ - [ ] **Checklist: el reinicio manual no funciona** — `resetRun` cierra el run y crea otro del mismo día, pero la clave única `uk_run (hotel_id, checklist_id, hotel_date)` lo impide: 500. Después From 22a8ec756c6979a96f714cd6459036ece4e3d5c7 Mon Sep 17 00:00:00 2001 From: razyd Date: Sun, 11 Oct 2026 12:38:17 +0200 Subject: [PATCH 03/11] docs(todo): close the checklist and cashier entries reviewed on 2026-10-11 - Remove: deleting comments and photos of closed checklist days; cashier history and totals without canViewReports. - Narrow "voucher lifecycle and closures without a lock" to what is left: shift and day closures without a lock. - Checklist manual reset: note the decision to leave the unused endpoint as it is and what to fix before using it. Why: fixed in opencode/fix-checklist and opencode/fix-cashier; the reset was decided on 2026-10-11. --- docs/TODO.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 25d1e7e..a7a9fd1 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -229,20 +229,14 @@ preview. `uk_run (hotel_id, checklist_id, hotel_date)` lo impide: 500. Después `getOrCreateRun` devuelve el run cerrado y los cambios del día se escriben en él. Sin transacción ni test. En Aiven nadie lo ha usado (0 cierres - manuales). _Comprobado por mí el 2026-09-28._ -- [ ] **Checklist: borrar comentarios e imágenes de días cerrados** — el autor - puede reescribir el histórico, y el evento se registra en el run de hoy y - como si fuera una alta. _Según la revisión `security` L3 del 2026-09-28 - (fichero y línea en el informe); no repasado por mí._ -- [ ] **Cashier: ciclo de vida de los vales y cierres sin bloqueo** — se puede - justificar un vale cancelado y cambiar el importe de uno justificado; el - límite de 5 vales y los cierres de turno y día se comprueban antes de - escribir, sin bloqueo. _Según la revisión `security` L3 del 2026-09-28 - (fichero y línea en el informe); no repasado por mí._ -- [ ] **Cashier: el historial y los totales se ven sin `canViewReports`** — - `GET /shifts/:id/history`, `/daily` y `/vouchers/stats`. _Según la - revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no - repasado por mí._ + manuales) y la pantalla no lo llama: decidido el 2026-10-11 dejar el + endpoint como está; antes de usarlo, cambiar la clave única. _Comprobado + el 2026-10-11._ +- [ ] **Cashier: los cierres de turno y de día no bloquean** — se comprueban + antes de escribir, sin bloqueo: un pago o un vale puede entrar en un turno + que otro usuario está cerrando. El ciclo de los vales y su límite de 5 ya + van en transacción (2026-10-11). _Según la revisión `security` L3 del + 2026-09-28; no repasado._ - [ ] **Backoffice: borrar una factura borra también su historial** — borrado definitivo, con las columnas de borrado lógico sin usar. _Comprobado por mí el 2026-09-28._ From 200cae2c38f78e3c95b722435ebbb8fb513e9010 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 00:09:06 +0200 Subject: [PATCH 04/11] docs(todo): close the backoffice and conciliation entries reviewed on 2026-10-11 - Remove: deleting an invoice also deletes its history (kept permanent by decision); integrity of the validated PDF (kept by decision); no checks on amounts and dates; uploads and downloads without content or size checks. - Move the monthly close of Conciliation to low priority and rewrite it: it has no screen and a closed month does not lock its days; left as is. - docs/conciliation: say the monthly close exists only in the API and does not lock the days. Why: fixed in opencode/fix-backoffice-files and opencode/fix-backoffice-invoices; the rest was decided on 2026-10-11, and both decisions are recorded in docs/backoffice/README.md. --- docs/TODO.md | 28 ++++++++-------------------- docs/conciliation/README.md | 6 +++++- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index a7a9fd1..3ec922b 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -237,26 +237,6 @@ preview. que otro usuario está cerrando. El ciclo de los vales y su límite de 5 ya van en transacción (2026-10-11). _Según la revisión `security` L3 del 2026-09-28; no repasado._ -- [ ] **Backoffice: borrar una factura borra también su historial** — borrado - definitivo, con las columnas de borrado lógico sin usar. _Comprobado por - mí el 2026-09-28._ -- [ ] **Backoffice: integridad del PDF validado** — se puede validar sin PDF o - con cualquier PDF, sin enlace entre el original y la copia sellada. _Según - la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); - no repasado por mí._ -- [ ] **Backoffice: sin validación de importes ni fechas** — importes negativos, - facturas duplicadas (sin UNIQUE por proveedor y número) y `paid_date` - libre. _Según la revisión `security` L3 del 2026-09-28 (fichero y línea en - el informe); no repasado por mí._ -- [ ] **Backoffice: subidas y descargas sin comprobar contenido ni tamaño** — - solo el tipo declarado por el navegador; las descargas del ZIP no tienen - límite de tamaño y siguen redirecciones. _Según la revisión `security` L3 - del 2026-09-28 (fichero y línea en el informe); no repasado por mí._ -- [ ] **Conciliation: el cierre mensual tiene una carrera** — la validación - (lectura) y el cambio de estado (escritura) no comparten transacción ni - bloqueo; dos administradores cerrando días distintos del mismo mes a la - vez pueden dejarlo inconsistente. _Según la revisión `security` L3 del - 2026-09-28 (fichero y línea en el informe); no repasado por mí._ ### 🟢 Baja @@ -296,6 +276,14 @@ preview. pestaña muestra las rechazadas. Una factura incorrecta hoy se borra. El dueño decidió el 2026-10-04 no añadirlo porque casi nunca pasa; si se añade, hace falta también reabrir (`rejected` → `pending`). +- [ ] **Conciliation: el cierre mensual no tiene pantalla ni congela los + días** — `PATCH /monthly-summary/:year/:month/status` solo existe en la + API (`useUpdateMonthlySummaryStatus` no lo usa ningún componente), y con + el mes cerrado un admin puede reabrir o borrar cualquiera de sus días. La + comprobación de que todos los días están cerrados va sin bloqueo. El + dueño decidió el 2026-10-11 dejarlo así; si se le da pantalla, cerrar el + mes debe bloquear sus días y comprobar con el mes bloqueado. _Comprobado + el 2026-10-11._ - [ ] **Conciliation: `GET /api/conciliations` sin paginar ni filtrar** — devuelve todo el histórico a cualquier rol con acceso. _Según la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado diff --git a/docs/conciliation/README.md b/docs/conciliation/README.md index c94d536..83a651e 100644 --- a/docs/conciliation/README.md +++ b/docs/conciliation/README.md @@ -38,6 +38,8 @@ reconciliation or closes a month. - **Monthly summary**: totals for the month, which days are missing, and whether the month can be closed. - **Close the month**, only once every day of the month exists and is closed. + It exists only in the API (`PATCH /monthly-summary/:year/:month/status`): no + screen calls it. ## What data it handles @@ -63,7 +65,9 @@ Four tables (`backend/db-mysql/aiven/09_conciliation.sql`): - **A closed day cannot be edited** — not its adjustments, not its totals — except by an `admin`. `confirmed` is not a locking state. - **A month closes only when every one of its days exists and is closed.** - Missing or still-open days block it and are listed by day number. + Missing or still-open days block it and are listed by day number. A closed + month does not lock its days: an `admin` can still reopen or delete them + (left as is on 2026-10-11, see `docs/TODO.md`). - **Deleting a day's reconciliation is only for `admin`.** ## How information flows From 2ceb14752d6119e6578278bbdfaeebba9145319f Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 00:24:35 +0200 Subject: [PATCH 05/11] docs(todo): widen the Aiven UTC entry to the timezone handling of dates - Rewrite "MySQL de Aiven en UTC" as the whole problem: DATE columns reach the client as a timestamp at Node's midnight, DATETIMEs shift when Node and MySQL run in different zones, and "today" is computed in UTC in the browser, the backend and Aiven. - List where it shows up and the plan: DATE as YYYY-MM-DD strings, today in Europe/Madrid, a fixed zone between Node and MySQL. Why: editing an invoice in local moved its dates back one day; measured on 2026-10-12 against the local database with Node in Madrid and in UTC. The owner decided to handle it as its own task. --- docs/TODO.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 3ec922b..d65f24e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -139,11 +139,33 @@ preview. los `CLAUDE.md` de módulo se borraron en vez de quedar como punteros; si Claude Code solo lee `CLAUDE.md` por directorio, ese contexto se pierde. _Sin comprobar._ -- [ ] **MySQL de Aiven en UTC** — `time_zone=SYSTEM` con el sistema en UTC. - `CURDATE()` y `NOW()` de la BD (filtros rápidos de parking, código - `PK-AAAAMMDD` del trigger, `CURRENT_TIMESTAMP` de las tablas) van 1–2 h - por detrás de Madrid: entre las 00:00 y las 02:00 «hoy» sigue siendo ayer. - _Comprobado el 2026-09-28._ +- [ ] **Fechas y «hoy» dependen de la zona horaria de cada pieza** — navegador + en Madrid, Node en la del proceso (Madrid en local; Render sin comprobar, + UTC si no tiene `TZ`) y MySQL en la suya (Madrid en local; Aiven en UTC, + `time_zone=SYSTEM`, comprobado el 2026-09-28). No son relojes + desincronizados: + - El pool (`config/db.ts`) no fija `timezone` ni `dateStrings`: una + columna `DATE` llega como instante a medianoche de Node. Con Node en + Madrid, el 30/09 viaja como `2026-09-29T22:00:00Z`, y quien lo corta por + la `T` se queda con el 29. Así, en local, cada edición de una factura le + resta un día a sus fechas (`InvoiceFormModal.tsx:83-87`). Con Node en + UTC sale bien. + - Si Node y MySQL no están en la misma zona, los `DATETIME` se desplazan: + con Node en UTC contra la MySQL local, `NOW()` sale 2 h movido (p. ej. + `pnpm dev:aiven` desde un equipo en Madrid). + - «Hoy» en UTC está mal entre las 00:00 y las 02:00 de Madrid: + `toISOString()` en 26 sitios del frontend y 6 del backend, y + `CURDATE()`/`NOW()` en Aiven (19 consultas con `CURDATE()`: filtros de + parking, código `PK-AAAAMMDD` del trigger, `CURRENT_TIMESTAMP`). + - 15 sitios del frontend cortan por la `T` fechas del servidor (grupos, + lista negra, parking, horarios, checklist, caja, backoffice). + + Plan: `DATE` como texto `AAAA-MM-DD` (`dateStrings`), «hoy» siempre en + Madrid (un helper en el frontend, `getTodayMadrid` en el backend) y zona + fija entre Node y MySQL. Antes: mirar si Render tiene `TZ` y si alguna + fecha de Aiven se ha movido ya por ediciones. Tarea propia, decidida el + 2026-10-12. _Medido el 2026-10-12 contra la BD local con Node en Madrid y + en UTC._ - [ ] **Logbook: papelera e historial solo ocultos en la pantalla** — el backend da `/trashed`, `include_trashed`, el historial y los comentarios de entradas borradas a cualquier rol con acceso. _Según la revisión From 8093de3d5dd1b2c01ea39159f4cb49206705f82a Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 00:38:44 +0200 Subject: [PATCH 06/11] docs(todo): close the low-priority entries reviewed in A5.1 - Remove: conciliation list without paging and checklist_config unused (both left as is by decision, now noted in their module docs); month reset without a transaction and day ids of another month in bulk edits (fixed in opencode/fix-scheduling); LI as rest in the solver (kept as is, recorded in docs/scheduling/decisions.md). - Keep: rejecting invoices has no screen (decided on 2026-10-04). Why: reviewed against the code on 2026-10-12; the owner decided the three entries that needed a decision. --- docs/TODO.md | 15 --------------- docs/checklist/README.md | 3 ++- docs/conciliation/README.md | 2 ++ 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index d65f24e..16159b1 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -306,21 +306,6 @@ preview. dueño decidió el 2026-10-11 dejarlo así; si se le da pantalla, cerrar el mes debe bloquear sus días y comprobar con el mes bloqueado. _Comprobado el 2026-10-11._ -- [ ] **Conciliation: `GET /api/conciliations` sin paginar ni filtrar** — - devuelve todo el histórico a cualquier rol con acceso. _Según la revisión - `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado - por mí._ -- [ ] **Checklist: `checklist_config` existe pero no se usa.** _Comprobado por - mí el 2026-09-28._ -- [ ] **Scheduling: reiniciar un mes no usa transacción** — si falla a mitad, el - mes queda vacío. _Comprobado por mí el 2026-09-28._ -- [ ] **Scheduling: comprobar si `LI` debe contar como descanso en el solver** — - falta en `REST_SHIFTS` de `rest.py`. _Sin comprobar su efecto._ -- [ ] **Scheduling: `day_id` de otro mes aceptado** y comprobación de bloqueo - fuera de transacción en las ediciones masivas. El tamaño de las listas y - los ids de empleado ya se validan (2026-10-02). _Según la revisión - `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado - por mí._ - [ ] **Scheduling: el daemon del solver puede quedar bloqueado** — el semáforo se retiene durante el arranque (hasta 30 min) con cola sin límite. _Según la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); diff --git a/docs/checklist/README.md b/docs/checklist/README.md index 6300c9f..139b855 100644 --- a/docs/checklist/README.md +++ b/docs/checklist/README.md @@ -61,7 +61,8 @@ from `backend/`. - **`checklist_step_comments`** and **`checklist_step_attachments`**: comments and images per step. - **`checklist_event_log`**: every tick, untick and restart. -- **`checklist_config`**: exists but is not used. +- **`checklist_config`**: exists but is not used: the 06:30 reset and the + `Europe/Madrid` zone are written in the code. Left as is on 2026-10-12. ## What rules it follows diff --git a/docs/conciliation/README.md b/docs/conciliation/README.md index 83a651e..b704311 100644 --- a/docs/conciliation/README.md +++ b/docs/conciliation/README.md @@ -69,6 +69,8 @@ Four tables (`backend/db-mysql/aiven/09_conciliation.sql`): month does not lock its days: an `admin` can still reopen or delete them (left as is on 2026-10-11, see `docs/TODO.md`). - **Deleting a day's reconciliation is only for `admin`.** +- **`GET /api/conciliations` returns every day, without paging or filters**, + to any role with access. No screen uses it; left as is on 2026-10-12. ## How information flows From 8f5ad0f30ebdfaa57fb7968ab5119bd12e585165 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 00:56:59 +0200 Subject: [PATCH 07/11] docs(todo): close the low-priority entries reviewed in A5.2 - Remove: the Excel importer (deleted on 2026-10-04); the group page not refreshing and contacts visible to every role (fixed in opencode/fix-group-detail); the solver daemon queue and the repeated group reminders (kept as they are by decision, recorded in docs/scheduling/decisions.md and docs/groups/README.md). Why: reviewed against the code on 2026-10-12; the owner decided the two entries that needed a decision. --- docs/TODO.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 16159b1..3ad0db1 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -306,28 +306,6 @@ preview. dueño decidió el 2026-10-11 dejarlo así; si se le da pantalla, cerrar el mes debe bloquear sus días y comprobar con el mes bloqueado. _Comprobado el 2026-10-11._ -- [ ] **Scheduling: el daemon del solver puede quedar bloqueado** — el semáforo - se retiene durante el arranque (hasta 30 min) con cola sin límite. _Según - la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); - no repasado por mí._ -- [ ] **Importador del Excel** — borra también las celdas bloqueadas, sin - transacción ni comprobar si el mes está publicado. _Según la revisión - `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado - por mí._ -- [ ] **Groups: recordatorios repetidos cada 48 h** — los de pago se buscan en - un rango de días y se deduplican por título en una ventana de 48 h: el - mismo pago se vuelve a avisar con «vence en 15 días» aunque falten 2, y - los vencidos se repiten sin fin. _Según la revisión `security` L3 del - 2026-09-28 (fichero y línea en el informe); no repasado por mí._ -- [ ] **Groups: la ficha no se refresca tras editar el grupo** — después de - «Grupo actualizado correctamente» el balance sigue mostrando el importe - anterior (y el panel de pagos calcula los porcentajes sobre él) hasta que - otra acción recarga los datos; el backend sí guarda el valor nuevo. - _Comprobado en producción el 2026-09-29._ -- [ ] **Groups: contactos (email y teléfono) visibles para todos los roles**, - también `mantenimiento` (ASVS L3: solo los datos mínimos). _Según la - revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no - repasado por mí._ - [ ] **Maintenance: identificador `DDMMAA-NNN` frágil** — se calcula leyendo el último del día (dos altas a la vez chocan y dan 500) con la hora del servidor; a partir del parte 1000 del día el id no cumple el formato y el From 39762abed12e96dfc152f78386e77cb6512b9183 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 01:24:02 +0200 Subject: [PATCH 08/11] docs(todo): close the low-priority entries reviewed in A5.3 - Delete the two Maintenance races, sensitive data in the logs and the authorization log, fixed in fix-maintenance-history and fix-logging. - Delete the unused parking procedures and the Logbook history entries, left as they are, and record both decisions in the module docs. - Add the Maintenance identifier to the timezone entry: it takes the date of Node. The A5.3 review checked each entry against the code and the owner decided the ones that were not bugs. --- docs/TODO.md | 30 +++--------------------------- docs/logbook/README.md | 4 ++++ docs/parking/README.md | 7 +++++++ 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 3ad0db1..39dcc24 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -154,9 +154,10 @@ preview. con Node en UTC contra la MySQL local, `NOW()` sale 2 h movido (p. ej. `pnpm dev:aiven` desde un equipo en Madrid). - «Hoy» en UTC está mal entre las 00:00 y las 02:00 de Madrid: - `toISOString()` en 26 sitios del frontend y 6 del backend, y + `toISOString()` en 26 sitios del frontend y 6 del backend, `CURDATE()`/`NOW()` en Aiven (19 consultas con `CURDATE()`: filtros de - parking, código `PK-AAAAMMDD` del trigger, `CURRENT_TIMESTAMP`). + parking, código `PK-AAAAMMDD` del trigger, `CURRENT_TIMESTAMP`) y el + identificador `DDMMAA-NNN` de Maintenance (fecha de Node). - 15 sitios del frontend cortan por la `T` fechas del servidor (grupos, lista negra, parking, horarios, checklist, caja, backoffice). @@ -306,33 +307,8 @@ preview. dueño decidió el 2026-10-11 dejarlo así; si se le da pantalla, cerrar el mes debe bloquear sus días y comprobar con el mes bloqueado. _Comprobado el 2026-10-11._ -- [ ] **Maintenance: identificador `DDMMAA-NNN` frágil** — se calcula leyendo el - último del día (dos altas a la vez chocan y dan 500) con la hora del - servidor; a partir del parte 1000 del día el id no cumple el formato y el - parte queda inaccesible. _Comprobado por mí el 2026-09-28._ (el límite de - 1000, según el revisor) -- [ ] **Maintenance: el límite de 5 fotos se salta subiendo en paralelo** — - se cuenta antes de subir, sin bloqueo. _Según la revisión `security` L3 - del 2026-09-28 (fichero y línea en el informe); no repasado por mí._ - [ ] **Maintenance: comprobar si Cloudinary quita los metadatos EXIF/GPS** de las fotos. _Sin comprobar._ -- [ ] **Procedimientos de BD sin uso** — de las 2 funciones y 5 procedimientos - de parking, el código solo llama a `check_availability`; el resto - (incluido `generate_availability`, que amplía el calendario) no los usa - nadie. _Comprobado por mí el 2026-09-28._ -- [ ] **Datos sensibles en los logs** — usernames de logins fallidos, `redact` - de un solo nivel y, probablemente, `err.sql` de mysql2 con los valores - (textos, matrículas, hashes). _Según la revisión `security` L3 del - 2026-09-28 (fichero y línea en el informe); no repasado por mí._ No - verificado en ejecución. -- [ ] **Logbook: el historial registra cada comentario dos veces y cada - lectura sin límite.** (Editar o volver a borrar un comentario borrado ya - no es posible desde el 2026-10-02.) _Según la revisión `security` L3 del - 2026-09-28; no repasado por mí._ -- [ ] **Registrar decisiones de autorización** — ni los 403 ni los accesos de un - `admin` a conversaciones ajenas quedan en el log (ASVS L3). _Según la - revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no - repasado por mí._ - [ ] **Parking: salto de precio a partir de 31 días** — las tarifas llegan a 30 días (250 € en Aiven); a partir de 31 se cobran 15 €/día (465 €). - [ ] **Parking: el mapa de estado no se refresca solo** — los cambios de otro diff --git a/docs/logbook/README.md b/docs/logbook/README.md index 53f06f0..9339245 100644 --- a/docs/logbook/README.md +++ b/docs/logbook/README.md @@ -58,6 +58,10 @@ Departments are the application's general ones (table `departments`). entries and comments, and also reading, unmarking, resolving and reopening. Each change and its history rows are written in one transaction: either both are saved or neither is. +- **A comment leaves two history rows** (one of the comment, one of the entry), + and **each read or unmark adds one**, with no limit; all of them show in the + entry's history in Reports and in the recent activity. Left as is (decided + on 2026-10-12). - **Nothing is truly deleted**: entries and comments are only marked with `deleted_at`. There is no way to restore them from the interface. - **Only the author edits or deletes** their entry or comment. diff --git a/docs/parking/README.md b/docs/parking/README.md index 51c505e..5a5b867 100644 --- a/docs/parking/README.md +++ b/docs/parking/README.md @@ -58,6 +58,13 @@ Part of the logic lives **in the database**: the triggers in `05_parking_functions_triggers.sql` generate the booking code and keep the calendar up to date. +The database also has 2 functions (`check_availability`, +`get_total_availability`) and 5 procedures (`06_parking_procedures.sql`) that +**nothing calls**, neither the code nor the triggers; only the local seeds run +`generate_availability`, which the backend replaced with `extendAvailability`. +`daily_parking_maintenance` would mark expired bookings as no-shows if someone +ran it by hand. They stay in the database (decided on 2026-10-12). + ## What rules it follows **Booking states** From e7436cd8312c8211599f57448adea7bc8e0abd1e Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 08:45:29 +0200 Subject: [PATCH 09/11] docs(todo): close the low-priority entries reviewed in A5.4 - Delete the home panel entry, fixed in fix-home-logbooks. - Delete the parking status map, the Logbook trash and filters, and the messaging features without a screen, left as they are, and record the decisions in the parking, Logbook and messaging docs. - Rewrite the frontend tests entry: the tests exist in backend/tests; what is missing is a runner of its own. The A5.4 review checked each entry against the code and the owner decided the ones that were not bugs. --- docs/TODO.md | 19 +++---------------- docs/general/messages/README.md | 3 ++- docs/logbook/README.md | 5 +++++ docs/parking/README.md | 1 + 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 39dcc24..1553381 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -311,21 +311,6 @@ preview. las fotos. _Sin comprobar._ - [ ] **Parking: salto de precio a partir de 31 días** — las tarifas llegan a 30 días (250 € en Aiven); a partir de 31 se cobran 15 €/día (465 €). -- [ ] **Parking: el mapa de estado no se refresca solo** — los cambios de otro - usuario no aparecen hasta recargar. -- [ ] **Logbook: papelera y filtros sin interfaz** — `/trashed`, `/author`, - `/department` y `/priority` existen en el backend y en `queries.ts`, pero - la pantalla del módulo no los usa (Informes usa `/all` con filtros). - Tampoco hay forma de restaurar lo borrado. -- [ ] **Panel de inicio: una petición por día** — con el periodo _mes_ lanza - hasta 31 peticiones a `/api/logbooks/day/` - (`frontend/app/dashboard/page.tsx`); `/all?date_from=&date_to=` lo haría - en una. -- [ ] **Mensajería: exponer en la interfaz lo que el backend ya ofrece** — - renombrar grupo, añadir y quitar participantes, buscar en mensajes, - contador global de no leídos y vista de todas las conversaciones para - `admin`. Las funciones existen en `frontend/app/lib/messaging/queries.ts` - pero nadie las llama. _Comprobado el 2026-09-28 con búsqueda de usos._ - [ ] **Mensajería sin tiempo real** — los mensajes nuevos solo aparecen al reabrir la conversación. Valorar un sondeo periódico como el de las notificaciones. _Comprobado el 2026-09-28: no hay intervalo ni WebSocket._ @@ -334,7 +319,9 @@ preview. - [ ] **Presencias a mano** — la pestaña de presencias de `scheduling` pide pegar el texto en un `textarea`; podría leerse de `scheduling_assignments`. -- [ ] **Tests en el frontend** — hoy no hay ninguno. +- [ ] **Ejecutor de tests en el frontend** — no tiene: sus tests viven en + `backend/tests/` (10 ficheros: 7 importan funciones del frontend y 3 leen + su código) y no cubren componentes ni hooks. _Contado el 2026-10-12._ - [ ] **Scheduling: cerrar la fase 3 del solver con uso real** — bloqueado hasta que se generen meses de verdad. (1) Con ≥30 generaciones en `scheduling_solver_runs`, buscar patrones (estado, penalización, desglose) diff --git a/docs/general/messages/README.md b/docs/general/messages/README.md index 323d2cf..33d9fa3 100644 --- a/docs/general/messages/README.md +++ b/docs/general/messages/README.md @@ -41,7 +41,8 @@ conversation, and list them all. **The backend offers, but the interface does not use yet**: renaming a group, adding or removing participants after creation, searching in message text, the -global unread counter and the view of all conversations for `admin`. +global unread counter and the view of all conversations for `admin`. They are +not planned (decided on 2026-10-12). ## What data it handles diff --git a/docs/logbook/README.md b/docs/logbook/README.md index 9339245..5b05f95 100644 --- a/docs/logbook/README.md +++ b/docs/logbook/README.md @@ -37,6 +37,11 @@ All roles **except `mantenimiento`**, which does not have access to the module. - **Edit and delete** own entries. Deleting does not remove: the entry goes to the trash. +**The backend offers, but the module screen does not use**: the trash +(`/trashed`) and the lists by author, department and priority (Reports uses +`/all` with its own filters, deleted ones included). Nothing restores a deleted +entry, not even the backend. Not planned (decided on 2026-10-12). + ## What data it handles Four tables (`backend/db-mysql/aiven/03_logbook_tables.sql`): diff --git a/docs/parking/README.md b/docs/parking/README.md index 5a5b867..4caa698 100644 --- a/docs/parking/README.md +++ b/docs/parking/README.md @@ -133,6 +133,7 @@ BookingsListClient / BookingDetailClient ───────────── 2. The status map requests spaces, bookings, expired ones and statistics with React Query. **It does not refresh on its own**: each action requests the data again, but changes made by another person do not appear until reloading. + Left as is (decided on 2026-10-12). 3. Creating, moving, closing or cancelling a booking writes to `parking_bookings` within a transaction; the triggers update the calendar in the same operation. From bbdbd3db075c9d19416994e9ed8dc167f112c8c5 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 09:08:24 +0200 Subject: [PATCH 10/11] docs(todo): add the leaked user password to the rotation and keep the Aiven CA - TODO: the credential rotation now covers the plaintext password of an application user found in the published history (test-login.js, 8595ec0). - database README: record that the Aiven CA certificate stays in the repository. Why: the privacy review of the full history (A7.1-A8.2) found that password, which the 2026-10-05 history rewrite missed; the owner chose to rotate it instead of rewriting history again, and to keep the public CA certificate the TLS connection needs. --- docs/TODO.md | 6 +++++- docs/general/database/README.md | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 1553381..99244ec 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -41,7 +41,11 @@ preview. de Cloudinary, y revocar las claves de servicios que ningún código usa. El historial publicado no las contiene, pero siguen en copias privadas (ADR-007). El procedimiento está fuera del repositorio. - `SECRET_JWT_KEY` ya se cambió el 2026-09-29. + `SECRET_JWT_KEY` ya se cambió el 2026-09-29. Además, el historial + publicado sí trae la contraseña en claro de un usuario de la aplicación + (`test-login.js`, commit `8595ec0`, con su hash): cambiarla en toda + cuenta que la use, en Aiven y fuera. Decisión del dueño (2026-10-12): + se rota y el historial no se reescribe. - [ ] **Fnb: ninguna mutación de ingresos deja rastro de quién la hizo** — `upsertMany` sobrescribe el importe anterior (`ON DUPLICATE KEY UPDATE`) y `deleteDay` borra filas sin dejar ningún registro de usuario, valor diff --git a/docs/general/database/README.md b/docs/general/database/README.md index e566e8c..03ade29 100644 --- a/docs/general/database/README.md +++ b/docs/general/database/README.md @@ -70,7 +70,10 @@ Tables by module: `NOW()` or `CURDATE()` runs 1–2 hours behind Madrid; the backend calculates "today" in Madrid time on its own. - **Encrypted connection**: TLS with the Aiven certificate in - `backend/config/certs/`. + `backend/config/certs/`. It is the project's public CA certificate (no + private key) and names the Aiven project by its id; it stays in the + repository because the backend and `apply-migration.sh` need it (decided on + 2026-10-12). **Migration status**: the 15 recorded in `INDEX.md` are applied on Aiven (verified on 2026-09-28). From 880bc2176f09536a5900ebaf593dc99e2e0594a5 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 09:34:25 +0200 Subject: [PATCH 11/11] docs(todo): close the Aiven migration entry and update the migration count - TODO: remove the high-priority entry to apply 20261011_keep_group_history_on_delete.sql on Aiven. - database README: 26 migrations recorded and applied on Aiven, with the two applied on 2026-10-12. Why: both migrations were applied on Aiven on 2026-10-12 after a dry run and read-only checks, and verified afterwards; the README still said 15. --- docs/TODO.md | 7 ------- docs/general/database/README.md | 6 ++++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 99244ec..a4a0e7c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -30,13 +30,6 @@ preview. ### 🔴 Alta -- [ ] **Aplicar en Aiven `20261011_keep_group_history_on_delete.sql` antes de - desplegar el borrado de grupos** — el código escribe una fila de - historial tras borrar el grupo; con la clave foránea todavía en Aiven - ese insert falla y el borrado se deshace (500). Preview y producción - usan Aiven: aplicarla antes de que la rama llegue a compassionate, con - `scripts/apply-migration.sh aiven --dry-run` primero, y - marcarla en `backend/db-mysql/INDEX.md`. Ya aplicada en local. - [ ] **Rotar las credenciales de producción** — contraseña de la BD y claves de Cloudinary, y revocar las claves de servicios que ningún código usa. El historial publicado no las contiene, pero siguen en copias privadas diff --git a/docs/general/database/README.md b/docs/general/database/README.md index 03ade29..43d4468 100644 --- a/docs/general/database/README.md +++ b/docs/general/database/README.md @@ -75,8 +75,10 @@ Tables by module: repository because the backend and `apply-migration.sh` need it (decided on 2026-10-12). -**Migration status**: the 15 recorded in `INDEX.md` are applied on Aiven -(verified on 2026-09-28). +**Migration status**: the 26 recorded in `INDEX.md` are applied on Aiven +(the last two, `20261011_keep_group_history_on_delete.sql` and +`20261012_unique_invoice_supplier_number.sql`, applied and verified on +2026-10-12). ## How information flows