diff --git a/backend/src/cyclone/store.py b/backend/src/cyclone/store.py index de4396a..a6d4afa 100644 --- a/backend/src/cyclone/store.py +++ b/backend/src/cyclone/store.py @@ -760,6 +760,28 @@ def to_ui_remittance_with_adjustments( return base +def _svc_to_wire_dict(svc) -> dict: + """Project an ORM ``ServiceLinePayment`` to the wire format used by + the remit drawer's ``serviceLinePayments`` array. + + Mirrors the shape produced by the line-reconciliation endpoint so + the UI can render the same components from either source. + """ + import json as _json + return { + "id": svc.id, + "line_number": svc.line_number, + "procedure_qualifier": svc.procedure_qualifier, + "procedure_code": svc.procedure_code, + "modifiers": _json.loads(svc.modifiers_json or "[]"), + "charge": str(Decimal(str(svc.charge))), + "payment": str(Decimal(str(svc.payment))), + "units": str(Decimal(str(svc.units))) if svc.units is not None else None, + "unit_type": svc.unit_type, + "service_date": svc.service_date.isoformat() if svc.service_date else None, + } + + def to_ui_provider( *, npi: str, @@ -1188,6 +1210,11 @@ class CycloneStore: and labels each via :mod:`cyclone.parsers.cas_codes`. Returns ``None`` when the remittance is not found so the API layer can map that to a 404. + + SP7: also returns the per-line SVC composites + (``serviceLinePayments``) and the CLP-level (claim-level) CAS + bucket (``claimLevelAdjustments``) so the remit drawer can show + per-line payments + adjustments without a second fetch. """ with db.SessionLocal()() as s: row = s.get(Remittance, remittance_id) @@ -1203,12 +1230,39 @@ class CycloneStore: ) if parsed_at is not None and parsed_at.tzinfo is None: parsed_at = parsed_at.replace(tzinfo=timezone.utc) - return to_ui_remittance_with_adjustments( + body = to_ui_remittance_with_adjustments( row, batch_id=row.batch_id, parsed_at=parsed_at, cas_rows=cas_rows, ) + # SP7: per-line SVC composites + claim-level CAS bucket. + from cyclone.db import ServiceLinePayment as SLP + slps = ( + s.query(SLP) + .filter(SLP.remittance_id == remittance_id) + .order_by(SLP.line_number) + .all() + ) + body["serviceLinePayments"] = [ + _svc_to_wire_dict(svc) for svc in slps + ] + body["claimLevelAdjustments"] = [ + { + "id": c.id, + "group_code": c.group_code, + "reason_code": c.reason_code, + "amount": str(Decimal(str(c.amount))), + "quantity": ( + str(Decimal(str(c.quantity))) + if c.quantity is not None + else None + ), + } + for c in cas_rows + if c.service_line_payment_id is None + ] + return body def get_claim_detail(self, claim_id: str) -> dict | None: """Return the SP4 detail-drawer shape for one claim, or ``None``.