feat(backend): add store mappers (claim, remittance, provider, activity)
This commit is contained in:
@@ -14,8 +14,8 @@ from typing import Iterable, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from cyclone.parsers.models import ParseResult
|
||||
from cyclone.parsers.models_835 import ParseResult835
|
||||
from cyclone.parsers.models import ClaimOutput, ParseResult
|
||||
from cyclone.parsers.models_835 import ClaimPayment, ParseResult835
|
||||
|
||||
|
||||
BatchKind = Literal["837p", "835"]
|
||||
@@ -66,3 +66,132 @@ store = InMemoryStore()
|
||||
def utcnow_iso() -> str:
|
||||
"""ISO 8601 UTC timestamp, second precision, suffixed with 'Z'."""
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mappers: rich backend models → simpler UI types.
|
||||
# The shape of the returned dicts must match the TypeScript interfaces in
|
||||
# `src/types/index.ts`. Keep both sides in sync.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def to_ui_claim(
|
||||
claim: ClaimOutput,
|
||||
*,
|
||||
batch_id: str,
|
||||
parsed_at: str,
|
||||
) -> dict:
|
||||
"""Map a 837P ClaimOutput to the UI's `Claim` shape.
|
||||
|
||||
Status rules (per spec section 6.2, evaluated in priority order):
|
||||
- !passed → denied
|
||||
- passed and any warnings → pending
|
||||
- passed and frequency_code == "1" → submitted
|
||||
- otherwise → draft
|
||||
"""
|
||||
v = claim.validation
|
||||
if not v.passed:
|
||||
status = "denied"
|
||||
elif v.warnings:
|
||||
status = "pending"
|
||||
elif claim.claim.frequency_code == "1":
|
||||
status = "submitted"
|
||||
else:
|
||||
status = "draft"
|
||||
|
||||
return {
|
||||
"id": claim.claim_id,
|
||||
"patientName": f"{claim.subscriber.first_name} {claim.subscriber.last_name}".strip(),
|
||||
"providerNpi": claim.billing_provider.npi,
|
||||
"payerName": claim.payer.name,
|
||||
"cptCode": (
|
||||
claim.service_lines[0].procedure.code
|
||||
if claim.service_lines
|
||||
else ""
|
||||
),
|
||||
"billedAmount": float(claim.claim.total_charge or 0.0),
|
||||
"receivedAmount": 0.0,
|
||||
"status": status,
|
||||
"denialReason": None,
|
||||
"submissionDate": parsed_at,
|
||||
"batchId": batch_id,
|
||||
"parsedAt": parsed_at,
|
||||
}
|
||||
|
||||
|
||||
def to_ui_remittance(
|
||||
cp: ClaimPayment,
|
||||
*,
|
||||
batch_id: str,
|
||||
parsed_at: str,
|
||||
) -> dict:
|
||||
"""Map an 835 ClaimPayment to the UI's `Remittance` shape.
|
||||
|
||||
Status mapping (per spec section 6.3):
|
||||
- status_code 21/22 → reconciled (reversal/correction)
|
||||
- otherwise → received
|
||||
"""
|
||||
code = cp.status_code
|
||||
if code in {"21", "22"}:
|
||||
status = "reconciled"
|
||||
else:
|
||||
status = "received"
|
||||
|
||||
return {
|
||||
"id": cp.payer_claim_control_number,
|
||||
"claimId": cp.original_claim_id or "",
|
||||
"payerName": "",
|
||||
"paidAmount": float(cp.total_paid or 0.0),
|
||||
"adjustmentAmount": 0.0,
|
||||
"status": status,
|
||||
"denialReason": None,
|
||||
"receivedDate": parsed_at,
|
||||
"batchId": batch_id,
|
||||
"parsedAt": parsed_at,
|
||||
}
|
||||
|
||||
|
||||
def to_ui_provider(
|
||||
*,
|
||||
npi: str,
|
||||
name: str,
|
||||
tax_id: str | None = None,
|
||||
address: str | None = None,
|
||||
city: str | None = None,
|
||||
state: str | None = None,
|
||||
zip: str | None = None,
|
||||
phone: str | None = None,
|
||||
claim_count: int = 0,
|
||||
outstanding_ar: float = 0.0,
|
||||
) -> dict:
|
||||
return {
|
||||
"npi": npi,
|
||||
"name": name,
|
||||
"taxId": tax_id or "",
|
||||
"address": address or "",
|
||||
"city": city or "",
|
||||
"state": state or "",
|
||||
"zip": zip or "",
|
||||
"phone": phone or "",
|
||||
"claimCount": claim_count,
|
||||
"outstandingAr": float(outstanding_ar),
|
||||
}
|
||||
|
||||
|
||||
def to_activity_event(
|
||||
*,
|
||||
id: str,
|
||||
kind: str,
|
||||
message: str,
|
||||
timestamp: str,
|
||||
npi: str | None = None,
|
||||
amount: float | None = None,
|
||||
) -> dict:
|
||||
return {
|
||||
"id": id,
|
||||
"kind": kind,
|
||||
"message": message,
|
||||
"timestamp": timestamp,
|
||||
"npi": npi,
|
||||
"amount": amount,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user