feat(sp40): 837P serializer always emits PER-02/03/04 + SBR-09
Edifabric /v2/x12/validate rejects the SP39-regenerated 837P files
with 'Element PER-03 is required / Element PER-04 is required /
Element SBR-09 is required'. Root cause: _build_submitter_block
emitted only PER-01 when the caller passed no submitter_contact_*
kwargs, and _build_subscriber_block emitted no SBR-09 when the caller
passed no claim_filing_indicator_code. SP33-era callers and the SP39
regen script both bypass the kwargs.
Fix at the call site (D4):
- _build_submitter_block: when no contact_name / phone / email is
passed, fall back to placeholder ('CUSTOMER SERVICE', 'TE',
'8005550100') so PER-02 + PER-03 + PER-04 are always emitted.
Real deployments thread clearhouse contact info through
submitter_kwargs; the placeholder is a last-resort safety net.
- _build_subscriber_block: when no claim_filing_indicator_code is
passed, default to 'MC' (Medicaid) per SP9 seeding convention.
Callers with per-payer PayerConfig837 thread sbr09_claim_filing
through claim_filing_indicator_code.
3 regression tests in test_serialize_837.py:
- PER always emits Name + Comm-Qualifier + Comm-Number
- SBR always emits SBR-09 default 'MC'
- Explicit claim_filing_indicator_code kwarg wins over default
This commit is contained in:
@@ -682,4 +682,59 @@ def test_2010bb_preserves_foreign_payer_id_verbatim():
|
||||
assert parts[9] == "OTHER_PAYER"
|
||||
assert parts[3] == "OTHER PAYER NAME"
|
||||
# No substitution log for a foreign payer id
|
||||
assert "SP39" not in log_stream.getvalue()
|
||||
assert "SP39" not in log_stream.getvalue()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SP40: PER-02/03/04 + SBR-09 must always be emitted (Edifabric rejects
|
||||
# the bare-PER / no-SBR09 shapes). The serializer fall-back fills them
|
||||
# with safe placeholders when the caller passes no kwargs.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_per_segment_emits_name_and_phone_qualifier_when_no_contact():
|
||||
"""SP40: without any submitter_contact_* kwargs, the submitter
|
||||
block must still produce a PER segment with at least PER-02 (Name)
|
||||
and a PER-03 (Communication Number Qualifier) + PER-04 (Number)
|
||||
pair. Edifabric rejects PER*IC alone."""
|
||||
claim = _load_claim()
|
||||
text = serialize_837_for_resubmit(claim, interchange_index=42)
|
||||
per_line = next(s for s in text.split("~") if s.startswith("PER"))
|
||||
parts = per_line.split("*")
|
||||
# PER*IC*<Name>*<Qual>*<Number>
|
||||
assert parts[1] == "IC"
|
||||
assert parts[2], f"PER-02 (Name) is required, got empty; line={per_line!r}"
|
||||
assert parts[3] in {"TE", "EM", "FX"}, (
|
||||
f"PER-03 must be a Communication Number Qualifier (TE/EM/FX); "
|
||||
f"got {parts[3]!r}; line={per_line!r}"
|
||||
)
|
||||
assert parts[4], f"PER-04 (Number) is required, got empty; line={per_line!r}"
|
||||
|
||||
|
||||
def test_sbr_segment_emits_claim_filing_indicator_default_mc():
|
||||
"""SP40: SBR-09 (Claim Filing Indicator Code) must always be
|
||||
emitted. Default is 'MC' (Medicaid) when the caller passes no
|
||||
claim_filing_indicator_code kwarg. Edifabric rejects the bare
|
||||
SBR*P*18******* shape."""
|
||||
claim = _load_claim()
|
||||
text = serialize_837_for_resubmit(claim, interchange_index=42)
|
||||
sbr_line = next(s for s in text.split("~") if s.startswith("SBR"))
|
||||
parts = sbr_line.split("*")
|
||||
# SBR*P*18*******MC → index 9 is SBR-09
|
||||
assert len(parts) >= 10, f"SBR must have 10 elements (with SBR-09), got {len(parts)}: {sbr_line!r}"
|
||||
assert parts[9] == "MC", (
|
||||
f"SBR-09 must default to 'MC' (Medicaid), got {parts[9]!r}; line={sbr_line!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_sbr_segment_respects_explicit_claim_filing_indicator():
|
||||
"""SP40: explicit claim_filing_indicator_code kwargs win over the
|
||||
default — preserves the existing caller-facing behavior for
|
||||
non-CO payers (e.g. '16' for Medicare Part B)."""
|
||||
claim = _load_claim()
|
||||
text = serialize_837_for_resubmit(
|
||||
claim, interchange_index=42,
|
||||
claim_filing_indicator_code="16",
|
||||
)
|
||||
sbr_line = next(s for s in text.split("~") if s.startswith("SBR"))
|
||||
parts = sbr_line.split("*")
|
||||
assert parts[9] == "16", f"SBR-09 should reflect explicit kwarg, got {parts[9]!r}"
|
||||
|
||||
Reference in New Issue
Block a user