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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user