ADM-001: Medios y Secciones (fundacional) #15

Merged
dmolinari merged 9 commits from feature/ADM-001 into main 2026-04-17 14:37:15 +00:00
Owner

Summary

Entrega el catálogo fundacional Medio + Seccion — base del resto del sistema comercial (20+ UDTs downstream bloqueadas hasta esta UDT). Incluye CRUD admin completo en backend + frontend con auditoría obligatoria y Temporal Tables (retention 10 años).

  • BD: V011 (DDL + temporal + permiso nuevo) + V012 (seed ELDIA/ELPLATA) + rollbacks idempotentes.
  • Backend: entities immutables, Clean Architecture (Domain/Application/Infrastructure/Api), Dapper, FluentValidation, Auditoría vía IAuditLogger dentro de TransactionScope fail-closed. Endpoints bajo /api/v1/admin/{medios,secciones} con [RequirePermission] (administracion:medios:gestionar ya existía en V005; administracion:secciones:gestionar es nuevo en V011).
  • Frontend: features/medios/ + features/secciones/ con TanStack Query + shadcn. Rutas admin + gating por permiso en sidebar.

Alcance y decisiones

OQ / Decisión Resuelto
OQ-ADM-001 Relación Medio↔Seccion 1:N (Seccion.MedioId NOT NULL) — M:N difrerida a ADM-003 con tabla auxiliar
Enum TipoMedio Diario/Radio/Web/Poster (honrando 3.2.1; "Digital" del doc 2.10 corregido)
PlataformaEmpresaId INT NULL sin FK — INT-003 agregará FK vía ALTER
Delete Soft (flag Activo + endpoints deactivate/reactivate idempotentes)
Seed Migración separada V012 idempotente (MERGE)
Uniqueness UQ_Medio_Codigo global; UQ_Seccion_MedioId_Codigo compuesto
Seccion.Tipo VARCHAR(20) CHECK IN ('clasificados','notables','suplementos')
Permisos Granulares: administracion:medios:gestionar (V005) + administracion:secciones:gestionar (V011)

Scope OUT (explícito — previene creep)

  • Config avanzada de Sección (par/impar, suplementos, cupos) → ADM-003
  • Puntos de Venta → ADM-008
  • Tablas fiscales → ADM-009
  • Tarifarios → PRC-*/ADM-002
  • IMAC mapping (uso de PlataformaEmpresaId) → INT-003

Auditoría y Temporal Tables

  • Catálogo de Entidades Auditables en 2.5 Auditoría.md: Medio y Seccion
  • Actions nuevas: medio.{create,update,deactivate,reactivate} y seccion.{...}
  • Cada mutación corre dentro del mismo TransactionScope que el IAuditLogger.LogAsync → fail-closed
  • Integration tests validan: (1) row en AuditEvent, (2) row en Medio_History/Seccion_History tras update

Commits (8)

  • ff7d898 feat(db): V011 + V012 migrations + temporal
  • bb98dbf feat(domain): entities + 4 exceptions
  • f672de7 feat(medios,secciones): application layer + handlers TDD
  • a1a8e6e fix(tests): realign Application.Tests con seed 22 permisos
  • 2f0da2d feat(infra): MedioRepository + SeccionRepository
  • a6f4011 fix(tests): resolve regresiones Api.Tests fixture
  • 13480ad feat(api): Controllers + ExceptionFilter mappings
  • 6b946f6 feat(web): Medios + Secciones UI + hooks + routing

Test plan

  • dotnet test tests/SIGCM2.Application.Tests458 passed / 0 failed
  • dotnet test tests/SIGCM2.Api.Tests166 passed / 0 failed
  • cd src/web && npx vitest run207 passed / 0 failed (38 test files)
  • Total: 831 tests / 0 failures
  • Migrations V011/V012 idempotentes verificadas en SIGCM2_Test y SIGCM2 (re-ejecutadas).
  • Seeds ELDIA y ELPLATA sembradas (MERGE WHERE NOT EXISTS).
  • Rollbacks V011/V012 validados estructuralmente.

Verificación post-merge

  • STATUS.md marca [x] ADM-001 (Obsidian — no viene en el PR, gitignored).
  • 2.10 y 2.5 actualizados (Obsidian — no viene en el PR).
  • Smoke test prod-like: login admin → navegar a /admin/medios y /admin/secciones → crear + editar + deactivate → verificar rows en AuditEvent y *_History.

