From bb35ecd40cf6082de17b611411b5e5444c25742e Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 19 Jun 2026 15:40:09 -0600 Subject: [PATCH] feat(backend): PayerConfig with co_medicaid and generic factories --- backend/src/cyclone/parsers/payer.py | 59 ++++++++++++++++++++++++++++ backend/tests/test_payer.py | 22 +++++++++++ 2 files changed, 81 insertions(+) create mode 100644 backend/src/cyclone/parsers/payer.py create mode 100644 backend/tests/test_payer.py diff --git a/backend/src/cyclone/parsers/payer.py b/backend/src/cyclone/parsers/payer.py new file mode 100644 index 0000000..ea30534 --- /dev/null +++ b/backend/src/cyclone/parsers/payer.py @@ -0,0 +1,59 @@ +"""Payer-specific configuration for the 837P parser. + +The same parser module can ingest 837P files from any payer, but the +validation rules differ. ``PayerConfig`` carries the differences so +``validator.validate(claim, config)`` can be payer-aware without branching +in the parser. +""" + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class PayerConfig(BaseModel): + model_config = ConfigDict(frozen=True) + + name: str + sbr09_claim_filing: str = "MC" + allowed_claim_frequencies: set[int] = Field(default_factory=lambda: {1, 7, 8}) + require_ref_g1_for_adjustments: bool = False + allowed_bht06: set[str] = Field(default_factory=lambda: {"CH"}) + payer_id: str = "" + payer_name: str = "" + no_patient_loop: bool = False + encounter_claim_in_same_batch: bool = False + + @classmethod + def co_medicaid(cls) -> "PayerConfig": + """Defaults for Colorado Medical Assistance Program (FFS). + + Source: ``docs/companionguides/837p.md``. + """ + return cls( + name="Colorado Medical Assistance Program", + sbr09_claim_filing="MC", + allowed_claim_frequencies={1, 7, 8}, + # Lenient in v1 — see spec §9 R031. + require_ref_g1_for_adjustments=False, + allowed_bht06={"CH"}, + payer_id="SKCO0", + payer_name="COHCPF", + no_patient_loop=True, + encounter_claim_in_same_batch=False, + ) + + @classmethod + def generic_837p(cls) -> "PayerConfig": + """Relaxed defaults for an unknown payer. Structural rules only.""" + return cls( + name="Generic 837P", + sbr09_claim_filing="", + allowed_claim_frequencies={1, 2, 3, 4, 5, 6, 7, 8, 9}, + require_ref_g1_for_adjustments=False, + allowed_bht06={"CH", "RP"}, + payer_id="", + payer_name="", + no_patient_loop=False, + encounter_claim_in_same_batch=True, + ) diff --git a/backend/tests/test_payer.py b/backend/tests/test_payer.py new file mode 100644 index 0000000..814eff2 --- /dev/null +++ b/backend/tests/test_payer.py @@ -0,0 +1,22 @@ +from cyclone.parsers.payer import PayerConfig + + +def test_co_medicaid_defaults(): + cfg = PayerConfig.co_medicaid() + assert cfg.name == "Colorado Medical Assistance Program" + assert cfg.sbr09_claim_filing == "MC" + assert cfg.allowed_claim_frequencies == {1, 7, 8} + assert cfg.require_ref_g1_for_adjustments is False # lenient in v1 + assert cfg.allowed_bht06 == {"CH"} + assert cfg.payer_id == "SKCO0" + assert cfg.payer_name == "COHCPF" + assert cfg.no_patient_loop is True + assert cfg.encounter_claim_in_same_batch is False + + +def test_generic_837p_defaults_are_relaxed(): + cfg = PayerConfig.generic_837p() + assert cfg.name == "Generic 837P" + assert cfg.allowed_claim_frequencies == {1, 2, 3, 4, 5, 6, 7, 8, 9} + assert cfg.no_patient_loop is False + assert cfg.payer_id == "" # unset