diff --git a/docs/TODO.md b/docs/TODO.md index 473ba85c..a4a0e7ce 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -34,7 +34,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 @@ -132,11 +136,34 @@ 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, + `CURDATE()`/`NOW()` en Aiven (19 consultas con `CURDATE()`: filtros de + 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). + + 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 @@ -208,75 +235,28 @@ 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í._ -- [ ] **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._ +- [ ] **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._ - [ ] **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 `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í._ -- [ ] **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í._ + 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._ ### 🟢 Baja @@ -316,87 +296,18 @@ 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: `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); - 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 - 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í._ +- [ ] **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._ - [ ] **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 - 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._ @@ -405,7 +316,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/checklist/README.md b/docs/checklist/README.md index 6300c9f3..139b8552 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 c94d5368..b7043115 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,8 +65,12 @@ 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`.** +- **`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 diff --git a/docs/general/database/README.md b/docs/general/database/README.md index e566e8c5..43d44687 100644 --- a/docs/general/database/README.md +++ b/docs/general/database/README.md @@ -70,10 +70,15 @@ 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/`. - -**Migration status**: the 15 recorded in `INDEX.md` are applied on Aiven -(verified on 2026-09-28). + `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 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 diff --git a/docs/general/messages/README.md b/docs/general/messages/README.md index 323d2cf8..33d9fa33 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 53f06f09..5b05f959 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`): @@ -58,6 +63,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 51c505ed..4caa6986 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** @@ -126,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.