Downstream desbloqueado

ADM-003, ADM-008, ADM-009, CAT-001..006, PRD-002, PRC-003/004/008, VTA-001, FAC-*, INT-001/002/003/004.

## Summary Entrega el catálogo fundacional **Medio** + **Seccion** — base del resto del sistema comercial (20+ UDTs downstream bloqueadas hasta esta UDT). Incluye CRUD admin completo en backend + frontend con **auditoría obligatoria** y **Temporal Tables** (retention 10 años). - **BD**: V011 (DDL + temporal + permiso nuevo) + V012 (seed ELDIA/ELPLATA) + rollbacks idempotentes. - **Backend**: entities immutables, Clean Architecture (Domain/Application/Infrastructure/Api), Dapper, FluentValidation, Auditoría vía `IAuditLogger` dentro de `TransactionScope` fail-closed. Endpoints bajo `/api/v1/admin/{medios,secciones}` con `[RequirePermission]` (`administracion:medios:gestionar` ya existía en V005; `administracion:secciones:gestionar` es nuevo en V011). - **Frontend**: `features/medios/` + `features/secciones/` con TanStack Query + shadcn. Rutas admin + gating por permiso en sidebar. ## Alcance y decisiones | OQ / Decisión | Resuelto | |---|---| | OQ-ADM-001 Relación Medio↔Seccion | 1:N (`Seccion.MedioId NOT NULL`) — M:N difrerida a ADM-003 con tabla auxiliar | | Enum `TipoMedio` | Diario/Radio/Web/Poster (honrando 3.2.1; "Digital" del doc 2.10 corregido) | | `PlataformaEmpresaId` | `INT NULL` sin FK — INT-003 agregará FK vía ALTER | | Delete | **Soft** (flag `Activo` + endpoints `deactivate`/`reactivate` idempotentes) | | Seed | Migración separada V012 idempotente (MERGE) | | Uniqueness | `UQ_Medio_Codigo` global; `UQ_Seccion_MedioId_Codigo` compuesto | | `Seccion.Tipo` | `VARCHAR(20) CHECK IN ('clasificados','notables','suplementos')` | | Permisos | Granulares: `administracion:medios:gestionar` (V005) + `administracion:secciones:gestionar` (V011) | ## Scope OUT (explícito — previene creep) - Config avanzada de Sección (par/impar, suplementos, cupos) → **ADM-003** - Puntos de Venta → **ADM-008** - Tablas fiscales → **ADM-009** - Tarifarios → **PRC-*/ADM-002** - IMAC mapping (uso de `PlataformaEmpresaId`) → **INT-003** ## Auditoría y Temporal Tables - Catálogo de Entidades Auditables en `2.5 Auditoría.md`: Medio y Seccion ⏳ → ✅ - Actions nuevas: `medio.{create,update,deactivate,reactivate}` y `seccion.{...}` - Cada mutación corre dentro del mismo `TransactionScope` que el `IAuditLogger.LogAsync` → fail-closed - Integration tests validan: (1) row en `AuditEvent`, (2) row en `Medio_History`/`Seccion_History` tras update ## Commits (8) - `ff7d898` feat(db): V011 + V012 migrations + temporal - `bb98dbf` feat(domain): entities + 4 exceptions - `f672de7` feat(medios,secciones): application layer + handlers TDD - `a1a8e6e` fix(tests): realign Application.Tests con seed 22 permisos - `2f0da2d` feat(infra): MedioRepository + SeccionRepository - `a6f4011` fix(tests): resolve regresiones Api.Tests fixture - `13480ad` feat(api): Controllers + ExceptionFilter mappings - `6b946f6` feat(web): Medios + Secciones UI + hooks + routing ## Test plan - [x] `dotnet test tests/SIGCM2.Application.Tests` — **458 passed / 0 failed** - [x] `dotnet test tests/SIGCM2.Api.Tests` — **166 passed / 0 failed** - [x] `cd src/web && npx vitest run` — **207 passed / 0 failed** (38 test files) - [x] **Total: 831 tests / 0 failures** - [x] Migrations V011/V012 idempotentes verificadas en SIGCM2_Test y SIGCM2 (re-ejecutadas). - [x] Seeds ELDIA y ELPLATA sembradas (MERGE `WHERE NOT EXISTS`). - [x] Rollbacks V011/V012 validados estructuralmente. ## Verificación post-merge - [ ] STATUS.md marca `[x] ADM-001` (Obsidian — no viene en el PR, gitignored). - [ ] 2.10 y 2.5 actualizados (Obsidian — no viene en el PR). - [ ] Smoke test prod-like: login admin → navegar a `/admin/medios` y `/admin/secciones` → crear + editar + deactivate → verificar rows en `AuditEvent` y `*_History`. ## Downstream desbloqueado ADM-003, ADM-008, ADM-009, CAT-001..006, PRD-002, PRC-003/004/008, VTA-001, FAC-*, INT-001/002/003/004.
dmolinari added 8 commits 2026-04-16 22:30:44 +00:00
V011 crea dbo.Medio y dbo.Seccion con SYSTEM_VERSIONING ON (retention 10
anios) y PAGE compression en history; siembra el permiso
'administracion:secciones:gestionar' y lo asigna a rol admin. El permiso
'administracion:medios:gestionar' ya existia desde V005.

