feat(backend): add store iterators (claims, remits, providers, activity)

This commit is contained in:
Tyler
2026-06-19 19:06:10 -06:00
parent 2a78d129ca
commit 0fa80e29d5
2 changed files with 208 additions and 0 deletions
+124
View File
@@ -59,6 +59,130 @@ class InMemoryStore:
with self._lock:
return list(self._batches)
def iter_claims(
self,
*,
batch_id: str | None = None,
status: str | None = None,
provider_npi: str | None = None,
date_from: str | None = None,
date_to: str | None = None,
sort: str | None = None,
order: str = "desc",
limit: int = 100,
offset: int = 0,
) -> Iterable[dict]:
with self._lock:
all_claims: list[dict] = []
for b in self._batches:
if b.kind != "837p":
continue
if batch_id is not None and b.id != batch_id:
continue
# ParseResult / ParseResult835 union: 837p kinds are ParseResult
result = b.result # type: ignore[assignment]
for c in result.claims:
all_claims.append(to_ui_claim(
c, batch_id=b.id, parsed_at=b.parsed_at,
))
if status is not None:
all_claims = [c for c in all_claims if c["status"] == status]
if provider_npi is not None:
all_claims = [c for c in all_claims if c["providerNpi"] == provider_npi]
if sort is not None:
all_claims.sort(
key=lambda c: c.get(sort, 0) or 0,
reverse=(order == "desc"),
)
yield from all_claims[offset:offset + limit]
def iter_remittances(
self,
*,
batch_id: str | None = None,
payer: str | None = None,
claim_id: str | None = None,
date_from: str | None = None,
date_to: str | None = None,
sort: str | None = None,
order: str = "desc",
limit: int = 100,
offset: int = 0,
) -> Iterable[dict]:
with self._lock:
all_remits: list[dict] = []
for b in self._batches:
if b.kind != "835":
continue
if batch_id is not None and b.id != batch_id:
continue
# 835 kinds are ParseResult835; its claims list holds ClaimPayment
result = b.result # type: ignore[assignment]
for cp in result.claims:
all_remits.append(to_ui_remittance(
cp, batch_id=b.id, parsed_at=b.parsed_at,
))
if payer is not None:
all_remits = [r for r in all_remits if r.get("payerName") == payer]
if claim_id is not None:
all_remits = [r for r in all_remits if r["claimId"] == claim_id]
if sort is not None:
all_remits.sort(
key=lambda r: r.get(sort, 0) or 0,
reverse=(order == "desc"),
)
yield from all_remits[offset:offset + limit]
def distinct_providers(self) -> list[dict]:
with self._lock:
by_npi: dict[str, dict] = {}
for b in self._batches:
if b.kind != "837p":
continue
result = b.result # type: ignore[assignment]
if not result.claims:
continue
# billing_provider lives on each claim, not at the batch level
bp = result.claims[0].billing_provider
npi = bp.npi
if npi not in by_npi:
by_npi[npi] = to_ui_provider(
npi=npi,
name=bp.name or "",
tax_id=bp.tax_id,
claim_count=0,
outstanding_ar=0.0,
)
by_npi[npi]["claimCount"] += len(result.claims)
return list(by_npi.values())
def recent_activity(self, *, limit: int = 200) -> list[dict]:
with self._lock:
events: list[dict] = []
for b in reversed(self._batches):
if b.kind == "837p":
result = b.result # type: ignore[assignment]
for c in result.claims:
events.append(to_activity_event(
id=f"{b.id}:{c.claim_id}",
kind="claim_submitted",
message=f"Claim {c.claim_id} submitted · {c.payer.name}",
timestamp=b.parsed_at,
npi=c.billing_provider.npi,
amount=float(c.claim.total_charge or 0.0),
))
elif b.kind == "835":
result = b.result # type: ignore[assignment]
for cp in result.claims:
events.append(to_activity_event(
id=f"{b.id}:{cp.payer_claim_control_number}",
kind="remit_received",
message=f"Remit {cp.payer_claim_control_number} received",
timestamp=b.parsed_at,
amount=float(cp.total_paid or 0.0),
))
return events[:limit]
store = InMemoryStore()