feat: picking a policy holder copies their insurance, address and status

Selecting a policy holder from the patient search fills the dependent's
insurance type, member ID, group number, status, address, city, state and
zip from the holder's record. Dependents may now share the holder's
insurance ID without the duplicate-ID check blocking the save.
Also teach the AI CDT lookup that "PNAO" is a typo of "pano" (D0330).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 23:13:27 -04:00
co-authored by Claude Opus 5.5
parent db7ea8ef2f
commit fa6c55421c
5 changed files with 41 additions and 5 deletions
+1
View File
@@ -72,6 +72,7 @@ const ALIAS_MAP: Record<string, string> = {
"fmx": "intraoral complete series",
"pano": "panoramic",
"panorex": "panoramic",
"pnao": "panoramic", // common typo of "pano"
"bitewing": "bitewing",
"bw": "bitewing two",
"2bw": "bitewing two",
+8 -2
View File
@@ -223,6 +223,11 @@ router.post("/create-temp", async (req: Request, res: Response): Promise<any> =>
}
});
// Dependents (spouse, children) are covered under the subscriber's member ID, so a
// shared insurance ID is expected when the patient has a policy holder.
const hasPolicyHolder = (p: { policyHolder?: string | null; policyHolderPatientId?: number | null }) =>
p.policyHolderPatientId != null || !!(p.policyHolder ?? "").trim();
// Create a new patient
router.post("/", async (req: Request, res: Response): Promise<any> => {
try {
@@ -242,7 +247,7 @@ router.post("/", async (req: Request, res: Response): Promise<any> => {
const patientData = insertPatientSchema.parse(body);
// Check for duplicate insuranceId if it's provided
if (patientData.insuranceId) {
if (patientData.insuranceId && !hasPolicyHolder(patientData as any)) {
const existingPatient = await storage.getPatientByInsuranceId(
patientData.insuranceId as string
);
@@ -307,7 +312,8 @@ router.put(
// If updating insuranceId, check for uniqueness (excluding self)
if (
patientData.insuranceId &&
patientData.insuranceId !== existingPatient.insuranceId
patientData.insuranceId !== existingPatient.insuranceId &&
!hasPolicyHolder({ ...existingPatient, ...(patientData as any) })
) {
const duplicatePatient = await storage.getPatientByInsuranceId(
patientData.insuranceId as string
@@ -34,6 +34,11 @@ export interface IStorage {
dateOfBirth: Date;
insuranceId: string | null;
insuranceProvider: string | null;
groupNumber: string | null;
address: string | null;
city: string | null;
state: string | null;
zipCode: string | null;
status: string;
}[]
>;
@@ -184,6 +189,11 @@ export const patientsStorage: IStorage = {
dateOfBirth: true,
insuranceId: true,
insuranceProvider: true,
groupNumber: true,
address: true,
city: true,
state: true,
zipCode: true,
status: true,
},
});
@@ -596,9 +596,27 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
value={field.value || ""}
holderPatientId={(form.watch("policyHolderPatientId" as any) as number | null) ?? null}
excludePatientId={patient?.id}
onChange={(name, holderPatientId) => {
onChange={(name, holderPatientId, holder) => {
field.onChange(name);
form.setValue("policyHolderPatientId" as any, holderPatientId, { shouldDirty: true });
// Dependents share the holder's policy, so copy their insurance, member ID and group number
if (holder?.insuranceProvider) {
form.setValue("insuranceProvider", holder.insuranceProvider, { shouldDirty: true });
}
if (holder?.insuranceId) {
form.setValue("insuranceId", String(holder.insuranceId), { shouldDirty: true });
}
if (holder?.groupNumber) {
form.setValue("groupNumber", holder.groupNumber, { shouldDirty: true });
}
// Same policy, so the holder's eligibility status applies to the dependent
if (holder?.status) {
form.setValue("status" as any, holder.status, { shouldDirty: true });
}
// Dependents usually live with the holder, so copy the address too
for (const key of ["address", "city", "state", "zipCode"] as const) {
if (holder?.[key]) form.setValue(key, holder[key], { shouldDirty: true });
}
// Picking someone else means the patient isn't the holder
if (name.trim() && form.getValues("policyHolderRelationship" as any) === "SELF") {
form.setValue("policyHolderRelationship" as any, "", { shouldDirty: true });
@@ -14,7 +14,8 @@ interface PolicyHolderSearchProps {
holderPatientId: number | null;
/** Patient being edited — hidden from the results */
excludePatientId?: number;
onChange: (name: string, holderPatientId: number | null) => void;
/** `holder` is the picked patient record, only passed when one is chosen from the results */
onChange: (name: string, holderPatientId: number | null, holder?: Patient) => void;
}
// Type to search patients in the DB and pick the policy holder; free text is still allowed
@@ -97,7 +98,7 @@ export function PolicyHolderSearch({ value, holderPatientId, excludePatientId, o
type="button"
className="block w-full px-3 py-2 text-left text-sm hover:bg-gray-100"
onClick={() => {
onChange(`${p.firstName} ${p.lastName}`.trim(), Number(p.id));
onChange(`${p.firstName} ${p.lastName}`.trim(), Number(p.id), p);
setOpen(false);
}}
>