Skip to content

[FEATURE] Backfill de supplyId en la carga suelta de expediciones (shipments.items) #317

Description

@vgpastor

Seguimiento de #226 (PR #314) · Parte de la épica #228

Problema o valor

El backfill de soft-links supplyId (#226) cubre las 4 tablas relacionales con columna supply_id (need_items, offer_items, resource_items, donation_intake_lines) y las líneas jsonb de containers.lines. En la auditoría de cierre se detectó que shipments.items es el único almacén de SupplyLine con supplyId que quedó fuera: la carga suelta de una expedición (logistics/infrastructure/drizzle/schema.ts, jsonb items) persiste líneas canónicas con supplyId, pero el backfill no las recorre. Las expediciones legacy con carga suelta quedan sin enlazar al catálogo y fuera de la agregación/previsión.

Propuesta

Añadir shipment_items como sexta fuente del backfill existente:

  • Nuevo literal en SupplyLineSource (domain/ports/supply-link-backfill.repository.ts).
  • En DrizzleSupplyLinkBackfillRepository: mismo tratamiento que containers.lines (jsonb) — listUnlinked con filtro EXISTS sobre jsonb_array_elements, y relink read-modify-write con SELECT … FOR UPDATE reutilizando relinkContainerLines (o extrayendo el helper a nombre neutro).
  • El caso de uso BackfillSupplyLinks no cambia: es agnóstico de la fuente.

Decidir si tiene sentido excluir expediciones en estados terminales o enlazarlas igualmente (en #226 se decidió enlazar también contenedores sellados: el soft-link es metadato de catálogo, no altera el contenido declarado — probablemente aplique el mismo criterio).

Alcance mínimo

Solo la fuente nueva en puerto + adapter + tests (unit del caso de uso con la fuente nueva e int-spec del jsonb de shipments). Sin cambios de endpoint ni DTO (la fuente aparece sola en sources del informe; regenerar pnpm gen:api por el enum del DTO).

Criterios de aceptación

  • GET /admin/supplies/backfill lista los textos sin enlazar de shipments.items con su recuento.
  • POST /admin/supplies/backfill enlaza las líneas de carga suelta que casan (exacto/alias) y respeta la idempotencia (supplyId ya fijado nunca se toca).
  • Escrituras concurrentes del agregado Shipment no pierden líneas (mismo patrón FOR UPDATE + filtro EXISTS que containers).
  • Int-spec contra Postgres real cubriendo relink del jsonb y re-ejecución a 0.

Dependencias o riesgos

Depende de #226 (mergeado en 122544d). Riesgo compartido con containers: el agregado Shipment persiste su jsonb completo en save() sin bloqueo optimista, así que un save() que leyó antes del commit del backfill puede revertir los supplyId recién enlazados (solo el metadato; recuperable re-ejecutando). El fix de raíz (versionado optimista de los agregados con jsonb) queda fuera de alcance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2area:dataIngesta, taxonomia y datos de recursosfeature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions