feat(sp31): integrate content-keys fallback into reconcile.run() with auto_matched_835 event
This commit is contained in:
@@ -653,3 +653,120 @@ def test_content_keys_npi_from_remit_raw_json(db_session, make_claim):
|
||||
db_session.add(remit)
|
||||
db_session.flush()
|
||||
assert _content_keys_match(remit, claim) is True # charge + NPI = 2 of 3
|
||||
|
||||
|
||||
# --- SP31: end-to-end integration via reconcile.run() -----------------------
|
||||
|
||||
|
||||
def test_reconcile_run_emits_score_auto_match_when_pcn_misses(db_session, make_claim, make_remit):
|
||||
"""A remit with no PCN match but matching charge+NPI triggers score-auto."""
|
||||
from decimal import Decimal
|
||||
from datetime import date
|
||||
from sqlalchemy import select
|
||||
from cyclone.db import Batch, ClaimState
|
||||
from cyclone.reconcile import run as reconcile_run
|
||||
claim = make_claim(patient_control_number="UNIQUE-CLM",
|
||||
total_charge=Decimal("100.00"),
|
||||
rendering_provider_npi="1111111111",
|
||||
service_date_from=date(2026, 6, 1))
|
||||
remit = make_remit(payer_claim_control_number="DIFFERENT-PCN",
|
||||
total_charge_amount=Decimal("100.00"),
|
||||
rendering_provider_npi="1111111111",
|
||||
service_date=date(2026, 6, 5))
|
||||
batch = Batch(id="batch-1", kind="835", input_filename="x.835", parsed_at=date(2026, 6, 5))
|
||||
db_session.add(batch)
|
||||
db_session.flush()
|
||||
# Re-parent the remit to this batch (make_remit doesn't set batch_id).
|
||||
remit.batch_id = batch.id
|
||||
db_session.flush()
|
||||
|
||||
result = reconcile_run(db_session, batch.id)
|
||||
db_session.flush() # autoflush=False on SessionLocal; force flush pending Match row
|
||||
|
||||
assert result.matched == 1
|
||||
# Verify Match row was written with strategy="score-auto"
|
||||
from cyclone.db import Match
|
||||
matches = list(db_session.execute(
|
||||
select(Match).where(Match.remittance_id == remit.id)
|
||||
).scalars().all())
|
||||
assert len(matches) == 1
|
||||
assert matches[0].strategy == "score-auto"
|
||||
assert matches[0].claim_id == claim.id
|
||||
# Verify ActivityEvent was emitted with kind="auto_matched_835"
|
||||
from cyclone.db import ActivityEvent
|
||||
events = list(db_session.execute(
|
||||
select(ActivityEvent).where(
|
||||
ActivityEvent.remittance_id == remit.id,
|
||||
ActivityEvent.kind == "auto_matched_835",
|
||||
)
|
||||
).scalars().all())
|
||||
assert len(events) == 1
|
||||
assert events[0].claim_id == claim.id
|
||||
# Verify claim state was flipped
|
||||
db_session.refresh(claim)
|
||||
assert claim.state in {ClaimState.PAID, ClaimState.PARTIAL, ClaimState.RECEIVED}
|
||||
|
||||
|
||||
def test_reconcile_run_pcn_path_unchanged(db_session, make_claim, make_remit):
|
||||
"""Existing PCN-exact match still produces strategy='pcn-exact' (regression guard)."""
|
||||
from decimal import Decimal
|
||||
from datetime import date
|
||||
from sqlalchemy import select
|
||||
from cyclone.db import Batch, Match
|
||||
from cyclone.reconcile import run as reconcile_run
|
||||
claim = make_claim(patient_control_number="SAME-PCN",
|
||||
total_charge=Decimal("100.00"),
|
||||
rendering_provider_npi="1111111111",
|
||||
service_date_from=date(2026, 6, 1))
|
||||
remit = make_remit(payer_claim_control_number="SAME-PCN",
|
||||
total_charge_amount=Decimal("100.00"),
|
||||
rendering_provider_npi="1111111111",
|
||||
service_date=date(2026, 6, 2))
|
||||
batch = Batch(id="batch-1", kind="835", input_filename="x.835", parsed_at=date(2026, 6, 5))
|
||||
db_session.add(batch)
|
||||
db_session.flush()
|
||||
remit.batch_id = batch.id
|
||||
db_session.flush()
|
||||
|
||||
reconcile_run(db_session, batch.id)
|
||||
db_session.flush() # autoflush=False on SessionLocal; force flush pending Match row
|
||||
|
||||
matches = list(db_session.execute(
|
||||
select(Match).where(Match.remittance_id == remit.id)
|
||||
).scalars().all())
|
||||
assert len(matches) == 1
|
||||
assert matches[0].strategy == "pcn-exact" # NOT "score-auto"
|
||||
|
||||
|
||||
def test_reconcile_run_no_match_leaves_remit_unlinked(db_session, make_claim, make_remit):
|
||||
"""No 2-of-3 match → no Match row, claim state unchanged."""
|
||||
from decimal import Decimal
|
||||
from datetime import date
|
||||
from sqlalchemy import select
|
||||
from cyclone.db import Batch, Match, ClaimState
|
||||
from cyclone.reconcile import run as reconcile_run
|
||||
claim = make_claim(patient_control_number="UNRELATED",
|
||||
total_charge=Decimal("999.99"),
|
||||
rendering_provider_npi="2222222222",
|
||||
service_date_from=date(2026, 6, 1))
|
||||
remit = make_remit(payer_claim_control_number="OTHER",
|
||||
total_charge_amount=Decimal("100.00"),
|
||||
rendering_provider_npi="1111111111",
|
||||
service_date=date(2026, 6, 5))
|
||||
batch = Batch(id="batch-1", kind="835", input_filename="x.835", parsed_at=date(2026, 6, 5))
|
||||
db_session.add(batch)
|
||||
db_session.flush()
|
||||
remit.batch_id = batch.id
|
||||
db_session.flush()
|
||||
|
||||
result = reconcile_run(db_session, batch.id)
|
||||
|
||||
assert result.matched == 0
|
||||
matches = list(db_session.execute(
|
||||
select(Match).where(Match.remittance_id == remit.id)
|
||||
).scalars().all())
|
||||
assert len(matches) == 0
|
||||
db_session.refresh(claim)
|
||||
assert claim.state == ClaimState.SUBMITTED # unchanged
|
||||
db_session.refresh(remit)
|
||||
assert remit.claim_id is None # unchanged
|
||||
|
||||
Reference in New Issue
Block a user