feat(sp24): expose PATIENT_LOOP_DEFAULT_INCLUDED constant in serialize_837

Promotes the default value of _build_subscriber_block(include_patient_loop=...)
from a hardcoded True (the broken IG-incorrect state that emitted HL*3 /
PAT*01 / NM1*QC for SBR02='18' claims) to a public module-level constant
PATIENT_LOOP_DEFAULT_INCLUDED = False (the IG-correct state).

Per X12 005010X222A1, the 2000C Patient Hierarchical Level MUST be absent
when SBR02 == '18' (Self-pay, the CO-Medicaid IHSS workflow). The previous
default of True emitted the forbidden 2000C block on every self-pay claim,
which Edifabric rejected with '2000C HL must be absent when 2000B SBR02 = "18"'.

Also adds __all__ to the module so PATIENT_LOOP_DEFAULT_INCLUDED is
documented as part of the public surface. The IG-correctness guard
(see cyclone.reissue.ig_correctness_check in the next commit) reads this
constant rather than inspect.signature'ing _build_subscriber_block —
that handle survives any future refactor of the helper.
This commit is contained in:
Nora
2026-07-08 22:27:21 -06:00
parent daf55d9a38
commit 9e422fdadb
+25 -1
View File
@@ -52,6 +52,14 @@ from types import SimpleNamespace
from cyclone.parsers.models import ClaimOutput
__all__ = [
"PATIENT_LOOP_DEFAULT_INCLUDED",
"SerializeError",
"serialize_837",
"serialize_837_for_resubmit",
"serialize_member_week_batch",
]
_SEG = "~"
_ELEM = "*"
_ISA_COMPONENT_SEPARATOR = ":"
@@ -64,6 +72,22 @@ class SerializeError(Exception):
"""Raised when a claim cannot be serialized."""
#: Default value for ``_build_subscriber_block(include_patient_loop=...)``.
#:
#: Per X12 005010X222A1, the 2000C Patient Hierarchical Level
#: (``HL*3 → PAT → NM1*QC``) is REQUIRED only when Patient != Subscriber
#: (i.e. ``SBR02 != "18"``). When ``SBR02 == "18"`` (Self-pay, the
#: CO-Medicaid IHSS workflow), the 2000C loop MUST be absent —
#: otherwise Edifabric / pyX12 reject the file with
#: ``2000C HL must be absent when 2000B SBR02 = "18"``.
#:
#: The regression test
#: ``tests/test_serialize_837.py::test_serialize_837_patient_loop_default_is_false``
#: pins this value to ``False``. Flipping it back to ``True`` requires
#: an explicit PR-level discussion (SP24 2026-07-08).
PATIENT_LOOP_DEFAULT_INCLUDED: bool = False
# ---------------------------------------------------------------------------
# Envelope helpers
# ---------------------------------------------------------------------------
@@ -502,7 +526,7 @@ def _build_subscriber_block(
subscriber,
claim_filing_indicator_code: str | None,
payer=None,
include_patient_loop: bool = True,
include_patient_loop: bool = PATIENT_LOOP_DEFAULT_INCLUDED,
) -> list[str]:
"""Loop 2000B (HL*2) → SBR → 2010BA (NM1*IL) → 2010BB (NM1*PR).