feat(sp7): GET /api/claims/{id}/line-reconciliation endpoint
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""SP7 — endpoint test for GET /api/claims/{id}/line-reconciliation.
|
||||
|
||||
Spec §5.1.
|
||||
|
||||
Architecture note: 837 service lines live in ``Claim.raw_json``, not a
|
||||
separate ORM table, so the test seeds them via the raw_json blob.
|
||||
"""
|
||||
from datetime import date, datetime, timezone
|
||||
from decimal import Decimal
|
||||
import json
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import select
|
||||
|
||||
from cyclone import db
|
||||
from cyclone.api import app
|
||||
from cyclone.parsers.models_835 import ClaimPayment, ServicePayment
|
||||
from cyclone.reconcile import run
|
||||
from cyclone.store import _persist_835_remit, _remittance_835_row
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def _seed(client):
|
||||
"""Insert one claim + matching remit + 4 service lines (3 matched, 1 unmatched 837).
|
||||
Returns the claim id."""
|
||||
bid = str(uuid.uuid4())
|
||||
claim_id = "CLM-API-1"
|
||||
svc_lines_json = []
|
||||
# 4 837 service lines: 99213, 99214, 99215, 90837 (last one is 837-only — no SVC)
|
||||
for i, code in enumerate(["99213", "99214", "99215", "90837"], start=1):
|
||||
svc_lines_json.append({
|
||||
"line_number": i,
|
||||
"procedure": {"qualifier": "HC", "code": code, "modifiers": []},
|
||||
"charge": "100.00",
|
||||
"unit_type": "UN",
|
||||
"units": "1",
|
||||
"place_of_service": "11",
|
||||
"service_date": f"2026-06-0{i}",
|
||||
"provider_reference": None,
|
||||
})
|
||||
|
||||
with db.SessionLocal()() as s:
|
||||
s.add(db.Batch(
|
||||
id=bid, kind="835", input_filename="x.835",
|
||||
parsed_at=datetime.now(timezone.utc),
|
||||
totals_json={"total_claims": 1, "total_paid": "240"},
|
||||
validation_json={"passed": True, "warnings": [], "errors": []},
|
||||
raw_result_json={"_": "stub"},
|
||||
))
|
||||
s.add(db.Claim(
|
||||
id=claim_id,
|
||||
patient_control_number="PCN-API",
|
||||
charge_amount=Decimal("300.00"),
|
||||
provider_npi="1234567890",
|
||||
payer_id="SKCO0",
|
||||
service_date_from=date(2026, 6, 1),
|
||||
service_date_to=date(2026, 6, 4),
|
||||
state="submitted",
|
||||
batch_id=bid,
|
||||
raw_json={"service_lines": svc_lines_json},
|
||||
))
|
||||
# Remit pays the first 3 lines (line 4 is 837-only)
|
||||
cp = ClaimPayment(
|
||||
payer_claim_control_number="PCN-API",
|
||||
status_code="1",
|
||||
total_charge=Decimal("300.00"),
|
||||
total_paid=Decimal("240.00"),
|
||||
service_payments=[
|
||||
ServicePayment(
|
||||
line_number=i, procedure_qualifier="HC",
|
||||
procedure_code=code, charge=Decimal("100.00"),
|
||||
payment=Decimal("80.00"),
|
||||
units=Decimal("1"),
|
||||
service_date=date(2026, 6, i),
|
||||
)
|
||||
for i, code in enumerate(["99213", "99214", "99215"], start=1)
|
||||
],
|
||||
)
|
||||
remit = _remittance_835_row(cp, bid)
|
||||
s.add(remit)
|
||||
s.flush()
|
||||
_persist_835_remit(s, cp, remit.id)
|
||||
s.commit()
|
||||
|
||||
# Run reconcile so matched_remittance_id gets set + LineReconciliation rows persist
|
||||
run(s, bid)
|
||||
s.commit()
|
||||
|
||||
return claim_id
|
||||
|
||||
|
||||
def test_endpoint_returns_documented_shape(client):
|
||||
claim_id = _seed(client)
|
||||
r = client.get(f"/api/claims/{claim_id}/line-reconciliation")
|
||||
assert r.status_code == 200, r.text
|
||||
body = r.json()
|
||||
assert body["claim_id"] == claim_id
|
||||
assert body["summary"]["total_lines"] == 4
|
||||
assert body["summary"]["matched_lines"] == 3
|
||||
statuses = sorted(row["status"] for row in body["lines"])
|
||||
assert statuses == ["matched", "matched", "matched", "unmatched_837_only"]
|
||||
|
||||
|
||||
def test_endpoint_404_for_unknown_claim(client):
|
||||
r = client.get("/api/claims/UNKNOWN/line-reconciliation")
|
||||
assert r.status_code == 404
|
||||
Reference in New Issue
Block a user