feat(report page) - collection by doctors - done
This commit is contained in:
@@ -1,37 +1,9 @@
|
|||||||
import { prisma } from "@repo/db/client";
|
import { prisma } from "@repo/db/client";
|
||||||
|
import {
|
||||||
export interface PatientBalanceRow {
|
DoctorBalancesAndSummary,
|
||||||
patientId: number;
|
GetPatientBalancesResult,
|
||||||
firstName: string | null;
|
PatientBalanceRow,
|
||||||
lastName: string | null;
|
} from "../../../../packages/db/types/payments-reports-types";
|
||||||
totalCharges: number;
|
|
||||||
totalPayments: number;
|
|
||||||
totalAdjusted: number;
|
|
||||||
currentBalance: number;
|
|
||||||
lastPaymentDate: string | null;
|
|
||||||
lastAppointmentDate: string | null;
|
|
||||||
patientCreatedAt?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetPatientBalancesResult {
|
|
||||||
balances: PatientBalanceRow[];
|
|
||||||
totalCount: number;
|
|
||||||
nextCursor: string | null;
|
|
||||||
hasMore: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DoctorBalancesAndSummary {
|
|
||||||
balances: PatientBalanceRow[];
|
|
||||||
totalCount: number;
|
|
||||||
nextCursor: string | null;
|
|
||||||
hasMore: boolean;
|
|
||||||
summary: {
|
|
||||||
totalPatients: number;
|
|
||||||
totalOutstanding: number;
|
|
||||||
totalCollected: number;
|
|
||||||
patientsWithBalance: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPaymentsReportsStorage {
|
export interface IPaymentsReportsStorage {
|
||||||
// summary now returns an extra field patientsWithBalance
|
// summary now returns an extra field patientsWithBalance
|
||||||
@@ -75,11 +47,21 @@ export interface IPaymentsReportsStorage {
|
|||||||
): Promise<DoctorBalancesAndSummary>;
|
): Promise<DoctorBalancesAndSummary>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper: format Date -> SQL literal 'YYYY-MM-DDTHH:mm:ss.sssZ' or null */
|
/** Return ISO literal for inclusive start-of-day (UTC midnight) */
|
||||||
function fmtDateLiteral(d?: Date | null): string | null {
|
function isoStartOfDayLiteral(d?: Date | null): string | null {
|
||||||
if (!d) return null;
|
if (!d) return null;
|
||||||
const iso = new Date(d).toISOString();
|
const dt = new Date(d);
|
||||||
return `'${iso}'`;
|
dt.setUTCHours(0, 0, 0, 0);
|
||||||
|
return `'${dt.toISOString()}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return ISO literal for exclusive next-day start (UTC midnight of the next day) */
|
||||||
|
function isoStartOfNextDayLiteral(d?: Date | null): string | null {
|
||||||
|
if (!d) return null;
|
||||||
|
const dt = new Date(d);
|
||||||
|
dt.setUTCHours(0, 0, 0, 0);
|
||||||
|
dt.setUTCDate(dt.getUTCDate() + 1);
|
||||||
|
return `'${dt.toISOString()}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cursor helpers — base64(JSON) */
|
/** Cursor helpers — base64(JSON) */
|
||||||
@@ -129,8 +111,10 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
try {
|
try {
|
||||||
const hasFrom = from !== undefined && from !== null;
|
const hasFrom = from !== undefined && from !== null;
|
||||||
const hasTo = to !== undefined && to !== null;
|
const hasTo = to !== undefined && to !== null;
|
||||||
const fromLit = fmtDateLiteral(from);
|
|
||||||
const toLit = fmtDateLiteral(to);
|
// Use inclusive start-of-day for 'from' and exclusive start-of-next-day for 'to'
|
||||||
|
const fromStart = isoStartOfDayLiteral(from); // 'YYYY-MM-DDT00:00:00.000Z'
|
||||||
|
const toNextStart = isoStartOfNextDayLiteral(to); // 'YYYY-MM-DDT00:00:00.000Z' of next day
|
||||||
|
|
||||||
// totalPatients: distinct patients who had payments in the date range
|
// totalPatients: distinct patients who had payments in the date range
|
||||||
let patientsCountSql = "";
|
let patientsCountSql = "";
|
||||||
@@ -139,7 +123,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SELECT COUNT(*)::int AS cnt FROM (
|
SELECT COUNT(*)::int AS cnt FROM (
|
||||||
SELECT pay."patientId" AS patient_id
|
SELECT pay."patientId" AS patient_id
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit} AND pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" >= ${fromStart} AND pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
`;
|
`;
|
||||||
@@ -148,7 +132,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SELECT COUNT(*)::int AS cnt FROM (
|
SELECT COUNT(*)::int AS cnt FROM (
|
||||||
SELECT pay."patientId" AS patient_id
|
SELECT pay."patientId" AS patient_id
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit}
|
WHERE pay."createdAt" >= ${fromStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
`;
|
`;
|
||||||
@@ -157,7 +141,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SELECT COUNT(*)::int AS cnt FROM (
|
SELECT COUNT(*)::int AS cnt FROM (
|
||||||
SELECT pay."patientId" AS patient_id
|
SELECT pay."patientId" AS patient_id
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
`;
|
`;
|
||||||
@@ -182,7 +166,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit} AND pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" >= ${fromStart} AND pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) pm
|
) pm
|
||||||
`;
|
`;
|
||||||
@@ -197,7 +181,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit}
|
WHERE pay."createdAt" >= ${fromStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) pm
|
) pm
|
||||||
`;
|
`;
|
||||||
@@ -212,7 +196,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) pm
|
) pm
|
||||||
`;
|
`;
|
||||||
@@ -239,11 +223,11 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
// totalCollected: sum(totalPaid) in the range
|
// totalCollected: sum(totalPaid) in the range
|
||||||
let collSql = "";
|
let collSql = "";
|
||||||
if (hasFrom && hasTo) {
|
if (hasFrom && hasTo) {
|
||||||
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" >= ${fromLit} AND "createdAt" <= ${toLit}`;
|
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" >= ${fromStart} AND "createdAt" <= ${toNextStart}`;
|
||||||
} else if (hasFrom) {
|
} else if (hasFrom) {
|
||||||
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" >= ${fromLit}`;
|
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" >= ${fromStart}`;
|
||||||
} else if (hasTo) {
|
} else if (hasTo) {
|
||||||
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" <= ${toLit}`;
|
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment" WHERE "createdAt" <= ${toNextStart}`;
|
||||||
} else {
|
} else {
|
||||||
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment"`;
|
collSql = `SELECT COALESCE(SUM("totalPaid"),0)::numeric(14,2) AS collected FROM "Payment"`;
|
||||||
}
|
}
|
||||||
@@ -262,7 +246,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit} AND pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" >= ${fromStart} AND pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
||||||
@@ -275,7 +259,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" >= ${fromLit}
|
WHERE pay."createdAt" >= ${fromStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
||||||
@@ -288,7 +272,7 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
SUM(pay."totalPaid")::numeric(14,2) AS total_paid,
|
||||||
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
SUM(pay."totalAdjusted")::numeric(14,2) AS total_adjusted
|
||||||
FROM "Payment" pay
|
FROM "Payment" pay
|
||||||
WHERE pay."createdAt" <= ${toLit}
|
WHERE pay."createdAt" <= ${toNextStart}
|
||||||
GROUP BY pay."patientId"
|
GROUP BY pay."patientId"
|
||||||
) t
|
) t
|
||||||
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
WHERE (COALESCE(t.total_charges,0) - COALESCE(t.total_paid,0) - COALESCE(t.total_adjusted,0)) > 0
|
||||||
@@ -355,17 +339,19 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
|
|
||||||
const hasFrom = from !== undefined && from !== null;
|
const hasFrom = from !== undefined && from !== null;
|
||||||
const hasTo = to !== undefined && to !== null;
|
const hasTo = to !== undefined && to !== null;
|
||||||
const fromLit = fmtDateLiteral(from);
|
|
||||||
const toLit = fmtDateLiteral(to);
|
// Use inclusive start-of-day for 'from' and exclusive start-of-next-day for 'to'
|
||||||
|
const fromStart = isoStartOfDayLiteral(from); // 'YYYY-MM-DDT00:00:00.000Z'
|
||||||
|
const toNextStart = isoStartOfNextDayLiteral(to); // 'YYYY-MM-DDT00:00:00.000Z' of next day
|
||||||
|
|
||||||
// Build payment subquery (aggregated payments by patient, filtered by createdAt if provided)
|
// Build payment subquery (aggregated payments by patient, filtered by createdAt if provided)
|
||||||
const paymentWhereClause =
|
const paymentWhereClause =
|
||||||
hasFrom && hasTo
|
hasFrom && hasTo
|
||||||
? `WHERE pay."createdAt" >= ${fromLit} AND pay."createdAt" <= ${toLit}`
|
? `WHERE pay."createdAt" >= ${fromStart} AND pay."createdAt" <= ${toNextStart}`
|
||||||
: hasFrom
|
: hasFrom
|
||||||
? `WHERE pay."createdAt" >= ${fromLit}`
|
? `WHERE pay."createdAt" >= ${fromStart}`
|
||||||
: hasTo
|
: hasTo
|
||||||
? `WHERE pay."createdAt" <= ${toLit}`
|
? `WHERE pay."createdAt" <= ${toNextStart}`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const pmSubquery = `
|
const pmSubquery = `
|
||||||
@@ -562,17 +548,19 @@ export const paymentsReportsStorage: IPaymentsReportsStorage = {
|
|||||||
|
|
||||||
const hasFrom = from !== undefined && from !== null;
|
const hasFrom = from !== undefined && from !== null;
|
||||||
const hasTo = to !== undefined && to !== null;
|
const hasTo = to !== undefined && to !== null;
|
||||||
const fromLit = fmtDateLiteral(from);
|
|
||||||
const toLit = fmtDateLiteral(to);
|
// Use inclusive start-of-day for 'from' and exclusive start-of-next-day for 'to'
|
||||||
|
const fromStart = isoStartOfDayLiteral(from); // 'YYYY-MM-DDT00:00:00.000Z'
|
||||||
|
const toNextStart = isoStartOfNextDayLiteral(to); // 'YYYY-MM-DDT00:00:00.000Z' of next day
|
||||||
|
|
||||||
// Filter payments by createdAt (time window) when provided
|
// Filter payments by createdAt (time window) when provided
|
||||||
const paymentTimeFilter =
|
const paymentTimeFilter =
|
||||||
hasFrom && hasTo
|
hasFrom && hasTo
|
||||||
? `AND pay."createdAt" >= ${fromLit} AND pay."createdAt" <= ${toLit}`
|
? `AND pay."createdAt" >= ${fromStart} AND pay."createdAt" <= ${toNextStart}`
|
||||||
: hasFrom
|
: hasFrom
|
||||||
? `AND pay."createdAt" >= ${fromLit}`
|
? `AND pay."createdAt" >= ${fromStart}`
|
||||||
: hasTo
|
: hasTo
|
||||||
? `AND pay."createdAt" <= ${toLit}`
|
? `AND pay."createdAt" <= ${toNextStart}`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
// Keyset predicate must use columns present in the 'patients' CTE rows (alias p).
|
// Keyset predicate must use columns present in the 'patients' CTE rows (alias p).
|
||||||
|
|||||||
@@ -11,35 +11,10 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { DoctorBalancesAndSummary } from "@repo/db/types";
|
||||||
|
|
||||||
type StaffOption = { id: number; name: string };
|
type StaffOption = { id: number; name: string };
|
||||||
|
|
||||||
type BalanceRow = {
|
|
||||||
patientId: number;
|
|
||||||
firstName: string | null;
|
|
||||||
lastName: string | null;
|
|
||||||
totalCharges: number | string;
|
|
||||||
totalPaid: number | string;
|
|
||||||
totalAdjusted: number | string;
|
|
||||||
currentBalance: number | string;
|
|
||||||
lastPaymentDate: string | null;
|
|
||||||
lastAppointmentDate: string | null;
|
|
||||||
patientCreatedAt?: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CollectionsResp = {
|
|
||||||
balances: BalanceRow[];
|
|
||||||
totalCount?: number;
|
|
||||||
nextCursor?: string | null;
|
|
||||||
hasMore?: boolean;
|
|
||||||
summary?: {
|
|
||||||
totalPatients?: number;
|
|
||||||
totalOutstanding?: number;
|
|
||||||
totalCollected?: number;
|
|
||||||
patientsWithBalance?: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function fmtCurrency(v: number) {
|
function fmtCurrency(v: number) {
|
||||||
return new Intl.NumberFormat("en-US", {
|
return new Intl.NumberFormat("en-US", {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
@@ -85,7 +60,7 @@ export default function CollectionsByDoctorReport({
|
|||||||
isError: isErrorRows,
|
isError: isErrorRows,
|
||||||
refetch,
|
refetch,
|
||||||
isFetching,
|
isFetching,
|
||||||
} = useQuery<CollectionsResp, Error>({
|
} = useQuery<DoctorBalancesAndSummary, Error>({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
"collections-by-doctor-rows",
|
"collections-by-doctor-rows",
|
||||||
staffId,
|
staffId,
|
||||||
@@ -115,7 +90,6 @@ export default function CollectionsByDoctorReport({
|
|||||||
return res.json();
|
return res.json();
|
||||||
},
|
},
|
||||||
enabled: Boolean(staffId), // only load when a doctor is selected
|
enabled: Boolean(staffId), // only load when a doctor is selected
|
||||||
staleTime: 30_000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const balances = collectionData?.balances ?? [];
|
const balances = collectionData?.balances ?? [];
|
||||||
@@ -152,7 +126,7 @@ export default function CollectionsByDoctorReport({
|
|||||||
// Map server rows to GenericRow
|
// Map server rows to GenericRow
|
||||||
const genericRows: GenericRow[] = balances.map((r) => {
|
const genericRows: GenericRow[] = balances.map((r) => {
|
||||||
const totalCharges = Number(r.totalCharges ?? 0);
|
const totalCharges = Number(r.totalCharges ?? 0);
|
||||||
const totalPayments = Number(r.totalPaid ?? 0);
|
const totalPayments = Number(r.totalPayments ?? 0);
|
||||||
const currentBalance = Number(r.currentBalance ?? 0);
|
const currentBalance = Number(r.currentBalance ?? 0);
|
||||||
const name = `${r.firstName ?? ""} ${r.lastName ?? ""}`.trim() || "Unknown";
|
const name = `${r.firstName ?? ""} ${r.lastName ?? ""}`.trim() || "Unknown";
|
||||||
|
|
||||||
@@ -169,7 +143,9 @@ export default function CollectionsByDoctorReport({
|
|||||||
<div>
|
<div>
|
||||||
<div className="mb-4 grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="mb-4 grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-gray-700 block mb-1">Doctor</label>
|
<label className="text-sm text-gray-700 block mb-1 ml-2">
|
||||||
|
Select Doctor
|
||||||
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={staffId || undefined}
|
value={staffId || undefined}
|
||||||
onValueChange={(v) => setStaffId(v)}
|
onValueChange={(v) => setStaffId(v)}
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export default function PatientsWithBalanceReport({
|
|||||||
return res.json();
|
return res.json();
|
||||||
},
|
},
|
||||||
enabled: true,
|
enabled: true,
|
||||||
staleTime: 30_000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const balances = data?.balances ?? [];
|
const balances = data?.balances ?? [];
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export default function SummaryCards({
|
|||||||
return res.json();
|
return res.json();
|
||||||
},
|
},
|
||||||
enabled: Boolean(startDate && endDate),
|
enabled: Boolean(startDate && endDate),
|
||||||
staleTime: 30_000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPatients = data?.totalPatients ?? 0;
|
const totalPatients = data?.totalPatients ?? 0;
|
||||||
|
|||||||
@@ -8,4 +8,25 @@ export interface PatientBalanceRow {
|
|||||||
currentBalance: number;
|
currentBalance: number;
|
||||||
lastPaymentDate: string | null;
|
lastPaymentDate: string | null;
|
||||||
lastAppointmentDate: string | null;
|
lastAppointmentDate: string | null;
|
||||||
}
|
patientCreatedAt?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetPatientBalancesResult {
|
||||||
|
balances: PatientBalanceRow[];
|
||||||
|
totalCount: number;
|
||||||
|
nextCursor: string | null;
|
||||||
|
hasMore: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DoctorBalancesAndSummary {
|
||||||
|
balances: PatientBalanceRow[];
|
||||||
|
totalCount: number;
|
||||||
|
nextCursor: string | null;
|
||||||
|
hasMore: boolean;
|
||||||
|
summary: {
|
||||||
|
totalPatients: number;
|
||||||
|
totalOutstanding: number;
|
||||||
|
totalCollected: number;
|
||||||
|
patientsWithBalance: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user