ADR-025 — Acta con premio original congelado + separación del estado de entrega; plazo visible; paginación de clientes¶
Status: accepted (2026-08-23; reabierto por judgment-day — CRIT-2, #6, #7)
Context (CRIT-2): El acta garantiza reproducibilidad del sorteo (seed+lista+algo →
mismos ganadores), pero su export incluye premio_unidad_id y estado_entrega, que MUTAN
con la operación (entrega, devolución, pase a suplente). Dos exports del mismo acta muestran
premios/estados distintos, confundiendo el artefacto legal (lo sorteado) con el estado
operativo (lo entregado). El sorteo en sí es inmutable; la entrega evoluciona.
Decision (CRIT-2):
resultado_sorteo gana premio_unidad_id_original (BIGINT nullable), seteado UNA VEZ al
ejecutar (SorteoService.persistirResultados) y NUNCA modificado por las operaciones de
entrega. Migración V10: agrega la columna y la backfillea con el premio_unidad_id actual
para actas ya existentes.
El export del acta se parte en dos secciones EXPLÍCITAS:
Acta del sorteo (INMUTABLE) — fecha_ejecucion, seed, algo_version, hash_lista,
cantidad_participantes, y por cada posición: orden, tipo, chance/cliente, y el
premio sorteado originalmente (premio_unidad_id_original). Reproducible: matchea
hash_lista y reproducir().
Estado de entrega (snapshot, con exportado_en) — por cada resultado: premio actual,
estado_entrega, datos de entrega. Claramente rotulado como estado al momento del export.
Dos exports en momentos distintos: la sección 1 es idéntica; la 2 puede diferir.
Decision (#6 — plazo de retiro): NO se automatiza (un scheduler violaría ADR-009 y podría
despojar a un ganador legítimo). Se VISIBILIZA: la vista de ganadores expone plazo_vencido
(días desde el primer contacto vs campania.plazo_retiro_dias); registrarEntrega sobre un
resultado con plazo vencido exige un flag de confirmación explícito (no bloquea, advierte).
Decision (#7 — clientes): la búsqueda del padrón exige un término mínimo (query vacío no
materializa todo el padrón) y se pagina server-side (Pageable, tope por página). Evita
cargar decenas de miles de filas al abrir la pantalla en una campaña con volumen.
Consequences: El acta protege su valor legal (lo sorteado nunca cambia) sin perder la
trazabilidad operativa (estado de entrega como snapshot). El plazo queda como decisión
informada del operador. La pantalla de clientes escala con el padrón.
[x] AC-44 — premio_unidad_id_original congelado al ejecutar; el export separa "acta
(inmutable)" de "estado de entrega (snapshot)". Tras una devolución, dos exports: la
sección de acta es idéntica (hash estable, reproduce() matchea), la de entrega difiere.
[x] AC-45 — Ganadores expone plazo_vencido (días desde primer contacto vs plazo);
registrarEntrega con plazo vencido sin confirmación → advertencia/409; con confirmación
explícita → procede. Nunca auto-transiciona a NO_OTORGADO.
[x] AC-46 — Búsqueda de clientes: query vacío no carga el padrón completo (400/paginado);
respuesta paginada (page/size/total). Test con volumen confirma que no materializa todo.