feat(sp33): cli resubmit-rejected-claims + fixture/test payer_id refresh
- Adds `cyclone resubmit-rejected-claims` to push corrected single-claim 837 files to the Gainwell ToHPE SFTP dir. Idempotent (stat-then-skip by byte size). One persistent paramiko session per batch with reconnect-every=50 to dodge MOVEit's silent per-session file cap (~200 puts/session, no exception). - Validates each file via `parse_837` before upload and rejects any whose payer_id is not `CO_TXIX` (catches a bad byte-fix early). - Refreshes test fixtures (`minimal_837p.txt`, `co_medicaid_837p.txt`, `co_medicaid_837p_with_renderer.txt`) and the corresponding test assertions (`test_payer.py`, `test_payer_summary.py`, `test_co_medicaid_fixture.py`, `test_parse_837.py`) from the old `SKCO0`/`COHCPF` payer IDs to `CO_TXIX`, matching PayerConfig.co_medicaid() and the HCPF 837P Companion Guide. - Adds `ingest/` to .gitignore — local scratch / production-data staging only.
This commit is contained in:
@@ -5,13 +5,13 @@ The endpoint is the payer-level aggregate that the drill-down UI's
|
||||
denial rate, and the top 5 NPIs by claim volume for one payer_id,
|
||||
cached in-process for 60s.
|
||||
|
||||
The minimal 837P fixture ships one CLM with ``payer_id="SKCO0"``,
|
||||
The minimal 837P fixture ships one CLM with ``payer_id="CO_TXIX"``,
|
||||
charge_amount=100.00; the minimal 835 carries one CLP for the same
|
||||
claim with total_paid=85.00. So ``/api/payers/SKCO0/summary`` returns
|
||||
claim with total_paid=85.00. So ``/api/payers/CO_TXIX/summary`` returns
|
||||
``claim_count >= 1`` after both files are ingested.
|
||||
|
||||
Note: the spec calls this ``payer_id`` (the X12 NM1*PR*PI qualifier,
|
||||
e.g. ``SKCO0``). It is NOT the configured payer name from
|
||||
e.g. ``CO_TXIX``). It is NOT the configured payer name from
|
||||
``config/payers.yaml``. The filter key in the store layer is
|
||||
``Claim.payer_id`` — not the ``payer=`` substring filter used by
|
||||
``/api/claims?payer=...``.
|
||||
@@ -54,7 +54,7 @@ def client() -> TestClient:
|
||||
def seeded_db(client: TestClient):
|
||||
"""Ingest one minimal 837P + one minimal 835.
|
||||
|
||||
Both fixtures carry ``payer_id="SKCO0"`` so the summary endpoint
|
||||
Both fixtures carry ``payer_id="CO_TXIX"`` so the summary endpoint
|
||||
has something to aggregate. ``client`` is yielded back so the
|
||||
test can hit the API on the same TestClient that ingested the
|
||||
fixtures (parses share the per-test SQLite from conftest).
|
||||
@@ -77,11 +77,11 @@ def seeded_db(client: TestClient):
|
||||
|
||||
|
||||
def test_payer_summary_happy_path(seeded_db: TestClient):
|
||||
"""Seeded db has at least one claim for SKCO0 → 200 with the spec shape."""
|
||||
resp = seeded_db.get("/api/payers/SKCO0/summary")
|
||||
"""Seeded db has at least one claim for CO_TXIX → 200 with the spec shape."""
|
||||
resp = seeded_db.get("/api/payers/CO_TXIX/summary")
|
||||
assert resp.status_code == 200, resp.text
|
||||
data = resp.json()
|
||||
assert data["payer_id"] == "SKCO0"
|
||||
assert data["payer_id"] == "CO_TXIX"
|
||||
assert "claim_count" in data
|
||||
assert "billed_total" in data
|
||||
assert "received_total" in data
|
||||
@@ -99,8 +99,8 @@ def test_payer_summary_unknown_payer_returns_404(client: TestClient):
|
||||
|
||||
def test_payer_summary_caches_then_invalidates(seeded_db: TestClient):
|
||||
"""Two back-to-back calls return identical payloads (in-process cache)."""
|
||||
resp1 = seeded_db.get("/api/payers/SKCO0/summary")
|
||||
resp2 = seeded_db.get("/api/payers/SKCO0/summary")
|
||||
resp1 = seeded_db.get("/api/payers/CO_TXIX/summary")
|
||||
resp2 = seeded_db.get("/api/payers/CO_TXIX/summary")
|
||||
assert resp1.status_code == 200
|
||||
assert resp2.status_code == 200
|
||||
assert resp1.json() == resp2.json()
|
||||
Reference in New Issue
Block a user