- Claims & Payments: save npiProviderId when submitting MH claim; sync between claim and payment on update - Claims table: add Provider column showing rendering provider name - Payments table: add Provider column + purple Commissioned badge on status - Claim edit modal: add Rendering Provider dropdown (defaults to Mary Scannell) - Payment edit modal: add Rendering Provider dropdown + Commissioned metadata display - Reports page: add Provider filter dropdown (dynamic from NPI providers settings) - Reports page: remove Collections by Doctor report type and Select Doctor dropdown - Commission section: new section in reports page with date range + provider filter, shows eligible paid claims/payments per provider, multi-select checkboxes, Pay Commission modal with print + save, marks payments as commissioned so they are excluded from future cycles - DB: add CommissionBatch and CommissionBatchItem tables; backfill Payment.npiProviderId from linked claims - Backend: PATCH /api/payments/:id/provider syncs to linked claim; PUT /api/claims/:id syncs to linked payment; new /api/commissions routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
158 lines
4.8 KiB
TypeScript
Executable File
158 lines
4.8 KiB
TypeScript
Executable File
import React from "react";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { Calendar } from "lucide-react";
|
|
import { formatLocalDate, parseLocalDate } from "@/utils/dateUtils";
|
|
import { DateInput } from "@/components/ui/dateInput";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { apiRequest } from "@/lib/queryClient";
|
|
import { NpiProvider } from "@repo/db/types";
|
|
|
|
type ReportType =
|
|
| "patients_with_balance"
|
|
| "patients_no_balance"
|
|
| "monthly_collections"
|
|
| "procedure_codes_by_doctor"
|
|
| "payment_methods"
|
|
| "insurance_vs_patient_payments"
|
|
| "aging_report";
|
|
|
|
export default function ReportConfig({
|
|
startDate,
|
|
endDate,
|
|
setStartDate,
|
|
setEndDate,
|
|
selectedReportType,
|
|
setSelectedReportType,
|
|
npiProviderId,
|
|
setNpiProviderId,
|
|
}: {
|
|
startDate: string;
|
|
endDate: string;
|
|
setStartDate: (s: string) => void;
|
|
setEndDate: (s: string) => void;
|
|
selectedReportType: ReportType;
|
|
setSelectedReportType: (r: ReportType) => void;
|
|
npiProviderId: number | null;
|
|
setNpiProviderId: (id: number | null) => void;
|
|
}) {
|
|
let startDateObj: Date | null = null;
|
|
if (startDate) {
|
|
try { startDateObj = parseLocalDate(startDate); } catch { startDateObj = null; }
|
|
}
|
|
|
|
let endDateObj: Date | null = null;
|
|
if (endDate) {
|
|
try { endDateObj = parseLocalDate(endDate); } catch { endDateObj = null; }
|
|
}
|
|
|
|
const { data: npiProviders = [] } = useQuery<NpiProvider[]>({
|
|
queryKey: ["/api/npiProviders/"],
|
|
queryFn: async () => {
|
|
const res = await apiRequest("GET", "/api/npiProviders/");
|
|
return res.json();
|
|
},
|
|
staleTime: 60_000,
|
|
});
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Calendar className="h-5 w-5" /> Report Configuration
|
|
</CardTitle>
|
|
</CardHeader>
|
|
|
|
<CardContent className="space-y-4">
|
|
<div className="text-sm text-gray-500">
|
|
Choose the report type, date range, and provider.
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
<div>
|
|
<DateInput
|
|
label="Start Date"
|
|
value={startDateObj}
|
|
onChange={(d) => setStartDate(d ? formatLocalDate(d) : "")}
|
|
disableFuture
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<DateInput
|
|
label="End Date"
|
|
value={endDateObj}
|
|
onChange={(d) => setEndDate(d ? formatLocalDate(d) : "")}
|
|
disableFuture
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label>Provider</Label>
|
|
<Select
|
|
value={npiProviderId?.toString() ?? "all"}
|
|
onValueChange={(v) =>
|
|
setNpiProviderId(v === "all" ? null : Number(v))
|
|
}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="All Providers" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="all">All Providers</SelectItem>
|
|
{npiProviders.map((p) => (
|
|
<SelectItem key={p.id} value={p.id.toString()}>
|
|
{p.providerName}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="report-type">Report Type</Label>
|
|
<Select
|
|
value={selectedReportType}
|
|
onValueChange={(v) => setSelectedReportType(v as ReportType)}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select report type" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="patients_with_balance">
|
|
Patients with Outstanding Balance
|
|
</SelectItem>
|
|
<SelectItem value="patients_no_balance">
|
|
Patients with Zero Balance
|
|
</SelectItem>
|
|
<SelectItem value="monthly_collections">
|
|
Monthly Collections Summary
|
|
</SelectItem>
|
|
<SelectItem value="procedure_codes_by_doctor">
|
|
Procedure Codes by Doctor
|
|
</SelectItem>
|
|
<SelectItem value="payment_methods">
|
|
Payment Methods Breakdown
|
|
</SelectItem>
|
|
<SelectItem value="insurance_vs_patient_payments">
|
|
Insurance vs Patient Payments
|
|
</SelectItem>
|
|
<SelectItem value="aging_report">
|
|
Accounts Receivable Aging
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|