fix(sp41): tighten pipeline_a write_files contract + cleanup imports
This commit is contained in:
@@ -19,6 +19,8 @@ from cyclone.rebill.reconcile import ReconcileOutcome, OutcomeCategory
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class RebillClaim:
|
class RebillClaim:
|
||||||
|
"""Pre-emission 837P shape for Pipeline A. The serializer adapter (Task 12) converts this to a ClaimOutput for serialize_837."""
|
||||||
|
|
||||||
claim_id: str # reuses original claim_submit_id
|
claim_id: str # reuses original claim_submit_id
|
||||||
member_id: str
|
member_id: str
|
||||||
procedure: str
|
procedure: str
|
||||||
@@ -35,9 +37,10 @@ def build_pipeline_a_claims(
|
|||||||
carc_decisions: list[CarcDecision],
|
carc_decisions: list[CarcDecision],
|
||||||
cas_reasons_per_visit: list[tuple[str, ...]],
|
cas_reasons_per_visit: list[tuple[str, ...]],
|
||||||
) -> list[RebillClaim]:
|
) -> list[RebillClaim]:
|
||||||
"""One input list per visit; align by index."""
|
"""Zip three parallel per-visit lists by index; drop PAID/NOT_IN_835 outcomes and EXCLUDED CARC decisions; emit one frequency-7 RebillClaim per survivor."""
|
||||||
out: list[RebillClaim] = []
|
out: list[RebillClaim] = []
|
||||||
for vo, carc, reasons in zip(visit_outcomes, carc_decisions, cas_reasons_per_visit):
|
# strict=True: all three lists must be the same length; a mismatch is a contract bug, fail loud.
|
||||||
|
for vo, carc, reasons in zip(visit_outcomes, carc_decisions, cas_reasons_per_visit, strict=True):
|
||||||
if vo.category not in (OutcomeCategory.DENIED, OutcomeCategory.PARTIAL):
|
if vo.category not in (OutcomeCategory.DENIED, OutcomeCategory.PARTIAL):
|
||||||
continue
|
continue
|
||||||
if carc == CarcDecision.EXCLUDED:
|
if carc == CarcDecision.EXCLUDED:
|
||||||
@@ -50,7 +53,7 @@ def build_pipeline_a_claims(
|
|||||||
charge=vo.visit.billed,
|
charge=vo.visit.billed,
|
||||||
frequency_code="7",
|
frequency_code="7",
|
||||||
needs_review=(carc == CarcDecision.REVIEW),
|
needs_review=(carc == CarcDecision.REVIEW),
|
||||||
original_carc_reasons=tuple(reasons),
|
original_carc_reasons=reasons,
|
||||||
))
|
))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@@ -58,14 +61,16 @@ def build_pipeline_a_claims(
|
|||||||
def write_pipeline_a_files(
|
def write_pipeline_a_files(
|
||||||
claims: list[RebillClaim],
|
claims: list[RebillClaim],
|
||||||
out_dir: Path,
|
out_dir: Path,
|
||||||
serialize_837_fn, # injected: cyclone.parsers.serialize_837.serialize_837
|
serialize_837_fn, # injected: callable[[RebillClaim], str] (orchestrator-supplied adapter)
|
||||||
tpid: str,
|
tpid: str,
|
||||||
) -> list[Path]:
|
) -> list[Path]:
|
||||||
"""Write one 837P per claim, using HCPF-spec filenames via build_outbound_filename.
|
"""Write one 837P per claim, using HCPF-spec filenames via build_outbound_filename.
|
||||||
|
|
||||||
`serialize_837_fn(claim)` returns the X12 byte string. The filename is
|
`serialize_837_fn(claim)` returns the X12 string. The 837P envelope is
|
||||||
|
pure ASCII so we write it as text. The filename is
|
||||||
`cyclone.edi.filenames.build_outbound_filename(tpid, '837P')`.
|
`cyclone.edi.filenames.build_outbound_filename(tpid, '837P')`.
|
||||||
"""
|
"""
|
||||||
|
# Lazy: keep cyclone.rebill.pipeline_a importable without zoneinfo from filenames.
|
||||||
from cyclone.edi.filenames import build_outbound_filename
|
from cyclone.edi.filenames import build_outbound_filename
|
||||||
|
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -74,6 +79,6 @@ def write_pipeline_a_files(
|
|||||||
body = serialize_837_fn(c)
|
body = serialize_837_fn(c)
|
||||||
fname = build_outbound_filename(tpid, "837P")
|
fname = build_outbound_filename(tpid, "837P")
|
||||||
path = out_dir / fname
|
path = out_dir / fname
|
||||||
path.write_bytes(body)
|
path.write_text(body, encoding="ascii")
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
return paths
|
return paths
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
"""Pipeline A: denied/partial visits become frequency-7 replacement claims."""
|
"""Pipeline A: denied/partial visits become frequency-7 replacement claims."""
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
|
||||||
from cyclone.rebill.pipeline_a import (
|
from cyclone.rebill.pipeline_a import (
|
||||||
RebillClaim,
|
|
||||||
build_pipeline_a_claims,
|
build_pipeline_a_claims,
|
||||||
write_pipeline_a_files,
|
write_pipeline_a_files,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user