Antes el poll de 15s llamaba render() y reconstruía toda la tbody,
borrando el modo edición en filas existentes (el guard isEditing solo
cubría filas nuevas).
Ahora:
* loadAll() detecta si hay edición en curso (isEditing || editingRows.size)
y guarda los datos en allRows sin rerenderizar.
* Muestra "Edición en curso · refresco pausado" en el sync-status.
* maybeFlushRefresh() aplica los cambios remotos pendientes al salir
del modo edición (cancelar o guardar la última fila).
* saveExistingRow ya llama a render() vía saveRow, así que limpia la
bandera explícitamente.
* SQL: nueva migración 003 que añade `entrada VARCHAR(13)` después de
`estatus` en `escrituras_tramites`. Aplicada en producción.
* API: INSERT/UPDATE persisten el campo; auditoría lo registra con
label "Entrada" en el historial.
* Frontend:
- Nueva columna en el header entre Estatus y Últ. Actualización
- Input con `pattern=^\d{1,8}/\d{4}$`, `maxlength=13`, placeholder
"########/####" y validación nativa del navegador
- newRow() y duplicateRow() incluyen `entrada:''` por defecto
- exportHeaders / rowToArray / Excel widths actualizados
- pdfCols renumerado para incluir Entrada en el PDF
Setup completo del portal interno donna.aal-law.com:
* Landing público con OAuth login + grid de apps autenticado
* Flujo OAuth2 contra Mailcow (mail.aal-law.com)
* Sesiones SSO compartidas en el dominio (cookie donna_session, 8h)
* Whitelist de dominios aal-law.com / aalnlaw.com
* App "Control de Escrituras" migrada desde ZH-Consultores/control-escrituras
(mismo schema, mismo frontend, ahora con shared Auth/Db/Config libs)
* Migraciones SQL para users, escrituras_tramites, escrituras_historial
* Caddy on :80 con rewrite extless → .php
* Backup diario MariaDB (systemd timer + script) con retención 14d
* Documentación de arquitectura en README + infra files en docs/infra