feat: MassHealth PDF import auto-pays full balance + patient name fix

- PDF import now marks payments as PAID when MassHealth patient's
  mhPaidAmount >= totalBilled (no patient balance)
- Newly created patients from MH vouchers get insuranceProvider = 'MassHealth'
- Existing patients with blank insuranceProvider get it filled on import
- Fix: update patient name from PDF if existing record has empty name
- Various frontend/selenium/route updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 00:16:31 -04:00
co-authored by Claude Sonnet 4.6
parent 9efe5c8469
commit b7e06adf2f
15 changed files with 611 additions and 448 deletions
+108 -53
View File
@@ -31,12 +31,21 @@ import { apiRequest } from "@/lib/queryClient";
import { toast } from "@/hooks/use-toast";
import PaymentEditModal from "@/components/payments/payment-edit-modal";
function formatDateInput(raw: string): string {
const digits = raw.replace(/\D/g, "").slice(0, 8);
if (digits.length <= 2) return digits;
if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
}
export default function PaymentsPage() {
const [paymentPeriod, setPaymentPeriod] = useState<string>("all-time");
// Check Payments Online date range
const [mhFromDate, setMhFromDate] = useState<Date | undefined>(undefined);
const [mhToDate, setMhToDate] = useState<Date | undefined>(undefined);
const [mhFromText, setMhFromText] = useState("");
const [mhToText, setMhToText] = useState("");
const [fromCalendarOpen, setFromCalendarOpen] = useState(false);
const [toCalendarOpen, setToCalendarOpen] = useState(false);
@@ -229,7 +238,7 @@ export default function PaymentsPage() {
{/* Check Payments Online */}
<Card className="mb-6">
<CardHeader>
<CardTitle>Check Payments Online</CardTitle>
<CardTitle>Check MH Payments</CardTitle>
<CardDescription>
Select a date range and check MH payment status online
</CardDescription>
@@ -239,69 +248,115 @@ export default function PaymentsPage() {
{/* From date picker */}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">From:</span>
<Popover open={fromCalendarOpen} onOpenChange={setFromCalendarOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-[160px] justify-start text-left font-normal"
>
<CalendarIcon className="mr-2 h-4 w-4 text-gray-400" />
{mhFromDate
? mhFromDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
: <span className="text-muted-foreground">Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={mhFromDate}
onSelect={(d) => {
setMhFromDate(d);
setFromCalendarOpen(false);
}}
onClose={() => setFromCalendarOpen(false)}
/>
</PopoverContent>
</Popover>
<div className="flex items-center gap-1">
<input
type="text"
placeholder="MM/DD/YYYY"
className="border rounded-md px-2 py-1.5 text-sm w-[110px] focus:outline-none focus:ring-2 focus:ring-ring"
value={mhFromText}
onChange={(e) => {
const formatted = formatDateInput(e.target.value);
setMhFromText(formatted);
if (formatted === "") { setMhFromDate(undefined); return; }
if (/^\d{2}\/\d{2}\/\d{4}$/.test(formatted)) {
const parsed = new Date(formatted);
if (!isNaN(parsed.getTime())) setMhFromDate(parsed);
}
}}
/>
<Popover open={fromCalendarOpen} onOpenChange={setFromCalendarOpen}>
<PopoverTrigger asChild>
<Button type="button" variant="outline" size="icon" className="h-8 w-8 shrink-0">
<CalendarIcon className="h-4 w-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={mhFromDate}
onSelect={(d) => {
setMhFromDate(d);
setMhFromText(d ? d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) : "");
setFromCalendarOpen(false);
}}
onClose={() => setFromCalendarOpen(false)}
/>
</PopoverContent>
</Popover>
</div>
</div>
{/* To date picker */}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">To:</span>
<Popover open={toCalendarOpen} onOpenChange={setToCalendarOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-[160px] justify-start text-left font-normal"
>
<CalendarIcon className="mr-2 h-4 w-4 text-gray-400" />
{mhToDate
? mhToDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
: <span className="text-muted-foreground">Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={mhToDate}
onSelect={(d) => {
setMhToDate(d);
setToCalendarOpen(false);
}}
onClose={() => setToCalendarOpen(false)}
/>
</PopoverContent>
</Popover>
<div className="flex items-center gap-1">
<input
type="text"
placeholder="MM/DD/YYYY"
className="border rounded-md px-2 py-1.5 text-sm w-[110px] focus:outline-none focus:ring-2 focus:ring-ring"
value={mhToText}
onChange={(e) => {
const formatted = formatDateInput(e.target.value);
setMhToText(formatted);
if (formatted === "") { setMhToDate(undefined); return; }
if (/^\d{2}\/\d{2}\/\d{4}$/.test(formatted)) {
const parsed = new Date(formatted);
if (!isNaN(parsed.getTime())) setMhToDate(parsed);
}
}}
/>
<Popover open={toCalendarOpen} onOpenChange={setToCalendarOpen}>
<PopoverTrigger asChild>
<Button type="button" variant="outline" size="icon" className="h-8 w-8 shrink-0">
<CalendarIcon className="h-4 w-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={mhToDate}
onSelect={(d) => {
setMhToDate(d);
setMhToText(d ? d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) : "");
setToCalendarOpen(false);
}}
onClose={() => setToCalendarOpen(false)}
/>
</PopoverContent>
</Popover>
</div>
</div>
{/* Check All MH Payment button */}
{/* MH Batch Payment Check button */}
<Button
variant="default"
onClick={() => {
// Logic to be defined later
onClick={async () => {
if (!mhFromDate || !mhToDate) {
toast({ title: "Please select both From and To dates", variant: "destructive" });
return;
}
try {
const res = await apiRequest("POST", "/api/payments/mh-batch-payment-check", {
fromDate: mhFromDate.toISOString().split("T")[0],
toDate: mhToDate.toISOString().split("T")[0],
});
if (!res.ok) {
const err = await res.json();
toast({ title: "MH Batch Payment Check Failed", description: err.message, variant: "destructive" });
} else {
const result = await res.json();
if (result.noResults) {
toast({ title: "No Results", description: result.message, variant: "destructive" });
} else {
toast({ title: "MH Batch Payment Check", description: result.importSummary ?? result.message ?? "Done" });
}
}
} catch (e: any) {
toast({ title: "Error", description: e.message, variant: "destructive" });
}
}}
>
Check All MH Payment
Go
</Button>
</div>
</CardContent>