From 890207f40dc038c2d406b92f06c951ec3174a02d Mon Sep 17 00:00:00 2001 From: Tyler Date: Sun, 21 Jun 2026 17:40:10 -0600 Subject: [PATCH] feat(store): add find_existing_batch_for_claim and find_existing_batch_for_remit --- backend/src/cyclone/store.py | 34 +++++++++++++++++++++ backend/tests/test_store.py | 57 +++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/backend/src/cyclone/store.py b/backend/src/cyclone/store.py index bb07b5f..74c8a0e 100644 --- a/backend/src/cyclone/store.py +++ b/backend/src/cyclone/store.py @@ -64,6 +64,40 @@ class AlreadyMatchedError(Exception): """ +def find_existing_batch_for_claim(claim_id: str) -> str | None: + """Return the batch_id of the first batch containing this claim id, or None. + + Pure read; opens a short-lived session. Used by the 837 409 handler to + surface which prior batch already holds the same CLM01. + """ + from sqlalchemy import select + + from cyclone.db import Claim + + with db.SessionLocal()() as s: + row = s.execute( + select(Claim.batch_id).where(Claim.id == claim_id).limit(1) + ).first() + return row[0] if row else None + + +def find_existing_batch_for_remit(remit_id: str) -> str | None: + """Return the batch_id of the first batch containing this remit id, or None. + + Pure read; opens a short-lived session. Used by the 835 409 handler. + `remit_id` is the PK on `remittances.id` (= payer_claim_control_number = CLP01). + """ + from sqlalchemy import select + + from cyclone.db import Remittance + + with db.SessionLocal()() as s: + row = s.execute( + select(Remittance.batch_id).where(Remittance.id == remit_id).limit(1) + ).first() + return row[0] if row else None + + class NotMatchedError(Exception): """Raised by ``CycloneStore.manual_unmatch`` when the claim has no match. diff --git a/backend/tests/test_store.py b/backend/tests/test_store.py index ca31a0c..93eb2e2 100644 --- a/backend/tests/test_store.py +++ b/backend/tests/test_store.py @@ -200,4 +200,59 @@ def test_persistence_across_session(): s2 = CycloneStore() # fresh instance, same engine loaded = s2.get_batch("b-x") assert loaded is not None - assert loaded["kind"] == "837p" \ No newline at end of file + assert loaded["kind"] == "837p" + + +def test_find_existing_batch_for_claim_returns_none_for_unknown(): + """Unknown claim_id -> None.""" + from cyclone import store as store_mod # noqa: F401 + assert store_mod.find_existing_batch_for_claim("nope") is None + + +def test_find_existing_batch_for_claim_returns_batch_id(): + """Known claim_id -> batch_id of the holding batch.""" + from cyclone import db as _db + from cyclone.db import Batch, Claim + from datetime import datetime, timezone + + with _db.SessionLocal()() as s: + s.add(Batch( + id="B1", kind="837p", input_filename="x.txt", + parsed_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + raw_result_json={}, + )) + s.add(Claim(id="CLM-A", batch_id="B1", patient_control_number="M1")) + s.commit() + + from cyclone import store as store_mod # noqa: F401 + assert store_mod.find_existing_batch_for_claim("CLM-A") == "B1" + + +def test_find_existing_batch_for_remit_returns_none_for_unknown(): + """Unknown remit id -> None.""" + from cyclone import store as store_mod # noqa: F401 + assert store_mod.find_existing_batch_for_remit("nope") is None + + +def test_find_existing_batch_for_remit_returns_batch_id(): + """Known remit id -> batch_id of the holding batch.""" + from cyclone import db as _db + from cyclone.db import Batch, Remittance + from datetime import datetime, timezone + + with _db.SessionLocal()() as s: + s.add(Batch( + id="B2", kind="835", input_filename="y.txt", + parsed_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + raw_result_json={}, + )) + s.add(Remittance( + id="CLP-A", batch_id="B2", + payer_claim_control_number="CLP-A", + status_code="1", + received_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + )) + s.commit() + + from cyclone import store as store_mod # noqa: F401 + assert store_mod.find_existing_batch_for_remit("CLP-A") == "B2" \ No newline at end of file