feat(backend): structural + CO Medicaid validation rules
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from cyclone.parsers.models import (
|
||||
Address,
|
||||
BillingProvider,
|
||||
ClaimHeader,
|
||||
ClaimOutput,
|
||||
Diagnosis,
|
||||
Envelope,
|
||||
Payer,
|
||||
Procedure,
|
||||
ServiceLine,
|
||||
Subscriber,
|
||||
ValidationReport,
|
||||
)
|
||||
from cyclone.parsers.payer import PayerConfig
|
||||
from cyclone.parsers.validator import validate
|
||||
|
||||
|
||||
def _build_claim(**overrides) -> ClaimOutput:
|
||||
"""Build a passing claim for tests; override fields to break specific rules."""
|
||||
base = dict(
|
||||
claim_id="C1",
|
||||
control_number="991102977",
|
||||
transaction_date=date(2026, 6, 11),
|
||||
billing_provider=BillingProvider(
|
||||
name="Test Provider",
|
||||
npi="1234567890",
|
||||
tax_id="123456789",
|
||||
address=Address(line1="1 Main", city="X", state="CO", zip="80000"),
|
||||
),
|
||||
subscriber=Subscriber(
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
member_id="M1",
|
||||
dob=date(1980, 1, 1),
|
||||
gender="M",
|
||||
address=Address(line1="1 Main", city="X", state="CO", zip="80000"),
|
||||
),
|
||||
payer=Payer(name="COHCPF", id="SKCO0"),
|
||||
claim=ClaimHeader(
|
||||
claim_id="C1",
|
||||
total_charge=Decimal("100.00"),
|
||||
place_of_service="12",
|
||||
frequency_code="1",
|
||||
provider_signature="Y",
|
||||
assignment="Y",
|
||||
release_of_info="Y",
|
||||
),
|
||||
diagnoses=[Diagnosis(code="Z00", qualifier="ABK")],
|
||||
service_lines=[
|
||||
ServiceLine(
|
||||
line_number=1,
|
||||
procedure=Procedure(qualifier="HC", code="99213", modifiers=[]),
|
||||
charge=Decimal("100.00"),
|
||||
unit_type="UN",
|
||||
units=Decimal("1.0"),
|
||||
service_date=date(2026, 6, 11),
|
||||
)
|
||||
],
|
||||
validation=ValidationReport(passed=True, errors=[], warnings=[]),
|
||||
raw_segments=[],
|
||||
)
|
||||
base.update(overrides)
|
||||
return ClaimOutput(**base)
|
||||
|
||||
|
||||
def test_validate_passing_claim():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
report = validate(_build_claim(), cfg)
|
||||
assert report.passed is True
|
||||
assert report.errors == []
|
||||
|
||||
|
||||
def test_r010_clm01_required():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.claim.claim_id = ""
|
||||
report = validate(claim, cfg)
|
||||
assert not report.passed
|
||||
assert any(i.rule == "R010_clm01_present" for i in report.errors)
|
||||
|
||||
|
||||
def test_r011_total_charge_positive():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.claim.total_charge = Decimal("0.00")
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R011_total_charge_positive" for i in report.errors)
|
||||
|
||||
|
||||
def test_r020_npi_must_be_ten_digits():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.billing_provider.npi = "12345"
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R020_npi_format" for i in report.errors)
|
||||
|
||||
|
||||
def test_r030_frequency_allowed():
|
||||
cfg = PayerConfig.co_medicaid() # only 1, 7, 8
|
||||
claim = _build_claim()
|
||||
claim.claim.frequency_code = "5"
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R030_frequency_allowed" for i in report.errors)
|
||||
|
||||
|
||||
def test_r031_ref_g1_optional_no_error():
|
||||
"""R031 is informational in v1 — no REF*G1 should not error."""
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
report = validate(claim, cfg)
|
||||
assert not any(i.rule == "R031_ref_g1_optional" and i.severity == "error" for i in report.errors)
|
||||
|
||||
|
||||
def test_r050_diagnosis_required():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim(diagnoses=[])
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R050_diagnosis_present" for i in report.errors)
|
||||
|
||||
|
||||
def test_r060_service_dates_required():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.service_lines[0].service_date = None
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R060_service_dates_present" for i in report.errors)
|
||||
# Now add the date:
|
||||
claim.service_lines[0].service_date = date(2026, 6, 11)
|
||||
report = validate(claim, cfg)
|
||||
assert not any(i.rule == "R060_service_dates_present" for i in report.errors + report.warnings)
|
||||
|
||||
|
||||
def test_r070_charges_sum_warning():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.claim.total_charge = Decimal("999.00") # mismatch
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R070_charges_sum" and i.severity == "warning" for i in report.warnings)
|
||||
|
||||
|
||||
def test_r100_payer_id_warning_only():
|
||||
cfg = PayerConfig.co_medicaid()
|
||||
claim = _build_claim()
|
||||
claim.payer.id = "WRONG"
|
||||
report = validate(claim, cfg)
|
||||
assert any(i.rule == "R100_payer_id_matches" and i.severity == "warning" for i in report.warnings)
|
||||
assert report.passed is True
|
||||
Reference in New Issue
Block a user