feat: United Ucard name+DOB eligibility search via chat, duplicate-record handling, Product ID as member ID

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 22:30:01 -04:00
co-authored by Claude Sonnet 5
parent ce25cdd2a2
commit 7ec82902d9
7 changed files with 366 additions and 64 deletions
@@ -66,12 +66,16 @@ export interface UnitedUcardEligibilityProcessorResult {
async function processUnitedUcardResult(
userId: number,
insuranceId: string,
searchedInsuranceId: string,
formFirstName: string | undefined,
formLastName: string | undefined,
formDob: string | undefined,
seleniumResult: any
): Promise<UnitedUcardEligibilityProcessorResult> {
// Name-search jobs have no member ID up front; the worker returns the page's Product ID instead.
const scrapedId =
typeof seleniumResult?.memberId === "string" ? seleniumResult.memberId.replace(/\s+/g, "") : "";
const insuranceId = searchedInsuranceId || scrapedId;
const output: UnitedUcardEligibilityProcessorResult = {};
let createdPdfFileId: number | string | null = null;
@@ -82,21 +86,50 @@ async function processUnitedUcardResult(
? seleniumResult.patientName.trim()
: null;
const { firstName, lastName } = rawName
// Name-search jobs (no member ID) always use the names the user typed.
const { firstName, lastName } = rawName && searchedInsuranceId
? splitName(rawName)
: { firstName: formFirstName ?? "", lastName: formLastName ?? "" };
// 2) Create / update patient
await createOrUpdatePatientByInsuranceId({
insuranceId,
firstName,
lastName,
dob: formDob,
userId,
});
// 3) Fetch patient
const patient = await storage.getPatientByInsuranceId(insuranceId);
// 2-3) Resolve the patient record.
// Name + DOB searches have no member ID (the portal masks it), so we can only
// update a patient that is already on file — match on first/last name + DOB.
let patient: any = null;
if (insuranceId) {
await createOrUpdatePatientByInsuranceId({
insuranceId,
firstName,
lastName,
dob: formDob,
userId,
});
patient = await storage.getPatientByInsuranceId(insuranceId);
} else {
const dobDate = formDob ? new Date(formDob) : null;
const matches = await storage.searchPatients({
filters: {
firstName: { equals: firstName, mode: "insensitive" },
lastName: { equals: lastName, mode: "insensitive" },
...(dobDate && !isNaN(dobDate.getTime()) ? { dateOfBirth: dobDate } : {}),
},
limit: 1,
offset: 0,
});
patient = matches?.[0] ?? null;
if (!patient) {
// The portal masks the subscriber ID, so create the patient from the typed name + DOB
// (no insuranceId); the Member ID can be added later.
patient = await storage.createPatient({
firstName,
lastName,
dateOfBirth: dobDate && !isNaN(dobDate.getTime()) ? dobDate : null,
gender: "",
phone: "",
userId,
} as any);
log("uniteducard-processor", "created patient from name search", { patientId: patient?.id });
}
}
if (!patient?.id) {
output.patientUpdateStatus = "Patient not found; no update performed";
return output;
@@ -115,7 +148,7 @@ async function processUnitedUcardResult(
typeof seleniumResult?.memberId === "string"
? seleniumResult.memberId.replace(/\s+/g, "")
: null;
if (scrapedMemberId && scrapedMemberId !== insuranceId) {
if (insuranceId && scrapedMemberId && scrapedMemberId !== insuranceId) {
statusUpdates.insuranceId = scrapedMemberId;
log("uniteducard-processor", "member ID changed, overwriting on patient record", {
patientId: patient.id,