Create, rename, activate/deactivate or delete projects, and manage per-project user access (Allowed/Read-only/Settings Admins). To switch which project you're working on, use the selector in the Project tab — this panel edits whichever project is currently active.
Automatic backups are saved every time you push data to the cloud. Keep the 3 most recent backups, and older versions are automatically deleted after 90 days.
v1.06
Fix: Contract Vol. y Forecast en Summary y Package Detail. Contract Vol. sumaba Saves (siempre negativo), por lo que lo restaba del volumen contratado. Ahora Contract Vol. = Main Contract + CO + Order, y Forecast = Contract Vol. + ACP + Saves. En el desglose de Package Detail, la línea Saves pasa a ir entre ACP y Forecast. Petición explícita de Antonio (ejemplo: 08 OBRA CIVIL, Contract Vol. debía ser 3.788.562,99 € y mostraba 3.740.562,99 €).
v1.05
Summary: salto de línea explícito en las cabeceras de Variance. El ajuste de v1.04 (white-space:normal) no garantizaba que el título pasara a dos líneas: con table-layout:auto, el navegador puede seguir dando hueco suficiente para mostrarlo en una sola línea si hay espacio disponible en la tabla — dependía de cuánto se hubiera encogido el resto de columnas, no de una regla fija. Ahora el salto es explícito (<br> en el propio texto de la cabecera): "Variance vs
Budget" y "Variance vs
Actual Budget", siempre en el mismo punto, con independencia del ancho disponible. Petición explícita de Antonio ("no me gusta como queda").
v1.04
Summary: cabeceras de Variance en dos líneas. Las dos columnas nuevas de v1.03 ("Variance vs Budget"/"Variance vs Actual Budget") ensanchaban mucho su columna al llevar el título en una sola línea (comportamiento por defecto de las cabeceras de tabla en toda la app). Nueva clase .wrapHead: el título de ambas cabeceras pasa a dos líneas (texto centrado, tamaño de letra algo menor), con lo que la columna vuelve a ocupar un ancho similar al resto — la fila de cabecera crece de alto automáticamente para acomodar las dos líneas, sin afectar al resto de cabeceras de esa fila. Petición explícita de Antonio.
v1.03
Lists: nueva columna Description. Summary: desviación desglosada en dos columnas. (1) La pantalla Lists no mostraba ningún campo de texto libre — el usuario tenía que abrir Package/Contract Detail para saber de qué trataba un registro. Nueva columna "Description" (entre Type y Amount) que unifica, para cualquier tipo de registro, el campo de texto correspondiente: descripcion en BUDGET/TRASPASO BUDGET/ACP/SAVE, contrato en CONTRATO/OS y conceptoFactura en FACTURA — mismo orden de precedencia ya usado en el buscador (Search) y en Package Detail. Export CSV de Lists actualizado con la misma columna. (2) La pantalla Summary tenía una única columna "Variance" (Forecast − Actual Budget, ya con ajustes ACP y traspasos incluidos). Ahora hay dos: "Variance vs Budget" (Forecast − Budget Inicial, sin ajustes) y "Variance vs Actual Budget" (la desviación que ya existía, solo renombrada para distinguirla de la nueva). Mismo criterio de color aplicado a ambas. KPI superior y exportaciones (Excel del workbook completo, CSV de Summary) actualizados con el mismo cambio. Petición explícita de Antonio.
v1.02
Fixes tras siete auditorías externas de seguridad (verificadas contra el código): corregidas dos regresiones funcionales reales de la migración de v1.01 — (1) crear/borrar un "lote global" (Packages), editar los usuarios de un proyecto desde Admin → Projects y crear un proyecto nuevo ya no pueden sobrescribir en silencio cambios de otro usuario ni colisionar con un identificador existente: las tres operaciones comparten el mismo criterio "fallar cerrado" que ya usa el botón Push normal; (2) Contratistas y su catálogo (compartido por todos los proyectos) queda restringido de verdad al superadmin en las tres capas. Además, cerrar sesión ya borra la copia local de los datos del navegador, y se retiró el código heredado que consultaba de forma anónima, antes de iniciar sesión, la tabla de la arquitectura anterior a la migración de seguridad. (3) SEC-08 (XSS almacenado) cerrado en toda la aplicación, incluyendo una familia de casos más sutil detectada en la séptima revisión: escapar con escHtml() el contenido de un atributo onclick="alguna_funcion('...')" NO es una defensa real — es un escape para contexto HTML, no para el JavaScript embebido dentro de ese atributo, y el navegador decodifica las entidades HTML antes de ejecutar ese JavaScript. Cerrados los 2 sitios reales con este patrón (el buscador de nº de contrato y el de nombre de lote, que ahora generan sus opciones con textContent/addEventListener en vez de HTML interpolado) y los 3 enlaces clicables de las fichas de detalle (ahora abren el link vía un atributo data-link ya validado, con un único onclick fijo sin datos interpolados) — más, de paso, SEC-09: linkBtn() y esos mismos enlaces ya no aceptan ningún link que no sea https: real (antes solo se escapaba el HTML del link, sin comprobar su protocolo). Ver PLAN_MIGRACION_SEGURIDAD_2026-09.md.
v1.01
Migración de seguridad — separación real de datos por proyecto: hasta v1.00, todos los proyectos vivían en una única fila de la base de datos, descargada entera por cualquier usuario autenticado — la separación de "qué proyecto ves" era solo de interfaz, no de acceso real. Esta versión reparte los datos en tablas propias por proyecto (cost_control_projects) y una tabla de permisos (project_permissions) que Supabase usa para filtrar de verdad qué puede leer o escribir cada usuario, sin depender del navegador. También deja de guardarse la contraseña del superadmin en este navegador (antes se guardaba en texto plano para comodidad; ahora hay que reintroducirla cada vez que se usa "Guardar y conectar"). Crear/editar proyectos y gestionar sus usuarios sigue haciéndose exactamente igual desde la pantalla de Ajustes — sin cambios de uso para el superadmin ni para el resto del equipo. Ver PLAN_MIGRACION_SEGURIDAD_2026-09.md.
v1.00
Versión oficial 1.00 — Control de Costes pasa a producción: Antonio declara esta app como su primera versión oficial, operativa y funcional, alcanzando el hito reservado en la convención de versionado desde la Rev. 99 (2026-08-20). A partir de esta versión, los textos de versión (título, login, cabecera) dejan de mostrar el contador de Rev. junto al número de versión — mostrar "v0.112 · Rev. 112" duplicaba la misma información dos veces; a partir de ahora se muestra solo el número de versión (vX.XX), que pasa a ser también el único indicador visible de revisión. El Version Log sigue registrando cada entrega, ahora con el número de versión como etiqueta en vez de "Rev. XX". Petición explícita de Antonio.
Rev. 110
Fix: el superadmin no podía borrar lotes recién creados (ni ningún lote "numerado" por encima de 59): reportado por Antonio al probar la Rev. 109 — los lotes obligatorios de siempre (60 CONTINGENCIES...99 PRORATE) deben seguir bloqueados tal cual, pero un lote nuevo creado libremente también aparecía con el botón "×" deshabilitado. Causa: la comprobación de "lote obligatorio, no se puede borrar" (deletePackage() y el render de la fila en renderSettings()) usaba packNum(p)>=60 — cualquier lote cuyo número (real, o el valor por defecto 999 que packNum() asigna cuando el prefijo no es numérico, como el placeholder recién añadido "XX NEW") fuera 60 o más se trataba como obligatorio, en vez de comprobar si es realmente uno de los 11 lotes fijos de DEFAULT_PACKAGES (60/70/80/81/82/83/84/90/97/98/99). Corregido con MANDATORY_PACK_NUMS (conjunto de esos 11 números concretos, calculado una vez a partir de DEFAULT_PACKAGES) e isMandatoryPackage(p), que sustituye a packNum(p)>=60 en ambos puntos. Los 11 lotes obligatorios siguen exactamente igual de bloqueados que antes; cualquier otro lote (nuevo o ya existente, tenga el número que tenga) pasa a ser borrable por el superadmin. Petición explícita de Antonio.
Rev. 105
Sidebar: columna izquierda más ancha, tooltip con el nombre completo del proyecto al pasar el cursor, y Version Log movido a Admin: (1) .sidebar pasa de 200px a 230px, para que los nombres de proyecto largos en el grupo "Projects" se corten menos. (2) Nuevo tooltip propio (#sideProjectTip, sideProjectTipShow()/sideProjectTipHide()/sideProjectTipEl(), mismo patrón position:fixed ya usado por #cfPctFloat) que aparece junto a la fila al pasar el cursor sobre un proyecto del sidebar (sin clicar), mostrando el nombre completo — sustituye al atributo title nativo (tooltip del navegador, lento y con estilo genérico) por uno con la misma estética oscura del sidebar y aparición inmediata. (3) La sección "Version Log" (antes en el tab "Project", visible a cualquier admin de proyecto) pasa al tab "Admin", justo debajo de "Database Backups & Version History" — coherente con que el resto del historial técnico de la app ya vive ahí, y con que el tab "Project" quede centrado en el uso diario. Petición explícita de Antonio.
Rev. 104
Settings dividido en dos pantallas — "Project" (cualquier admin) y "Admin" (solo superadmin): hasta ahora, una única pantalla Settings mezclaba gestión de proyectos (crear/borrar/usuarios), Cloud Settings, Cloud Admin Account y Backups/Version History (en la práctica ya solo operativos para superadmin) con configuración de uso diario del proyecto (selector, Packages, Warranty/Prorate, Cashflow Settings), visible a cualquier admin de proyecto. Ahora: "Project" (antes "Settings", visible a cualquier isSettingsAdmin()) — selector de proyecto limitado a los proyectos donde ese admin está autorizado (myProjectsList(), mismo criterio que el selector del sidebar; el superadmin los ve todos), Packages (añadir siempre permitido; borrar solo visible para el superadmin, admin@admin.es), Project Settings, Cashflow Settings y Version Log. "Admin" (pestaña nueva, solo superadmin) — CRUD completo de Projects (crear, activar/desactivar, borrar, gestión de Allowed/Read-only/Settings Admins), Cloud Settings, Cloud Admin Account y Database Backups & Version History; opera siempre sobre el proyecto seleccionado en la pestaña "Project". Nuevas renderProjectPicker() (selector ligero del tab Project) y renderAdminSettings() (agrupa lo anterior); changeSettingsProject() pasa de isSuperAdmin() a isSettingsAdmin() con verificación explícita de projectUserAllowed() para no-superadmin (evita que un admin de un proyecto pueda activarse dentro de otro ajeno). Petición explícita de Antonio.
Rev. 103
Fix: al cambiar de proyecto en Settings, el título/código del proyecto y los campos de Users no se actualizaban: regresión introducida por la Rev. 102. changeSettingsProject() pasa por setActiveProject(), que ya actualiza SETTINGS_PROJECT_ID y dispara renderCurrentPanel() → renderSettings() — pero renderSettings() vuelve a llamar a syncSelectedProjectEditorToStore() como primera línea, y esa función leía getSettingsProjectId() "en caliente": en ese segundo llamado ya devolvía el proyecto **nuevo**, mientras el formulario en pantalla (Center/Project/Active/Users) todavía mostraba los valores del proyecto **anterior** (el nuevo renderProjectEditor() aún no se había ejecutado) — el resultado era que los datos antiguos se volcaban sobre el proyecto nuevo, sobrescribiéndolo con sus propios valores stale en el mismo instante en que se cargaba. Corregido con un id explícito (PROJECT_CARD_RENDERED_ID, nueva variable) que registra a qué proyecto pertenecen realmente los campos visibles en el DOM en cada momento — se fija al final de renderProjectEditor() y es el que usa syncSelectedProjectEditorToStore() para decidir dónde volcar, en vez de volver a preguntar el proyecto "actual" cada vez. Detectado por Antonio.
Rev. 102
Settings → Projects: seleccionar un proyecto lo activa directamente, sin botón "Use": hasta ahora, elegir un proyecto en el selector o en la lista de accesos rápidos de Settings (superadmin) solo cambiaba qué proyecto se estaba editando en el panel (changeSettingsProject()) — para que pasara a ser el proyecto realmente en uso en el resto de la app había que pulsar además un botón "Use" aparte (useSettingsProject()). changeSettingsProject() pasa a llamar directamente a setActiveProject() (misma función que ya usa el selector de "Projects" del sidebar, Rev. 76, y que ya cambia de proyecto sin botón adicional): la selección y la activación quedan unificadas en una sola acción. Retirados el botón "Use" y el indicador "Current App Project" (quedaba siempre igual al proyecto seleccionado, sin poder divergir). Petición explícita de Antonio.
Rev. 101
Ventana flotante de % acumulado (Cashflow, casillas Budget %/Ajuste %): añadidos los importes en € junto a cada porcentaje: además de "Acumulado" y "Resta" en %, la ventana muestra ahora el importe correspondiente entre paréntesis (Acumulado/Resta × Budget Actual del lote ÷ 100, la misma base que ya usa la tabla para calcular Budget €/Ajuste € por mes), y una tercera línea "Este mes" con el % que se está escribiendo y su importe, actualizada tecla a tecla igual que el resto del panel. Los inputs cf-budget/cf-adjust pasan a llevar data-base (Budget Actual del lote, calculado una vez por fila en renderEditableRow()) para que cfPctShow() no tenga que recalcularlo. Panel ensanchado de 170px a 230px (fijo por CSS, sin medir offsetWidth, mismo criterio de rendimiento que la Rev. 90) para que quepan los importes sin partir línea. Petición explícita de Antonio.
Rev. 100
Invoice Summary: lote clicable y botón de link uniformado en toda la app: la fila "TOTAL [Package]" de cada lote abre ahora la misma ventana emergente de detalle de lote (showPackDetail()) ya usada en Summary, en vez de ser texto plano — mismo componente .packLink; las filas individuales de contrato no son clicables, solo el subtotal del lote. Nuevo botón circular de enlace (icono SVG "enlace externo", nueva clase CSS .linkBtn + helper JS linkBtn(url)) que sustituye, en toda la app, al símbolo de texto "↗" usado hasta ahora: Invoice Summary (nueva columna, primer link no vacío entre las facturas del contrato de esa fila), historial de facturas de Invoice Entry, Package Detail (Contracts & Orders + Invoices), Contract Detail (Contract Lines + Invoices) y Lists (columna Link, que además deja de depender de openLink() — retirada por quedar sin uso — y pasa a un <a href> real con la URL escapada). Petición explícita de Antonio, que pidió expresamente extender el nuevo icono (ya aprobado en Invoice Summary) al resto de sitios con contratos, facturas o pedidos con link.
Rev. 99
Fix: las ventanas emergentes de detalle (Package Detail, Contract Detail) necesitaban scroll horizontal: .pdBody (cuadrícula Cost Breakdown + Contracts & Orders de Package Detail) usaba grid-template-columns:220px 1fr — el pitfall clásico de CSS Grid en el que una pista 1fr sin minmax(0,...) crece para ajustarse al contenido (la tabla de contratos) en vez de respetar el espacio disponible, forzando scroll horizontal en todo el modal. Corregido a minmax(160px,220px) minmax(0,1fr). Además, las celdas de tabla dentro de cualquier ventana emergente (.modal-body table) ahora permiten partir texto largo sin espacios (overflow-wrap:anywhere) en vez de forzar anchura — nombres de contratista largos ya no empujan la tabla más allá del modal. El scroll vertical sigue disponible donde ya lo había (listados de contratos/facturas con max-height). Petición explícita de Antonio.
Rev. 98
Excel del Informe de Cashflow: en las filas de lote, el % pasa a ser el dato guardado y el importe el que se calcula (antes al revés): en la hoja "DETALLE COMPLETO" (Rev. 97), las filas de lote tenían el importe (€) como número pegado y el % como fórmula dividiendo €/base — invertido respecto a cómo funciona realmente el dato: Budget % y Ajuste % (en meses abiertos) son el valor que se guarda y edita en la app, y el importe se calcula multiplicando ese % por el Budget Actual del lote, igual que hace buildCashflowProjection(). Ahora la celda de % lleva el número guardado y la de € la fórmula =Budget×%/100. Sin cambios en Coste Real (siempre un hecho medido, nunca un % de entrada) ni en Ajuste de meses cerrados (el importe sigue siendo el Coste Real real, con el % derivado de él) ni en las filas de categoría/totales (siguen siendo sumas, sin % guardado en ningún sitio al que referenciar). Petición explícita de Antonio.
Rev. 97
Excel del Informe de Cashflow reescrito para llevar fórmulas reales, no solo valores pegados: la hoja "DETALLE COMPLETO" deja de leerse del texto ya formateado en pantalla (Rev. 95) y pasa a construirse con números reales y fórmulas de Excel — SUMA() en las filas de categoría/totales sobre el rango de sus lotes, sumas por referencia entre subtotales para HARD COSTS/SOFT COSTS/TOTAL (que no vuelven a sumar cada lote, referencian directamente sus dos subtotales), y los tres % (Budget %, Coste Real %, Ajuste %) como fórmulas que dividen la celda € entre la celda Budget/Contratado de la misma fila, con protección `IF(...=0,0,...)` para evitar errores de división por cero. La hoja resumen ("CASHFLOW REPORT") pasa a su vez a referenciar la fila TOTAL de "DETALLE COMPLETO" para los 4 KPIs y la tabla mensual, en vez de llevar los mismos números pegados por duplicado. Nuevo `window.buildFullDetailFormulaMatrix()` (dentro del cierre de `renderCashflow()`, mismo patrón que `exportCashflowExcel`) y `excelColLetter()`. El PDF no cambia — sigue usando `cfReadFullDetailTable()` (lectura de texto ya formateado), porque un PDF no admite fórmulas vivas. Petición explícita de Antonio.
Rev. 96
Fix: los totales de categoría (CONSTRUCTION COSTS, HARD COSTS...) sumaban de más la columna "Budget" cuando algún lote tenía Budget Actual exactamente 0€: las filas individuales calculan ese caso con r.actualBudget??r.budget (operador de coalescencia nula: solo cae al Budget Inicial si `actualBudget` es `null`/`undefined`, nunca si es `0`), pero la suma de categoría (sumRows()) usaba data.summary.actualBudget||data.summary.budget (operador OR: trata `0` como "vacío" y cae igualmente al Budget Inicial) — por eso un lote mostraba correctamente 0k€ en su fila pero sumaba su Budget Inicial (no 0) al total de categoría. Mismo fix aplicado a la base del reparto mensual (`buildCashflowProjection`, llamada tanto desde `renderCashflow()` como desde el Informe de Cashflow, Rev. 94/95) por el mismo motivo. Ejemplo: CONSTRUCTION COSTS mostraba 15,11M€ en vez de 12,15M€ (coincidente con "suma budget ajustado", que sí sumaba bien por usar otra ruta de cálculo). Detectado por Antonio.
Rev. 95
Informe de Cashflow: anexo/hoja con el Detalle completo cashflow tal cual está: el PDF añade una página apaisada (A4 landscape, con salto de página horizontal automático si no cabe en una) con la tabla completa por lote y mes (Budget €/%, Coste Real €/%, Ajuste €/%, incluidas las filas de totales por categoría). El Excel añade una segunda hoja "DETALLE COMPLETO" con la misma tabla, cabeceras combinadas y primera columna/dos filas de cabecera congeladas para facilitar la navegación. En ambos casos, el contenido se lee directamente del DOM de la tabla ya renderizada en pantalla (nuevo cfReadFullDetailTable()) en vez de recalcular las fórmulas por separado — garantiza que el anexo coincide exactamente con lo que se ve en la app, sin riesgo de que ambos cálculos diverjan con el tiempo. Petición explícita de Antonio.
Rev. 94
Nuevo: Informe de Cashflow para remitir a la propiedad, en PDF y Excel: dos botones "📄 Generar PDF"/"📊 Generar Excel" en Detalle completo cashflow, en una barra separada de las herramientas internas ("Informe para propiedad"). Ambos incluyen cabecera de proyecto, 4 KPIs (Budget, Contratado, Facturado, Ajustado), un gráfico de evolución mensual (Budget/Real facturado/Ajustado) y la tabla mensual de esas tres series — nivel de resumen de proyecto, sin desglose por lote. El PDF usa jsPDF + jspdf-autotable (nuevas dependencias CDN). El Excel usa ExcelJS (nueva dependencia CDN) en vez de SheetJS específicamente para este informe, porque SheetJS gratuito no soporta escribir imágenes en el archivo — el gráfico de evolución se incrusta como imagen fija (no editable como gráfico nativo de Excel); el resto de exportaciones Excel de la app siguen en SheetJS. Petición explícita de Antonio, confirmado el alcance (resumen de proyecto, gráfico como imagen) con AskUserQuestion.
Rev. 93
Excel export/import (Rev. 91): meses cerrados exportan el Ajuste % efectivo, no el valor guardado: para un mes cerrado, `adjustArr[i]` (el % planificado) ya no se usa en la app — el Ajuste % real que se aplica es Coste Real ÷ Budget Actual (mismo cálculo de la Rev. 92). El Excel exportado volcaba el valor guardado (a menudo 0 o desactualizado) en vez de ese % efectivo. Nuevo closedAdjustPct(pack,i)/cfExportAdjustArr(pack): los meses cerrados se rellenan con el % efectivo (de solo lectura en la práctica, ya que tampoco son editables desde la tabla) y los abiertos siguen mostrando el % planificado editable. El import compara también contra ese % efectivo en meses cerrados, para no marcar como "distinto" un valor que en realidad coincide con lo exportado. Petición explícita de Antonio.
Rev. 92
Fix: Ajuste % dividía por "suma budget ajustado" (la suma de los propios ajustes del lote) en vez de por Budget Actual: mismo tipo de error que el de Budget % (Rev. 90) pero en la columna de Ajuste. Corregido en renderRow()/renderEditableRow(): `baseAdj` pasa de `tAdj` (suma de projection.adjusted) a `tBudget` (Budget Actual, la misma cifra ya usada como base de Budget %). La columna "suma budget ajustado" del resumen no cambia (sigue mostrando esa suma como referencia informativa, solo deja de usarse como denominador del %). Ejemplo: lote 01 EXCAVACION Y MOV. TIERRAS, mes cerrado con 108k€ de ajuste sobre 1,25M€ de Budget Actual = 8,64% (antes 12%, dividiendo por 872k€ de suma ajustado). Detectado por Antonio.
Rev. 91
"Detalle completo cashflow": exportar/importar Budget % y Ajuste % en Excel: nuevos botones "↓ Exportar % a Excel" y "↑ Importar % desde Excel" (solo admin), visibles junto a Export CSV/Refresh. La exportación (exportCashflowExcel()) genera un `.xlsx` con un lote por fila y dos bloques de columnas (Budget %, Ajuste %) por mes, cada bloque con una columna "Total" con fórmula =SUMA() para ver de un vistazo si el reparto cuadra al 100% mientras se rellena en Excel — igual que la ventana flotante ya hace en la app (Rev. 89/90). La importación (cfImportFile()) no aplica los cambios directamente: primero muestra un resumen con cada celda que cambiaría (lote, serie, mes, valor actual → nuevo), lotes no reconocidos y celdas de Ajuste % en meses cerrados ignoradas (no editables desde la tabla tampoco), y solo escribe en el proyecto tras pulsar "Confirmar import" (cfApplyImport()). Pensado como alternativa más ágil a rellenar celda a celda en la propia tabla. Petición explícita de Antonio.
Rev. 90
Fix: Budget €/mes se calculaba sobre Budget Inicial, no sobre Budget Actual/Ajustado (el mismo número de la columna "Budget"): `buildCashflowProjection()` vuelve a usar un único `totalBudget` (= Budget Actual) para Budget € y Ajuste €/mes — antes de la Rev. 85 ya era así; la separación introducida en esa revisión resultaba en que la columna "Budget" (Rev. 86, Budget Actual) y el reparto mensual de Budget % (Budget Inicial) usaran referencias distintas sin que se viera reflejado en la interfaz, dando resultados que no cuadraban al calcular a mano. Coste Real % sigue sobre Contratado, sin cambios. Ejemplo corregido: lote 04 ESTRUCTURA PREFABRICADA, 15% de 1,82M€ (Budget Actual) = 273k€, antes 204k€ (15% de 1,36M€ Budget Inicial). Además, fix de fluidez: la ventana flotante de % acumulado (Rev. 89) recalculaba su posición en pantalla (`getBoundingClientRect`/medida de ancho) en cada pulsación de tecla, forzando un recálculo de layout del navegador en una tabla grande — sensación de "trompicones" al escribir. Ahora la posición solo se calcula una vez, al entrar en la casilla; el contenido se sigue actualizando en cada tecla sin volver a medir nada. Detectado por Antonio.
Rev. 89
Sustituido el desplegable de % acumulado (Rev. 87/88) por una ventana flotante que aparece al editar: Antonio pidió un patrón distinto — no un icono que hay que pulsar, sino una ventana que aparece sola al seleccionar la casilla de % y se actualiza en vivo mientras se escribe. Los inputs de Budget %/Ajuste % ya no llevan el icono "Σ%"; al hacer foco (o escribir) en cualquiera, aparece junto a la casilla una ventana con el % acumulado del lote/serie y lo que resta hasta 100% (en rojo si se supera), calculado con el valor todavía no guardado que se está tecleando. Desaparece al salir de la casilla. Un único elemento flotante (`#cfPctFloat`, fuera de la tabla, anexado a `document.body`) evita el problema de recorte por `overflow:auto` que tenía el desplegable anterior.
Rev. 88
Fix: el desplegable de % acumulado (Rev. 87) no aparecía al pulsar el icono "Σ%": el panel usaba position:absolute, recortado por el overflow:auto de .cashflowFullWrap — mismo problema ya conocido y resuelto en esta app para los desplegables de autocomplete (contratistas, nº de contrato). Corregido con el mismo patrón: position:fixed + getBoundingClientRect(), calculado en el nuevo cfPctToggle() (disparado por el evento toggle del <details>), con ajuste para no salirse del viewport por la derecha. Detectado por Antonio.
Rev. 87
Detalle completo cashflow: quitadas las flechas de los inputs de % mensual y añadido desplegable de % acumulado/restante por lote: los campos numéricos de Budget % (cf-budget) y Ajuste % (cf-adjust) dejan de mostrar las flechas de subida/bajada del navegador (edición solo por teclado). Junto al nombre de cada lote aparece un icono "Σ%" que, al pulsarlo, despliega un panel con el % acumulado de Budget y de Ajuste a lo largo de todos los meses y lo que resta hasta el 100% (en rojo si el acumulado supera el 100%); se cierra solo al clicar fuera. Petición explícita de Antonio, que edita los porcentajes directamente con el teclado y quería esta información como consulta puntual, no fija en la fila.
Rev. 86
Fix: columna "Budget" de "Detalle completo cashflow" mostraba Budget Inicial en vez de Budget Actual: la columna de totales "Budget" (primera de las cuatro, junto a Contratado/suma budget ajustado/Facturado) usaba meta.budget/r.budget (Budget Inicial, sin ajustes) tanto en las filas de categoría (renderRow()) como en las de lote (renderEditableRow()). Pasa a usar actualBudget (Budget Inicial + ajustes ACP + traspasos) cuando existe, con Budget Inicial como fallback. La base del Budget %/mes (Rev. 85) no cambia — sigue dividiendo por Budget Inicial, ahora en una variable separada (tBudgetInicial) para no arrastrar el mismo cambio. Detectado por Antonio: 01 EXCAVACION Y MOV. TIERRAS mostraba 1,26M€ en vez de 1,248M€.
Rev. 85
Fix: fórmulas de % erróneas en "Detalle completo cashflow" (Budget %, Coste Real %, Ajuste %): los tres porcentajes de cada mes se calculaban contra la misma base ("suma budget ajustado"), en vez de cada uno contra su referencia propia. Corregido en buildCashflowProjection() (Budget €/mes ahora se calcula sobre el Budget Inicial del lote, no sobre el Budget Ajustado) y en renderRow()/renderEditableRow() (Budget % = Budget €/mes ÷ Budget Inicial; Coste Real % = Coste Real €/mes ÷ Contratado; Ajuste % = Ajuste €/mes ÷ suma Budget Ajustado — cada uno con su propio denominador). No afecta a las columnas de totales del resumen (Budget/Contratado/suma budget ajustado/Facturado) ni a las filas CONTROL BUDGET INITIAL TOTAL/CONTROL ADJUSTED TOTAL, que ya usaban la base correcta. Detectado por Antonio con ejemplos concretos (Construction Costs 62k€ sobre 11,98M€ mostraba 4% en vez de 0,53%; 80 HON 20% de 503k€ mostraba 97k€ en vez de 100k€).
Rev. 84
Cashflow: editar el Budget % por package/mes deja de exigir la cuenta admin@admin.es: la columna Budget % de "Detalle completo cashflow" solo era editable si isSuperAdmin() (comparaba el usuario conectado contra la cuenta literal admin@admin.es), un nivel más estricto que el resto de acciones de administrador de la app. Ahora usa isSettingsAdmin() (superadmin o cualquier usuario de la lista settingsAdmins del proyecto), mismo criterio que "Apply Gauss Distribution" y "Apply Plan to All Packages". Petición explícita de Antonio, que no podía editar la columna con su cuenta habitual de administrador del proyecto.
Rev. 83
Fix: los porcentajes de Budget mensual editados en Settings → Cashflow Settings no se guardaban ni se usaban: el botón "Save Settings" nunca leía los campos de % editables del plan inicial (función readCashflowPlanFromUI(), ya existente pero huérfana), así que cualquier cambio se perdía al guardar; y aunque se hubiera guardado, los paquetes nuevos tomaban siempre la curva de Gauss fija en vez del plan editado. Corregido: "Save Settings" y "Normalise Budget %" ahora sí leen y persisten los % introducidos; cualquier package sin distribución propia hereda el plan inicial editado (antes: siempre Gauss); nuevo botón "Apply Plan to All Packages" para aplicar el plan editado también a packages que ya tienen distribución propia (mismo criterio que "Apply Gauss Distribution", pero con el plan del usuario en vez de recalcular Gauss). Las cifras de Budget € mensuales por package ya se recalculaban automáticamente a partir del %, sin cambios ahí. Petición explícita de Antonio.
Rev. 82
Invoice Entry: listado de facturas del contrato junto al formulario: al seleccionar o buscar un número de contrato en Invoice Entry, aparece a la derecha del formulario un panel con todas las facturas ya registradas de ese contrato/OS/pedido (nº factura, fecha, importe certificado, concepto, enlace y total), para verlas de un vistazo mientras se introduce la nueva sin tener que navegar a otra pantalla. La factura que se está editando queda resaltada en el listado; clicar cualquier fila la carga en el formulario (mismo comportamiento que Search). Nueva función invRenderHistory(), invocada desde invFillFromContract() y invNew(). Petición explícita de Antonio.
Rev. 81
Fix: Invoice Entry perdía Link y mostraba Warranty/Prorate Ret. en blanco al editar una factura existente: invLoad() nunca asignaba inv-link (el campo quedaba vacío al reabrir la factura, y al guardar se sobreescribía el link real con cadena vacía). Además calculaba Warranty Ret./Prorate Ret. antes de rellenar el importe certificado (inv-cert) — ncAcSelect('inv-ncontrato',...) dispara invFillFromContract()→invUpdateRetentions(), que lee inv-cert en ese momento, así que las retenciones se calculaban con el valor todavía vacío (o el de la factura anterior) en vez del certificado real de la factura cargada. Corregido reordenando invLoad(): ahora inv-cert e inv-link se rellenan antes de llamar a ncAcSelect. Detectado por Antonio.
Rev. 80
Parent Contract (Contract Entry) con búsqueda por contrato, empresa y lote: el campo Parent Contract de las Órdenes de Servicio (OS) pasaba a solo filtrar por número de contrato al escribir, sin mostrar contratista ni lote — a diferencia de Contract Number en Invoice Entry, que ya buscaba por las tres columnas. Ahora usa exactamente el mismo desplegable de búsqueda (número, empresa o lote, con las tres columnas visibles en cada resultado), restringido a contratos de tipo CONTRATO (los únicos que pueden ser padre de una OS). Además, baja de varias ACP al crear una OS: tras guardar una nueva Orden de Servicio, antes solo se podía dar de baja una ACP del lote; ahora, tras cada baja, se pregunta si se quiere dar de baja otra, repitiendo hasta que no queden ACP abiertas en ese lote o el usuario decida parar. Protegido además contra seleccionar dos veces la misma ACP ya cerrada. También corregido: Package (Contract Entry) quedaba colgado al cambiar de pantalla: `icNew()` reseteaba Contract Number, Description, Amount, Contractor, Link y Parent Contract al entrar en Contracts, pero no Package — quedaba visible el último valor tecleado de una entrada anterior aunque el formulario estuviera "en blanco". Ahora Package también se limpia.
Rev. 79
Fix: Grand Total incorrecto en Invoice Summary: el Grand Total (Contracted, Invoiced, Pending) se calculaba con una acumulación que podía duplicar importes si `DB.packages` contenía algún package repetido. Corregido: ahora el Grand Total se calcula explícitamente como la suma de los subtotales "TOTAL [Package]" ya mostrados en pantalla (nunca una pasada independiente sobre las filas), y la lista de packages se deduplica antes de recorrerla. De paso, Warranty Ret. y Prorate Ret. — hasta ahora en blanco tanto en los subtotales como en el Grand Total — pasan a sumarse igual que el resto de columnas.
Rev. 78
Desplegables de Inputs con búsqueda dinámica: los campos Package (Budget, Budget Transfer, ACP, Contracts) y Parent Contract (Contracts) pasan de <select> plano a campo de búsqueda con autocomplete (mismo patrón ya usado en el buscador de contratistas y en Contract Number de Invoice) — al escribir, filtran dinámicamente por coincidencia. Además, Contract Number en Saves pasa a tener exactamente el mismo comportamiento y opciones que Contract Number en Invoice Entry: búsqueda por número de contrato, empresa y lote.
Rev. 77
Tipografía cambiada a Arial: sustituidas Barlow Condensed y Roboto (Google Fonts) por Arial en toda la app — variables CSS --font-title/--font-body, etiquetas de los gráficos Chart.js y estilos de la exportación a Excel. Eliminado el enlace a Google Fonts (ya no se carga ninguna fuente externa). De paso, corregido el texto de versión de login/cabecera, que seguía mostrando v0.75 · Rev. 75 pese a ser ya la Rev. 76.
Rev. 76
Projects pasa al lateral como sección propia: la sección "Projects" (Rev. 75) deja de ser un panel independiente al que hay que navegar — ahora es un grupo más del menú lateral (resaltado en verde, igual que Outputs/Inputs/System), colocado justo debajo de Inputs, con el listado de proyectos autorizados directamente como filas clicables dentro del propio lateral. Cambiar de proyecto ya no navega a ningún panel: se queda en la pantalla donde estabas, solo cambian los datos. El proyecto activo se resalta con un borde de acento en su fila.
Show previous revisions
Rev. 112
Packages: iconos para distinguir de un vistazo un lote global de uno bloqueado: la etiqueta "🌐 Global" (Rev. 111) solo se mostraba en los lotes creados explícitamente por el superadmin (DB.globalPackages) — los 11 lotes obligatorios de siempre (60 CONTINGENCIES...99 PRORATE) no llevaban ningún distintivo, pese a ser también "globales" en la práctica (existen en todos los proyectos, aunque por el mecanismo de re-creación automática de savePackages() en vez de por el catálogo nuevo). Ahora los lotes obligatorios llevan también la etiqueta "🌐 Global", más un candado "🔒" propio (sustituye al antiguo botón "×" deshabilitado y en gris, que era el único indicio de que ese lote no se podía borrar) que ilustra específicamente el bloqueo — visible para cualquier admin de proyecto con acceso a la pantalla, no solo para el superadmin. Un lote creado por el superadmin pero no obligatorio conserva la etiqueta "🌐 Global" junto al botón "×" normal (sigue siendo borrable, con el aviso de que se borrará en todos los proyectos al guardar). Petición explícita de Antonio.
Rev. 111
Packages: los lotes creados por el superadmin quedan marcados como "globales" — borrarlos los borra también en todos los proyectos, igual que crearlos los crea en todos: la Rev. 109 ya propagaba la creación de un lote nuevo del superadmin a todos los proyectos, pero un lote así quedaba indistinguible de uno local en adelante — borrarlo solo afectaba al proyecto activo. Ahora cada lote creado por el superadmin se registra en un nuevo catálogo persistente DB.globalPackages (a nivel de toda la base de datos, no por proyecto) en el momento de guardar; al guardar de nuevo, cualquier lote que estuviera en ese catálogo y ya no esté en la lista del proyecto (es decir, se ha borrado en esta sesión) se retira también del resto de proyectos y se quita de DB.globalPackages. Un lote local (creado por un admin de proyecto, o cualquiera de los ya existentes antes de esta distinción, cuyo origen se desconoce) sigue afectando solo al proyecto donde se crea o se borra — borrarlo el superadmin no lo convierte en global, la marca depende de quién lo creó, no de quién lo borra. Nueva PACKAGES_BASELINE (paralela a NEW_PACKAGE_FLAGS, snapshot del catálogo tal como se cargó, reiniciada al entrar en un proyecto) para poder calcular qué se ha borrado en la sesión de edición actual; nueva removePackageFromAllProjects(names), simétrica a propagateNewPackagesToAllProjects(). En el editor de Packages, cada lote global lleva ahora una etiqueta "🌐 Global" junto al nombre, para poder distinguirlo a simple vista de uno local antes de decidir borrarlo. Petición explícita de Antonio.
Rev. 109
Packages: los lotes que crea el superadmin se aplican a todos los proyectos; los que crea un admin de proyecto siguen siendo solo de ese proyecto: hasta ahora, DB.packages es un catálogo aislado por proyecto (a diferencia de DB.contractors) y "+ Add Package" solo afectaba al proyecto activo, con independencia de quién lo creara — comportamiento correcto para un admin de proyecto, pero insuficiente para el superadmin, que gestiona la estructura de lotes compartida entre obras. Ahora, al pulsar "Save Packages", cada fila añadida en esta sesión de edición (no las ya existentes, aunque se renombren) se registra como "lote nuevo"; si quien guarda es el superadmin, ese lote se añade también al catálogo de todos los demás proyectos que todavía no lo tuvieran (sin duplicar, sin tocar el resto de su catálogo). Si quien guarda es un admin de proyecto (no superadmin), el comportamiento no cambia: el lote nuevo queda solo en su proyecto. Implementación: nuevo array NEW_PACKAGE_FLAGS paralelo a DB.packages (se marca en addPackage(), se realinea en deletePackage() y se reinicia al cargar un proyecto en loadActiveProjectFromStore()), y nueva propagateNewPackagesToAllProjects(names) invocada desde savePackages() antes de saveDB() (que empuja el objeto DB completo, con todos los proyectos, en un único push a Supabase). Petición explícita de Antonio.
Rev. 108
Fix: las filas "CONTROL BUDGET INITIAL TOTAL" y "CONTROL ADJUSTED TOTAL" de Cashflow se volvían ilegibles (texto blanco sobre fondo casi blanco) al pasar el ratón por encima: la regla genérica de hover de cualquier tabla de la app (table tr:hover td{background:#f8fdf9}) ya excluía .group/.subtot/.total/.rowTotal, pero no .rowGroup — la clase real de estas dos filas (fondo verde oscuro, texto blanco, .cashflowTable .rowGroup td). Como esa regla de hover tiene más especificidad que el estilo base de .rowGroup, ganaba sobre el fondo oscuro en la mayoría de celdas de la fila (las columnas mensuales, que no llevan el !important reservado a las 5 primeras columnas/clases concretas), dejando el fondo casi blanco con el texto blanco encima sin cambiar de color — invisible. Corregido añadiendo :not(.rowGroup) al selector de exclusión, mismo criterio que ya se aplicaba a .rowTotal. Al ser una regla global de tabla (no específica de Cashflow), el fix cubre también cualquier otra fila .rowGroup de la app con el mismo problema latente (ej. totales de categoría en Invoice Summary/Summary). Detectado por Antonio.
Rev. 107
Sidebar: los proyectos donde el usuario es admin se distinguen por color: en el grupo "Projects" del lateral, un usuario puede ser settingsAdmin de unos proyectos y solo usuario/readonly de otros — hasta ahora todas las filas se veían con el mismo tono, sin forma de distinguir de un vistazo en cuáles tiene privilegios de administración. Nueva función isProjectAdminFor(user,id) (superadmin siempre `true`; resto, comprueba si está en la lista settingsAdmins de ese proyecto concreto) y clase CSS .sideProjectItem.isAdmin (color var(--brand-sky), del panel de colores ya existente — se evitó --amber por su significado ya establecido de "warn" en KPIs/cloud). El estado .active (proyecto en uso) y :hover siguen teniendo prioridad visual sobre el tono de admin. Petición explícita de Antonio.
Rev. 106
Contractors: borrar un contratista pasa a ser exclusivo del superadmin: el botón "×" de cada fila de la pantalla Contractors (ctrDelete()) no llevaba ninguna restricción de rol — cualquier usuario con acceso al tab (isSettingsAdmin(), cualquier admin de proyecto) podía borrar un contratista del catálogo, que es compartido entre todos los proyectos (DB.contractors[], no está aislado por proyecto como DB.packages). Mismo criterio ya aplicado a Packages en la Rev. 104: el botón "×" se genera condicionalmente (canDeleteContractors=isSuperAdmin(), oculto por completo para el resto en vez de solo deshabilitado) y ctrDelete(i) gana el guard explícito if(!isSuperAdmin()) como defensa en profundidad. Añadir (ctrAddNew) y editar/guardar (ctrSave) un contratista siguen sin restricción adicional para cualquier isSettingsAdmin(). Petición explícita de Antonio.
Rev. 75
Selección de proyecto tras el login: la pantalla de inicio ya no muestra un desplegable con todos los proyectos — solo pide usuario y contraseña. El proyecto activo se marca claramente en el lateral y también como una insignia destacada en la cabecera de la app.
Rev. 74
Fix: número de versión desactualizado en pantalla de inicio y cabecera: Las Rev. 72 y 73 no habían actualizado el texto fijo "Current version" del login ni el de la cabecera (.hdrTitle), que seguían mostrando v0.71 · Rev. 71 aunque el Version Log ya reflejaba las revisiones posteriores. Corregido para que ambos muestren siempre la versión activa real.
Rev. 73
Invoice Entry: reseteo automático tras guardar: Al pulsar Save, el formulario se limpia automáticamente (mismo efecto que pulsar New) en lugar de dejar cargada la factura recién guardada. El mensaje de confirmación (nº de factura, contrato y certificado) se sigue mostrando sobre el formulario ya vacío.
Rev. 72
Invoice Entry: selector de contrato buscable: El desplegable "Contract Number" del formulario de facturas pasa de un <select> plano a un campo de búsqueda con autocomplete (mismo patrón que el buscador de contratistas). Al escribir, filtra por número de contrato, empresa o lote; cada resultado muestra las tres columnas (nº contrato — empresa — lote) para localizarlo sin ambigüedad.
Rev. 71
Seguridad: eliminada cuenta de superadmin hardcodeada: Se retira la contraseña fija embebida en el código (usada como atajo de login local). Ahora todos los usuarios, incluido el superadmin, se autentican siempre contra la cuenta real de Supabase Authentication introducida en la pantalla de login. Sin cambios de uso para el resto de usuarios.
Rev. 70
Prorate: clics en lotes y pedidos: En la tabla Prorate Incomes, cada nombre de lote es un enlace clicable que abre el modal de detalle del lote (igual que en Summary). En la tabla Prorate Expenses, cada número de pedido abre el modal de detalle del contrato.
Rev. 69
Summary: links externos en modales de contratos y facturas: En los modales de detalle de lote y de contrato, las filas de contratos/líneas de contrato y facturas que tengan un link almacenado muestran un icono ↗ al final de la fila. Clic en el icono (o en la fila de factura/línea de contrato) abre el link en pestaña nueva. En la tabla Contracts & Orders del modal de lote, el ↗ abre el link del contrato principal sin interferir con el clic en la fila que abre el detalle del contrato.
Rev. 68
Summary: dashboard flotante de contrato: Desde el modal de detalle de lote (Rev. 67), clic en cualquier número de contrato abre un segundo modal con cabecera azul mostrando: barra de info (contratista, lote, % garantía, % prorrateo), 6 KPIs (Contracted, Invoiced, Pending, % Certified, Warranty Ret., Prorate Ret.), tabla de líneas del contrato (CONTRATO/OS/PEDIDO/SAVE con tipo, descripción, fecha e importe) y tabla de facturas (Nº, fecha, concepto, certificado, retención garantía, retención prorrateo). Ambos modales apilables; clic fuera o × cierra cada uno.
Rev. 67
Summary: dashboard flotante por lote: Clic en cualquier nombre de lote en la tabla Summary abre un modal de detalle con 6 KPIs (Actual Budget, Contract Vol., ACP, Forecast, Variance, Invoiced+%), desglose presupuesto/coste (Budget inicial → Actual Budget, Main Contract → Variance), tabla de contratos con importes contratado/facturado/pendiente por ncontrato, y listado de facturas con fecha y certificado. Clic fuera o en × cierra el modal.
Rev. 66
Contractors: panel de análisis por año y obra: Columna derecha en la pantalla Contractors con tabla "Contracted & Invoiced" por contratista. Filtros desplegables de año y proyecto (obra); combinables: año concreto + obra concreta, año concreto + todas las obras, o una obra + todos los años. Muestra importe contratado, facturado y % de certificación. Total con porcentaje global al pie.
Rev. 65
Data: edición inline de registros: Superadmin puede clicar cualquier celda de la tabla Data para editarla. Tipo/Lote/Infra abren un select; Contractor resuelve nombre↔ID automáticamente; campos monetarios usan parseMoney. Enter confirma, Escape cancela. Guarda en DB y refresca dropdowns.
Rev. 64
Búsqueda rápida en desplegables de Lists: Input de búsqueda en cada filtro que filtra opciones al escribir. Cierre del dropdown sin perder el foco del input de búsqueda.
Rev. 63
Desplegables Lists cierran al clicar fuera: Listener global en document detecta clic fuera de cualquier .multiFilterBox[open], lo cierra y aplica los filtros automáticamente.
Rev. 62
Fix filtro Contractor en Lists: El desplegable se construía desde los IDs en los records, mostrando contratistas eliminados o renombrados. Ahora se construye desde DB.contractors filtrando solo los que tienen registros, con nombre siempre actualizado.
Rev. 61
Fix contratista en ventana Search: El modal de búsqueda mostraba el ID interno del contratista (ctr_xxx) en la columna Contractor/Desc. Corregido con getContractorName().
Rev. 60
Fix contratista en Lists: La columna Contractor en la tabla Lists mostraba el ID interno (ctr_xxx) en lugar del nombre. Corregido con getContractorName() tanto en la tabla como en la exportación Excel.
Rev. 59
Botones cloud restringidos a admin: Pull Cloud, Push Cloud, Force Push y Auto-sync solo visibles para admins. Eliminado botón Salir del cloudBar — queda únicamente el botón rojo en el header superior derecho.
Rev. 58
Botones cloud y Salir: Force Push visible solo para admins. Botón Salir rediseñado en rojo y más grande en header y cloudBar para que no pase desapercibido.
Rev. 57
Fix visibilidad System + login cloud: Usuarios 'user' ya no ven el grupo System (bug: el bucle forEach reestablecía display tras el ocultado individual). Login muestra badge verde con ✓ cuando el cloud está configurado. Badge de éxito incluye número y fecha de la última copia de seguridad.
Rev. 56
Ajustes de navegación y roles: Lists movida a Outputs. Usuarios sin rol admin no ven el grupo System. Usuarios readonly no ven Inputs ni System. KPIs de Prorate movidos arriba. Version History y Version Log muestran solo las 3 últimas entradas con botón desplegable para el resto.
Rev. 55
Menú lateral: Navegación rediseñada con sidebar izquierdo fijo. Ítems agrupados en Dashboard, Outputs (Summary, Invoice Summary, Cashflow, Prorate, Lists), Inputs (Budget, ACP, Contracts, Invoices, Saves) y System (Contractors, Data, Settings).
Rev. 54
Fix historial de versiones: El contador siempre mostraba #1 porque el PATCH a version_metadata fallaba silenciosamente cuando la fila no existía aún. Cambiado a POST con upsert (Prefer: resolution=merge-duplicates), que crea la fila si no existe o la actualiza si existe. Ordenación añade created_at.desc como criterio secundario.
Rev. 53
Fix definitivo autocomplete: La causa raíz era que JSON.stringify(nombre) generaba comillas dobles que rompían el atributo HTML onmousedown, invalidando el handler. Ahora solo se pasa el ID del contratista y el nombre se resuelve en JS desde DB.contractors.
Rev. 52
Fix autocomplete contractors: Dropdown cambiado a position:fixed con posición calculada desde getBoundingClientRect() — evita el clipping de overflow:auto del contenedor .content. Selección mediante onmousedown (dispara antes del blur).
Rev. 51
Contractors: Fix selección en dropdown de autocomplete (onfocusout + onclick en lugar de onblur + onmousedown). Nueva pantalla "Contractors" como tab independiente accesible a admins — CRUD de contratistas con campos Nombre, CIF/NIF y Lotes. Eliminado de Settings.
Rev. 50
Contractors: Catálogo centralizado de contratistas en DB.contractors (compartido entre proyectos). Autocomplete con sugerencias al escribir + "Create new contractor" en el formulario de contrato. CRUD completo en Settings. Nombres se resuelven automáticamente en Invoice Summary, Prorate, filtros y formularios de facturas/ahorros.
Rev. 49
Cashflow e Invoice Summary: Gráfico integrado dentro de la tabla de facturado mensual como fila de cabecera — alineación perfecta mes a mes garantizada. Invoice Summary: filas de contrato 10px, subtotales 13px, Grand Total 14px.
Rev. 48
Summary e Invoice Summary: columna Invoiced añadida al final de Summary. En Invoice Summary, texto de contratos reducido (11px) y Grand Total más grande (16px, negrita).
Rev. 47
Summary mejorado: filas coloreadas ya no se ponen blancas al hacer hover. Total Hard Costs aparece después de Contingencies y Total Soft Costs después de Insurances and Taxes. Fuente de filas de lotes reducida (11px, padding compacto).
Rev. 46
Cashflow auto-refresh y Gauss: Al editar Budget % o Ajuste % en Cashflow, la tabla se recalcula automáticamente al pulsar Enter o cambiar de celda. Distribución por defecto cambiada a campana de Gauss (μ=40% duración, σ=22% duración) según CONTART 2016. Botón "Apply Gauss Distribution" en Settings aplica la distribución a todos los lotes.
Rev. 45
Summary y Cashflow: KPIs de Summary reducidos (padding, altura y fuente). En Cashflow, la columna Ajuste % pasa a ser editable (se introduce el % y se calcula el € automáticamente). La columna Ajuste € queda como valor calculado.
Rev. 44
Copias de seguridad automáticas: Sistema de historial de versiones en Supabase con retención de 90 días. Cada push automáticamente genera una copia de seguridad. Admins pueden restaurar cualquier versión anterior con un clic. Interfaz mejorada con modal de confirmación y mensajes claros.
Rev. 43
Lists mejorada: nuevo filtrado multiselección por desplegable y exportación a Excel de los registros filtrados con el formato de tabla del libro de referencia.
Rev. 42
Perfil solo lectura activado: nueva modalidad `readonly` con acceso exclusivo a Dashboard, Summary, Invoice Summary, Cashflow, Prorate y Lists, sin edición ni subida de datos a Supabase.
Rev. 41.1
Nuevo perfil solo lectura: usuarios `readonly` con acceso exclusivo a Dashboard, Summary, Invoice Summary, Cashflow, Prorate y Lists. Sin edición, sin pantallas de introducción, sin Data/Settings y sin push/autosync a Supabase.
Rev. 41
Settings de proyectos simplificados: nuevo selector compacto para navegar proyecto a proyecto en Settings, con resumen rápido y edición enfocada del proyecto seleccionado. El log de versiones muestra solo las 3 últimas revisiones y el resto queda bajo un desplegable.
Rev. 40
KPIs rediseñados: 5 KPIs en orden Budget Actual, PEC Contratado, Coste Previsto Final, Riesgo vivo, Ratio Certificación. Eliminados CPI/SPI.
Rev. 39
Dashboard: Riesgo vivo = solo ACP neto. Gráfica 5 = donut de distribución del volumen contratado por lote.
Rev. 38
Dashboard ajustes KPIs y gráfica 2: CPF se compara con Budget (no con PEC); Riesgo vivo muestra ACP neto + sin contratar; gráfica 2 muestra Budget inicial vs Budget actual por lote (barras agrupadas horizontales).
Rev. 37
Gráfica 4 ACP entradas/salidas: la baja de una ACP (por delete directo o al crear OS) registra un movimiento negativo en lugar de borrar. La gráfica 4 muestra barras de entrada (ámbar) y salida (naranja) por mes. Gráfica 5 muestra solo Contratos+OS. Campo Date en formularios ACP y Contracts. Fix saveRecord mes YYYY-MM.
Rev. 35
PEDIDO PRORRATEO → lote automático: al seleccionar tipo PEDIDO PRORRATEO en el formulario de contrato, el lote se fija automáticamente a 99 PRORATE y queda bloqueado. Al cambiar a otro tipo se libera.
Rev. 34
Colaboración real multiusuario: Cloud Admin Account en Settings — el superadmin guarda sus credenciales Supabase para push/pull automático. Pre-fetch de proyectos al cargar (política anon en Supabase). Auto-pull del admin al login. Spinner e indicador cloud en login. Repoblación del selector de proyectos tras pull.
Rev. 33
Auto-sync tiempo real: polling cada 30 s, auto-push con debounce 4 s, badge LIVE, toasts no bloqueantes, botón toggle Auto-sync ON/OFF.
Rev. 32
Dashboard con Chart.js: 5 gráficos reales (Curva S, Desviación por capítulo, Donut comprometido, Extras acumulados, Cashflow mensual). Cabecera de proyecto bloqueada para usuarios no-admin.
Rev. 31
Fix importación multi-proyecto: la importación de backup JSON ya no reemplaza toda la base de datos. Solo actualiza los records (y packages si los hay) del proyecto activo. El resto de proyectos permanecen intactos.
Rev. 30
Estabilización multi-proyecto: eliminadas definiciones duplicadas de renderDashboard, añadido editor de distribución mensual de cashflow en Settings, botón Normalise Budget %, corrección de referencia cashflowSettingsEditor.
Rev. 29
Multi-project base: login project selector, per-project data isolation, active-project management from Settings and project-specific user validation.