fix(api): include adjustments array in /api/remittances list response (SP3 P2 follow-up)
The P2 subagent shipped a UI chevron+expansion in Remittances.tsx that read adjustments from the list payload, but iter_remittances() didn't include the CasAdjustment rows. Result: the expansion was always empty when a remittance was opened from the list (the detail endpoint worked). Fix: bulk-fetch all CasAdjustment rows for the remittance_ids in one query, then attach them as the 'adjustments' array. N+1-free.
This commit is contained in:
@@ -971,6 +971,20 @@ class CycloneStore:
|
|||||||
q = q.filter(Remittance.claim_id == claim_id)
|
q = q.filter(Remittance.claim_id == claim_id)
|
||||||
|
|
||||||
rows = q.all()
|
rows = q.all()
|
||||||
|
# Bulk-fetch all CAS rows for these remittances in one query
|
||||||
|
# (SP3 P2 follow-up — fixes the list-view's empty adjustments
|
||||||
|
# expansion). N+1-free.
|
||||||
|
cas_by_remit: dict[str, list] = {}
|
||||||
|
if rows:
|
||||||
|
from cyclone.parsers.cas_codes import reason_label
|
||||||
|
cas_rows = (
|
||||||
|
s.query(CasAdjustment)
|
||||||
|
.filter(CasAdjustment.remittance_id.in_([r.id for r in rows]))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
for c in cas_rows:
|
||||||
|
cas_by_remit.setdefault(c.remittance_id, []).append(c)
|
||||||
|
|
||||||
out: list[dict] = []
|
out: list[dict] = []
|
||||||
for r in rows:
|
for r in rows:
|
||||||
raw = r.raw_json or {}
|
raw = r.raw_json or {}
|
||||||
@@ -984,6 +998,16 @@ class CycloneStore:
|
|||||||
payer_name = (
|
payer_name = (
|
||||||
r.batch.raw_result_json.get("payer", {}).get("name", "")
|
r.batch.raw_result_json.get("payer", {}).get("name", "")
|
||||||
)
|
)
|
||||||
|
adjustments = [
|
||||||
|
{
|
||||||
|
"group": c.group_code,
|
||||||
|
"reason": c.reason_code,
|
||||||
|
"label": reason_label(c.group_code, c.reason_code),
|
||||||
|
"amount": float(c.amount),
|
||||||
|
"quantity": float(c.quantity) if c.quantity is not None else None,
|
||||||
|
}
|
||||||
|
for c in cas_by_remit.get(r.id, [])
|
||||||
|
]
|
||||||
out.append({
|
out.append({
|
||||||
"id": r.id,
|
"id": r.id,
|
||||||
"claimId": r.claim_id or "",
|
"claimId": r.claim_id or "",
|
||||||
@@ -999,6 +1023,7 @@ class CycloneStore:
|
|||||||
"receivedDate": r.received_at.isoformat().replace("+00:00", "Z"),
|
"receivedDate": r.received_at.isoformat().replace("+00:00", "Z"),
|
||||||
"batchId": r.batch_id,
|
"batchId": r.batch_id,
|
||||||
"parsedAt": parsed_at_iso,
|
"parsedAt": parsed_at_iso,
|
||||||
|
"adjustments": adjustments,
|
||||||
"_sort_receivedDate": r.received_at.isoformat().replace("+00:00", "Z"),
|
"_sort_receivedDate": r.received_at.isoformat().replace("+00:00", "Z"),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,43 @@ def test_remittances_filters_by_batch_id(seeded_store):
|
|||||||
assert resp.json()["items"] == []
|
assert resp.json()["items"] == []
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# /api/remittances list payload includes adjustments array (SP3 P2 follow-up)
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_remittances_includes_adjustments_array(client: TestClient) -> None:
|
||||||
|
"""SP3 P2 follow-up: the list endpoint must include `adjustments` so the
|
||||||
|
UI's expandable row has data when a remittance is opened from the list
|
||||||
|
(not just from the detail endpoint)."""
|
||||||
|
_seed_835_remittance(
|
||||||
|
pcns=["PCN-LIST-1"],
|
||||||
|
cas_rows={"PCN-LIST-1": [("CO", "45", "50.00", ""), ("PR", "1", "10.00", "")]},
|
||||||
|
)
|
||||||
|
resp = client.get("/api/remittances", headers=JSON)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert len(body["items"]) == 1
|
||||||
|
item = body["items"][0]
|
||||||
|
assert "adjustments" in item, item
|
||||||
|
assert len(item["adjustments"]) == 2
|
||||||
|
# Verify labels come through (resolved by cas_codes.reason_label).
|
||||||
|
labels = {a["reason"]: a["label"] for a in item["adjustments"]}
|
||||||
|
assert "45" in labels and "1" in labels
|
||||||
|
assert labels["45"] # non-empty
|
||||||
|
assert labels["1"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_remittances_empty_adjustments_is_empty_array(client: TestClient) -> None:
|
||||||
|
"""A remittance with no CAS rows must still expose `adjustments: []`."""
|
||||||
|
_seed_835_remittance(pcns=["PCN-LIST-EMPTY"], cas_rows=None)
|
||||||
|
resp = client.get("/api/remittances", headers=JSON)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert len(body["items"]) == 1
|
||||||
|
assert body["items"][0]["adjustments"] == []
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# /api/providers
|
# /api/providers
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
|||||||
Reference in New Issue
Block a user