V012 siembra Medios fundacionales ELDIA y ELPLATA (MERGE idempotente).

Rollbacks V011/V012 validados estructuralmente; aplicacion y
reaplicacion verificadas en SIGCM2_Test y SIGCM2. Fixture de tests
actualizado: EnsureV011SchemaAsync, SeedMediosCanonicalAsync, ignora
Medio_History y Seccion_History en Respawner.
Entities sealed immutable con factory ForCreation + copy-with methods
WithUpdatedProfile/WithActivo (Codigo inmutable en Medio; MedioId y
Codigo inmutables en Seccion — enforzado en Validators en B4).

Exceptions: MedioCodigoDuplicado (UQ global), SeccionCodigoDuplicadoEnMedio
(UQ compuesto), MedioNotFound, SeccionNotFound. Todas heredan de
DomainException.
- IMedioRepository, ISeccionRepository interfaces
- MediosQuery, SeccionesQuery common records
- TipoSeccion static AllowedTipos helper
- Medios: 6 use cases (Create/Update/Deactivate/Reactivate/List/GetById) with validators, handlers and DTOs
- Secciones: 6 use cases mirroring Medios; Create validates MedioId active via IMedioRepository
- 52 unit tests (xUnit + NSubstitute) all green; audit LogAsync asserted per mutating handler
- DI registrations for all 12 handlers and validators auto-scanned via AddValidatorsFromAssemblyContaining
- Update hardcoded permiso count from 21 → 22 in AuthControllerTests and
  PermisosEndpointTests after V011 added 'administracion:secciones:gestionar'
- The TestSupport SqlTestFixture already had Medio_History/Seccion_History in
  TablesToIgnore; tests were failing due to stale binaries (needed rebuild)
- POST/GET/PUT + deactivate/reactivate endpoints for /api/v1/admin/medios
- POST/GET/PUT + deactivate/reactivate endpoints for /api/v1/admin/secciones
- ExceptionFilter: add Medio/Seccion 404+409 mappings after RolInUseException
- Integration tests: 19 scenarios covering 401/403/201/404/409/idempotency/AuditEvent
- All 166 Api.Tests + 458 Application.Tests passing
- API clients + TanStack Query hooks for medios and secciones
- CRUD pages: List, Create, Edit, Detail for both entities
- Components: MediosTable, MedioForm, DeactivateMedioModal,
  SeccionesTable, SeccionForm, DeactivateSeccionModal, SeccionesFilters
- TipoMedio enum (int→label) and TipoSeccion display helpers
- CanPerform permission gates: administracion:medios:gestionar,
  administracion:secciones:gestionar
- Routes /admin/medios/** and /admin/secciones/** in router.tsx
- Sidebar items (Newspaper + Columns3 icons) with requiredPermission
- Vitest+RTL+MSW tests: 11 test files, 38 new cases — 207 pass total
dmolinari added 1 commit 2026-04-17 13:13:28 +00:00
Reemplaza 13 <select>/<option> nativos en 8 archivos por el componente
shadcn Select (Radix UI). Los selects nativos ignoraban los tokens del
design system en dark mode, causando texto invisible. Se agrega mock de
pointer capture APIs en test setup para compatibilidad de Radix con jsdom.
dmolinari merged commit 91d353655d into main 2026-04-17 14:37:15 +00:00
dmolinari deleted branch feature/ADM-001 2026-04-17 14:37:15 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dmolinari/SIG-CM2.0#15