fix: allow letters in insurance ID; add patient state (default MA)
- Backend insurance ID check now accepts letters + digits (BCBS IDs have a letter prefix), matching the frontend schema; create route validates the normalized body - Patient.state column (default "MA") with State select on the patient form; ADA claim form uses the patient's state instead of the office's - Rebuild stale packages/db compiled .js so the runtime schemas include new fields (state, office billing NPI fields, Telnyx settings) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -239,10 +239,7 @@ router.post("/", async (req: Request, res: Response): Promise<any> => {
|
||||
});
|
||||
}
|
||||
// Validate request body
|
||||
const patientData = insertPatientSchema.parse({
|
||||
...req.body,
|
||||
userId: req.user!.id,
|
||||
});
|
||||
const patientData = insertPatientSchema.parse(body);
|
||||
|
||||
// Check for duplicate insuranceId if it's provided
|
||||
if (patientData.insuranceId) {
|
||||
|
||||
@@ -56,6 +56,7 @@ export type AdaClaimInput = {
|
||||
gender?: string | null;
|
||||
address?: string | null;
|
||||
city?: string | null;
|
||||
state?: string | null;
|
||||
zipCode?: string | null;
|
||||
insuranceId?: string | null;
|
||||
groupNumber?: string | null;
|
||||
@@ -209,7 +210,11 @@ async function fillOnePage(
|
||||
|
||||
// 12-23. Subscriber / patient
|
||||
const patientName = `${patient.lastName}, ${patient.firstName}`;
|
||||
const patientCsz = cityStateZip(patient.city, patient.address || patient.city ? officeState : "", patient.zipCode);
|
||||
const patientCsz = cityStateZip(
|
||||
patient.city,
|
||||
patient.state || (patient.address || patient.city ? officeState : ""),
|
||||
patient.zipCode,
|
||||
);
|
||||
const dob = fmtDate(patient.dateOfBirth);
|
||||
const gender = genderCode(patient.gender);
|
||||
const genderBox = { M: "Check Box1", F: "Check Box2", U: "Check Box3" }[gender];
|
||||
|
||||
@@ -9,7 +9,7 @@ export function normalizeInsuranceId(raw: unknown): string | undefined {
|
||||
s = raw;
|
||||
} else {
|
||||
// Not acceptable type
|
||||
throw new Error("Insurance ID must be a numeric string.");
|
||||
throw new Error("Insurance ID must be a string.");
|
||||
}
|
||||
|
||||
// Remove all whitespace
|
||||
@@ -18,9 +18,9 @@ export function normalizeInsuranceId(raw: unknown): string | undefined {
|
||||
// If empty after cleaning, treat as undefined
|
||||
if (cleaned === "") return undefined;
|
||||
|
||||
// Only digits allowed (since you said it's numeric)
|
||||
if (!/^\d+$/.test(cleaned)) {
|
||||
throw new Error("Insurance ID must contain only digits.");
|
||||
// Letters and digits allowed (e.g. BCBS IDs carry a letter prefix)
|
||||
if (!/^[A-Za-z0-9]+$/.test(cleaned)) {
|
||||
throw new Error("Insurance ID must contain only letters and digits.");
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
|
||||
@@ -33,6 +33,15 @@ import {
|
||||
import { z } from "zod";
|
||||
import { DateInputField } from "@/components/ui/dateInputField";
|
||||
|
||||
// New England first (most patients), then the remaining states alphabetically
|
||||
const US_STATES = [
|
||||
"MA", "NH", "RI", "CT", "VT", "ME",
|
||||
"AK", "AL", "AR", "AZ", "CA", "CO", "DC", "DE", "FL", "GA", "HI", "IA",
|
||||
"ID", "IL", "IN", "KS", "KY", "LA", "MD", "MI", "MN", "MO", "MS", "MT",
|
||||
"NC", "ND", "NE", "NJ", "NM", "NV", "NY", "OH", "OK", "OR", "PA", "SC",
|
||||
"SD", "TN", "TX", "UT", "VA", "WA", "WI", "WV", "WY",
|
||||
];
|
||||
|
||||
interface PatientFormProps {
|
||||
patient?: Patient;
|
||||
extractedInfo?: {
|
||||
@@ -102,6 +111,7 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
email: "",
|
||||
address: "",
|
||||
city: "",
|
||||
state: "MA",
|
||||
zipCode: "",
|
||||
insuranceProvider: "",
|
||||
insuranceId: extractedInfo?.insuranceId || "",
|
||||
@@ -366,6 +376,34 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="state"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>State</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
value={(field.value as string) || "MA"}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select state" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{US_STATES.map((st) => (
|
||||
<SelectItem key={st} value={st}>
|
||||
{st}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="zipCode"
|
||||
|
||||
Reference in New Issue
Block a user