feat(reporting): real AFCARS data extraction (collect + map + materialize child records)
Summary
Implement real AFCARS data extraction so POST /v1/reporting/afcars/generate produces a genuine, validated federal submission instead of an honest-but-empty draft stub.
Today (post-#762) generate_afcars creates an honest draft submission with record_count = 0 and cannot advance the federal workflow (there is no draft → validated path) or be transmitted (zero-record transmit gate). This issue makes generate actually collect, map, materialize, and validate AFCARS records — the AFCARS analogue of the NCANDS extraction trio (#654 (closed) row mapping, #655 (closed) collect+materialize, #656 (closed) TSV emitter).
Scope
- Collect foster-care data from craig-cases / craig-placement / craig-financial (the sources AFCARS reports on) for the reporting period, via the established service-to-service pattern (mirror
generate_ncands,services/craig-reporting/src/api/ncands.rs). - Map each child/placement to an AFCARS record per the federal AFCARS 2020 element set (mirror the NcandsChildRow mapping from #654 (closed); wire federal-mapping resolution per #583).
- Materialize the rows into a per-submission table idempotently (delete-then-insert on
submission_id, as NCANDS does), sorecord_count == COUNT(afcars_child_rows). - Validate field completeness →
validation_errors; only then set statusvalidated(restoring thedraft → validatedpath, which #762 (closed) intentionally left unbuilt). - Emit the real AFCARS flat file in
build_afcars_tsv(child records, not just the metadata row) — currently a header + one metadata row.
Acceptance criteria
-
generate_afcarsfetches + maps + materializes real child records;record_countreflects the materialized row count (not a hardcoded0). - A submission with records reaches
validated, then can movevalidated → reviewed → approved → transmitted; a zero-record period staysdraft(or surfaces validation errors) and remains non-transmittable. -
build_afcars_tsvemits one row per child record in the federal column layout. - The inline
// see #NNNreference ingenerate_afcarsis removed once extraction lands. - Restore end-to-end lifecycle integration coverage (generate-with-data → review → approve → transmit) that #762 (closed) could not keep while generate was a stub.
- Regression + property tests for the mapping (mirror the NCANDS test coverage).
References
- Blocks the honest-stub fix #762 (closed) (
fix(reporting): stop stamping zero-record AFCARS files "validated"). - Mirrors the NCANDS extraction trio: #654 (closed), #655 (closed), #656 (closed).
- Federal-mapping resolution: #583.
- Source: 2026-06-28 automated code-quality audit finding
reporting-afcars-hollow-stub-validated— the extraction half of that finding (the "stop lying" half shipped in #762 (closed)).