diff --git a/src/components/ClaimDrawer/ServiceLinesTable.test.tsx b/src/components/ClaimDrawer/ServiceLinesTable.test.tsx
index e51a001..d3f2110 100644
--- a/src/components/ClaimDrawer/ServiceLinesTable.test.tsx
+++ b/src/components/ClaimDrawer/ServiceLinesTable.test.tsx
@@ -117,9 +117,9 @@ describe("ServiceLinesTable", () => {
// Row carries the line number as a data attribute for test stability.
expect(rows[0].getAttribute("data-line-number")).toBe("1");
- // Cells: line #, procedure, charge, units, date.
+ // Cells: line #, procedure, charge, units, date, paid, adjustments.
const cells = rows[0].querySelectorAll("td");
- expect(cells.length).toBe(5);
+ expect(cells.length).toBe(7);
const cellText = Array.from(cells).map((c) => c.textContent ?? "");
expect(cellText[0]).toContain("1"); // line #
@@ -128,10 +128,52 @@ describe("ServiceLinesTable", () => {
expect(cellText[3]).toContain("1"); // units count
expect(cellText[3]).toContain("UN"); // units type
expect(cellText[4]).toContain("2026"); // date (year at minimum)
+ // Paid + Adjustments default to em-dash when no lineReconciliation is provided.
+ expect(cellText[5]).toContain("—");
+ expect(cellText[6]).toContain("—");
unmount();
});
+ it("test_paid_and_adjustments_columns_render_from_lineReconciliation", () => {
+ const line = makeLine({ lineNumber: 1, charge: 100.0 });
+ const lineReconciliation = [
+ { lineNumber: 1, status: "matched" as const, paid: "80.00", adjustmentsSum: "20.00" },
+ ];
+ const { container, unmount } = renderIntoContainer(
+
+ );
+ const row = container.querySelector('[data-testid="service-line-row"]');
+ expect(row).not.toBeNull();
+ const paid = row?.querySelector('[data-testid="paid-cell"]');
+ const adjustments = row?.querySelector('[data-testid="adjustments-cell"]');
+ expect(paid?.textContent ?? "").toContain("$80.00");
+ expect(adjustments?.textContent ?? "").toContain("$20.00");
+ unmount();
+ });
+
+ it("test_paid_and_adjustments_render_em_dash_when_null", () => {
+ const line = makeLine({ lineNumber: 1 });
+ const lineReconciliation = [
+ { lineNumber: 1, status: "unmatched_837_only" as const, paid: null, adjustmentsSum: null },
+ ];
+ const { container, unmount } = renderIntoContainer(
+
+ );
+ const row = container.querySelector('[data-testid="service-line-row"]');
+ const paid = row?.querySelector('[data-testid="paid-cell"]');
+ const adjustments = row?.querySelector('[data-testid="adjustments-cell"]');
+ expect(paid?.textContent ?? "").toBe("—");
+ expect(adjustments?.textContent ?? "").toBe("—");
+ unmount();
+ });
+
it("test_multiple_lines_render_in_order", () => {
const lines = [
makeLine({ lineNumber: 1, procedureCode: "99213" }),
diff --git a/src/components/ClaimDrawer/ServiceLinesTable.tsx b/src/components/ClaimDrawer/ServiceLinesTable.tsx
index fb630c9..b0d8911 100644
--- a/src/components/ClaimDrawer/ServiceLinesTable.tsx
+++ b/src/components/ClaimDrawer/ServiceLinesTable.tsx
@@ -1,9 +1,15 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { fmt } from "@/lib/format";
-import type { ClaimDetail } from "@/types";
+import type { ClaimDetail, ClaimDetailLineReconciliation } from "@/types";
type ServiceLinesTableProps = {
serviceLines: ClaimDetail["serviceLines"];
+ /**
+ * SP7: per-line Paid + Adjustments values from the slim projection
+ * embedded in the claim-detail response. Defaults to [] (renders
+ * em-dash placeholders in the new columns).
+ */
+ lineReconciliation?: ClaimDetailLineReconciliation[];
};
function formatProcedure(line: ClaimDetail["serviceLines"][number]): string {
@@ -16,14 +22,29 @@ function formatUnits(line: ClaimDetail["serviceLines"][number]): string {
return `${line.units ?? 0} ${line.unitType ?? ""}`.trimEnd();
}
+function formatMoneyOrDash(value: string | null): string {
+ if (value === null) return "—";
+ const n = Number(value);
+ return Number.isFinite(n) ? fmt.usdPrecise(n) : value;
+}
+
/**
- * Service-lines table for the claim detail drawer (SP4).
+ * Service-lines table for the claim detail drawer (SP4 + SP7).
*
- * Five columns: line #, procedure (qualifier + code + modifiers),
- * charge (mono, prominent), units, date. Charge is the headline —
+ * Seven columns: line #, procedure (qualifier + code + modifiers),
+ * charge (mono, prominent), units, date, paid (from 835 SVC), and
+ * adjustments (sum of per-line CAS rows). Charge is the headline —
* big mono digits with tabular-nums so columns align across rows.
*/
-export function ServiceLinesTable({ serviceLines }: ServiceLinesTableProps) {
+export function ServiceLinesTable({
+ serviceLines,
+ lineReconciliation = [],
+}: ServiceLinesTableProps) {
+ const lrByLine: Record = {};
+ for (const lr of lineReconciliation) {
+ lrByLine[lr.lineNumber] = lr;
+ }
+
return (
Charge
Units
Date
+ Paid
+ Adjustments
- {serviceLines.map((line) => (
-
-
- {line.lineNumber}
-
-
- {formatProcedure(line)}
-
- {
+ const lr = lrByLine[line.lineNumber];
+ return (
+
- {fmt.usdPrecise(line.charge)}
-
-
- {formatUnits(line)}
-
-
- {line.serviceDate ? fmt.date(line.serviceDate) : ""}
-
-
- ))}
+
+ {line.lineNumber}
+
+
+ {formatProcedure(line)}
+
+
+ {fmt.usdPrecise(line.charge)}
+
+
+ {formatUnits(line)}
+
+
+ {line.serviceDate ? fmt.date(line.serviceDate) : ""}
+
+
+ {formatMoneyOrDash(lr?.paid ?? null)}
+
+
+ {formatMoneyOrDash(lr?.adjustmentsSum ?? null)}
+
+
+ );
+ })}
)}