fix: UnitedDH pre-auth number extraction, service lines saved, RCT combo buttons
- Pre-auth step9: scan all table cells alphanumeric-first (A0260616190876 format), URL extraction, body scan fallback with debug logging - Pre-auth route: save service lines (totalBilled) when creating PREAUTH claim record so claim page shows correct billed amount after selecting patient - Pre-auth processor: read pdf_path fallback alongside pdf_url from Selenium result - UnitedDH/SCO workers: billing entity selection via direct paymentGroupId click, Summit Dental Care first with fallback, Escape to close dropdown - Pre-auth form: remove Other Insurance step (not present on pre-auth page), file upload direct to hidden input without button click - Pre-auth step8: JS click + Submit Authorization in XPath, Continue via [last()] + JS click - RCT combo buttons added (pre-auth form only): RCT Ant/Post/Crown, PreM/Post/Crown, Mol/Post/Crown; claim form excludes these three combos Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -148,7 +148,7 @@ export async function runUnitedDHPreAuthProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const preAuthNumber: string | undefined = seleniumResult.preAuthNumber ?? undefined;
|
const preAuthNumber: string | undefined = seleniumResult.preAuthNumber ?? undefined;
|
||||||
const pdf_url: string | undefined = seleniumResult.pdf_url ?? undefined;
|
const pdf_url: string | undefined = seleniumResult.pdf_url ?? seleniumResult.pdf_path ?? undefined;
|
||||||
|
|
||||||
if (claimId) {
|
if (claimId) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -62,6 +62,24 @@ router.post("/uniteddh-preauth", async (req: Request, res: Response): Promise<an
|
|||||||
const dob = claimData.dateOfBirth
|
const dob = claimData.dateOfBirth
|
||||||
? new Date(claimData.dateOfBirth)
|
? new Date(claimData.dateOfBirth)
|
||||||
: new Date("2000-01-01");
|
: new Date("2000-01-01");
|
||||||
|
|
||||||
|
const rawLines = Array.isArray(claimData.serviceLines) ? claimData.serviceLines : [];
|
||||||
|
const serviceLinesInput = rawLines.length > 0
|
||||||
|
? {
|
||||||
|
create: rawLines.map((sl: any) => ({
|
||||||
|
procedureCode: sl.procedureCode ?? "",
|
||||||
|
procedureDate: sl.procedureDate ? new Date(sl.procedureDate) : serviceDate,
|
||||||
|
quad: sl.quad ?? "",
|
||||||
|
arch: sl.arch ?? "",
|
||||||
|
toothNumber: sl.toothNumber ?? "",
|
||||||
|
toothSurface: sl.toothSurface ?? "",
|
||||||
|
totalBilled: Number(sl.totalBilled ?? 0),
|
||||||
|
totalAdjusted: Number(sl.totalAdjusted ?? 0),
|
||||||
|
totalPaid: Number(sl.totalPaid ?? 0),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const record = await storage.createClaim({
|
const record = await storage.createClaim({
|
||||||
patientId: Number(claimData.patientId),
|
patientId: Number(claimData.patientId),
|
||||||
appointmentId: claimData.appointmentId ? Number(claimData.appointmentId) : null,
|
appointmentId: claimData.appointmentId ? Number(claimData.appointmentId) : null,
|
||||||
@@ -75,9 +93,10 @@ router.post("/uniteddh-preauth", async (req: Request, res: Response): Promise<an
|
|||||||
serviceDate,
|
serviceDate,
|
||||||
insuranceProvider: "United/DentalHub",
|
insuranceProvider: "United/DentalHub",
|
||||||
status: "PREAUTH",
|
status: "PREAUTH",
|
||||||
|
...(serviceLinesInput ? { serviceLines: serviceLinesInput } : {}),
|
||||||
} as any);
|
} as any);
|
||||||
claimId = record.id;
|
claimId = record.id;
|
||||||
console.log(`[uniteddh-preauth route] created claim record id=${claimId}`);
|
console.log(`[uniteddh-preauth route] created claim record id=${claimId} with ${rawLines.length} service line(s)`);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("[uniteddh-preauth route] failed to create claim record:", e?.message);
|
console.error("[uniteddh-preauth route] failed to create claim record:", e?.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2342,6 +2342,7 @@ export function ClaimForm({
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<RegularComboButtons
|
<RegularComboButtons
|
||||||
|
excludeIds={["rctAnteriorPostCrown", "rctPremolarPostCrown", "rctMolarPostCrown"]}
|
||||||
onRegularCombo={(comboKey) => {
|
onRegularCombo={(comboKey) => {
|
||||||
setForm((prev) => {
|
setForm((prev) => {
|
||||||
const next = applyComboToForm(
|
const next = applyComboToForm(
|
||||||
|
|||||||
@@ -226,16 +226,31 @@ export const PROCEDURE_COMBOS = {
|
|||||||
label: "RCT Anterior",
|
label: "RCT Anterior",
|
||||||
codes: ["D3310"],
|
codes: ["D3310"],
|
||||||
},
|
},
|
||||||
|
rctAnteriorPostCrown: {
|
||||||
|
id: "rctAnteriorPostCrown",
|
||||||
|
label: "RCT Ant/Post/Crown",
|
||||||
|
codes: ["D3310", "D2954", "D2740"],
|
||||||
|
},
|
||||||
rctPremolar: {
|
rctPremolar: {
|
||||||
id: "rctPremolar",
|
id: "rctPremolar",
|
||||||
label: "RCT PreM",
|
label: "RCT PreM",
|
||||||
codes: ["D3320"],
|
codes: ["D3320"],
|
||||||
},
|
},
|
||||||
|
rctPremolarPostCrown: {
|
||||||
|
id: "rctPremolarPostCrown",
|
||||||
|
label: "RCT PreM/Post/Crown",
|
||||||
|
codes: ["D3320", "D2954", "D2740"],
|
||||||
|
},
|
||||||
rctMolar: {
|
rctMolar: {
|
||||||
id: "rctMolar",
|
id: "rctMolar",
|
||||||
label: "RCT Molar",
|
label: "RCT Molar",
|
||||||
codes: ["D3330"],
|
codes: ["D3330"],
|
||||||
},
|
},
|
||||||
|
rctMolarPostCrown: {
|
||||||
|
id: "rctMolarPostCrown",
|
||||||
|
label: "RCT Mol/Post/Crown",
|
||||||
|
codes: ["D3330", "D2954", "D2740"],
|
||||||
|
},
|
||||||
postCore: {
|
postCore: {
|
||||||
id: "postCore",
|
id: "postCore",
|
||||||
label: "Post/Core",
|
label: "Post/Core",
|
||||||
@@ -338,7 +353,7 @@ export const COMBO_CATEGORIES = {
|
|||||||
"plResin",
|
"plResin",
|
||||||
"plCast",
|
"plCast",
|
||||||
],
|
],
|
||||||
Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore", "coreBU"],
|
Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"],
|
||||||
Prosthodontics: ["crown"],
|
Prosthodontics: ["crown"],
|
||||||
Periodontics: ["deepCleaning"],
|
Periodontics: ["deepCleaning"],
|
||||||
Extractions: [
|
Extractions: [
|
||||||
|
|||||||
@@ -261,16 +261,31 @@ export const PROCEDURE_COMBOS: Record<
|
|||||||
label: "RCT Anterior",
|
label: "RCT Anterior",
|
||||||
codes: ["D3310"],
|
codes: ["D3310"],
|
||||||
},
|
},
|
||||||
|
rctAnteriorPostCrown: {
|
||||||
|
id: "rctAnteriorPostCrown",
|
||||||
|
label: "RCT Ant/Post/Crown",
|
||||||
|
codes: ["D3310", "D2954", "D2740"],
|
||||||
|
},
|
||||||
rctPremolar: {
|
rctPremolar: {
|
||||||
id: "rctPremolar",
|
id: "rctPremolar",
|
||||||
label: "RCT PreM",
|
label: "RCT PreM",
|
||||||
codes: ["D3320"],
|
codes: ["D3320"],
|
||||||
},
|
},
|
||||||
|
rctPremolarPostCrown: {
|
||||||
|
id: "rctPremolarPostCrown",
|
||||||
|
label: "RCT PreM/Post/Crown",
|
||||||
|
codes: ["D3320", "D2954", "D2740"],
|
||||||
|
},
|
||||||
rctMolar: {
|
rctMolar: {
|
||||||
id: "rctMolar",
|
id: "rctMolar",
|
||||||
label: "RCT Molar",
|
label: "RCT Molar",
|
||||||
codes: ["D3330"],
|
codes: ["D3330"],
|
||||||
},
|
},
|
||||||
|
rctMolarPostCrown: {
|
||||||
|
id: "rctMolarPostCrown",
|
||||||
|
label: "RCT Mol/Post/Crown",
|
||||||
|
codes: ["D3330", "D2954", "D2740"],
|
||||||
|
},
|
||||||
postCore: {
|
postCore: {
|
||||||
id: "postCore",
|
id: "postCore",
|
||||||
label: "Post/Core",
|
label: "Post/Core",
|
||||||
@@ -380,7 +395,7 @@ export const COMBO_CATEGORIES: Record<
|
|||||||
"plCast",
|
"plCast",
|
||||||
],
|
],
|
||||||
Implants: ["implantFull", "implantFixture", "implantAbutment", "implantCrown"],
|
Implants: ["implantFull", "implantFixture", "implantAbutment", "implantCrown"],
|
||||||
Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore", "coreBU"],
|
Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"],
|
||||||
Prosthodontics: ["crown"],
|
Prosthodontics: ["crown"],
|
||||||
Periodontics: ["deepCleaning"],
|
Periodontics: ["deepCleaning"],
|
||||||
Extractions: [
|
Extractions: [
|
||||||
|
|||||||
@@ -610,22 +610,34 @@ class AutomationUnitedDHClaimSubmit:
|
|||||||
print("[UnitedDH Claim] step1: Selecting Billing Entity...")
|
print("[UnitedDH Claim] step1: Selecting Billing Entity...")
|
||||||
billing_selected = False
|
billing_selected = False
|
||||||
try:
|
try:
|
||||||
billing_ng = WebDriverWait(self.driver, 10).until(
|
taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId")
|
||||||
EC.element_to_be_clickable((By.XPATH,
|
if taxonomy_input.is_displayed():
|
||||||
"//label[@for='paymentGroupId']/following-sibling::ng-select | "
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input)
|
||||||
"//label[@for='paymentGroupId']/..//ng-select"
|
taxonomy_input.click()
|
||||||
))
|
print("[UnitedDH Claim] step1: Clicked Billing Entity dropdown")
|
||||||
)
|
time.sleep(1)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng)
|
try:
|
||||||
arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
|
summit_option = WebDriverWait(self.driver, 5).until(
|
||||||
arrow.click()
|
EC.element_to_be_clickable((By.XPATH,
|
||||||
first_option = WebDriverWait(self.driver, 5).until(
|
"//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]"
|
||||||
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
|
))
|
||||||
)
|
)
|
||||||
option_text = first_option.text.strip()
|
summit_option.click()
|
||||||
first_option.click()
|
print("[UnitedDH Claim] step1: Selected Billing Entity: Summit Dental Care")
|
||||||
print(f"[UnitedDH Claim] step1: Selected Billing Entity: {option_text}")
|
billing_selected = True
|
||||||
billing_selected = True
|
except TimeoutException:
|
||||||
|
try:
|
||||||
|
first_option = self.driver.find_element(By.XPATH,
|
||||||
|
"//ng-dropdown-panel//div[contains(@class,'ng-option')]"
|
||||||
|
)
|
||||||
|
option_text = first_option.text.strip()
|
||||||
|
first_option.click()
|
||||||
|
print(f"[UnitedDH Claim] step1: Selected Billing Entity: {option_text}")
|
||||||
|
billing_selected = True
|
||||||
|
except Exception:
|
||||||
|
print("[UnitedDH Claim] step1: No options available in Billing Entity dropdown")
|
||||||
|
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||||
|
time.sleep(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[UnitedDH Claim] step1: Billing Entity selection failed: {e}")
|
print(f"[UnitedDH Claim] step1: Billing Entity selection failed: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -566,22 +566,34 @@ class AutomationUnitedDHPreAuth:
|
|||||||
print("[UnitedDH PreAuth] step1: Selecting Billing Entity...")
|
print("[UnitedDH PreAuth] step1: Selecting Billing Entity...")
|
||||||
billing_selected = False
|
billing_selected = False
|
||||||
try:
|
try:
|
||||||
billing_ng = WebDriverWait(self.driver, 10).until(
|
taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId")
|
||||||
EC.element_to_be_clickable((By.XPATH,
|
if taxonomy_input.is_displayed():
|
||||||
"//label[@for='paymentGroupId']/following-sibling::ng-select | "
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input)
|
||||||
"//label[@for='paymentGroupId']/..//ng-select"
|
taxonomy_input.click()
|
||||||
))
|
print("[UnitedDH PreAuth] step1: Clicked Billing Entity dropdown")
|
||||||
)
|
time.sleep(1)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng)
|
try:
|
||||||
arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
|
summit_option = WebDriverWait(self.driver, 5).until(
|
||||||
arrow.click()
|
EC.element_to_be_clickable((By.XPATH,
|
||||||
first_option = WebDriverWait(self.driver, 5).until(
|
"//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]"
|
||||||
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
|
))
|
||||||
)
|
)
|
||||||
option_text = first_option.text.strip()
|
summit_option.click()
|
||||||
first_option.click()
|
print("[UnitedDH PreAuth] step1: Selected Billing Entity: Summit Dental Care")
|
||||||
print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {option_text}")
|
billing_selected = True
|
||||||
billing_selected = True
|
except TimeoutException:
|
||||||
|
try:
|
||||||
|
first_option = self.driver.find_element(By.XPATH,
|
||||||
|
"//ng-dropdown-panel//div[contains(@class,'ng-option')]"
|
||||||
|
)
|
||||||
|
option_text = first_option.text.strip()
|
||||||
|
first_option.click()
|
||||||
|
print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {option_text}")
|
||||||
|
billing_selected = True
|
||||||
|
except Exception:
|
||||||
|
print("[UnitedDH PreAuth] step1: No options available in Billing Entity dropdown")
|
||||||
|
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||||
|
time.sleep(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[UnitedDH PreAuth] step1: Billing Entity selection failed: {e}")
|
print(f"[UnitedDH PreAuth] step1: Billing Entity selection failed: {e}")
|
||||||
|
|
||||||
@@ -589,9 +601,10 @@ class AutomationUnitedDHPreAuth:
|
|||||||
print("[UnitedDH PreAuth] step1: WARNING - Could not select Billing Entity")
|
print("[UnitedDH PreAuth] step1: WARNING - Could not select Billing Entity")
|
||||||
|
|
||||||
continue_btn2 = WebDriverWait(self.driver, 10).until(
|
continue_btn2 = WebDriverWait(self.driver, 10).until(
|
||||||
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
|
EC.element_to_be_clickable((By.XPATH, "(//button[contains(normalize-space(.),'Continue')])[last()]"))
|
||||||
)
|
)
|
||||||
continue_btn2.click()
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn2)
|
||||||
|
self.driver.execute_script("arguments[0].click();", continue_btn2)
|
||||||
print("[UnitedDH PreAuth] step1: Clicked Continue (Provider & Location) → Selected Patient page")
|
print("[UnitedDH PreAuth] step1: Clicked Continue (Provider & Location) → Selected Patient page")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
@@ -648,7 +661,7 @@ class AutomationUnitedDHPreAuth:
|
|||||||
|
|
||||||
for by, selector in preauth_selectors:
|
for by, selector in preauth_selectors:
|
||||||
try:
|
try:
|
||||||
btn = WebDriverWait(self.driver, 5).until(
|
btn = WebDriverWait(self.driver, 15).until(
|
||||||
EC.element_to_be_clickable((by, selector))
|
EC.element_to_be_clickable((by, selector))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
|
||||||
@@ -954,23 +967,6 @@ class AutomationUnitedDHPreAuth:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[UnitedDH PreAuth] step6: could not click span Add for row {idx}: {e}")
|
print(f"[UnitedDH PreAuth] step6: could not click span Add for row {idx}: {e}")
|
||||||
|
|
||||||
# Other coverage: click "No" (second radio button)
|
|
||||||
try:
|
|
||||||
print("[UnitedDH PreAuth] step6: selecting 'No' for Other coverage")
|
|
||||||
radio_buttons = WebDriverWait(self.driver, 8).until(
|
|
||||||
lambda d: d.find_elements(By.XPATH, "//input[@type='radio']")
|
|
||||||
)
|
|
||||||
if len(radio_buttons) >= 2:
|
|
||||||
no_radio = radio_buttons[1]
|
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", no_radio)
|
|
||||||
no_radio.click()
|
|
||||||
print("[UnitedDH PreAuth] step6: Clicked 'No' (2nd radio) for Other coverage")
|
|
||||||
else:
|
|
||||||
print(f"[UnitedDH PreAuth] step6: Only {len(radio_buttons)} radio button(s) found — skipping")
|
|
||||||
time.sleep(0.5)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[UnitedDH PreAuth] step6: Could not click 'No' for Other coverage (non-fatal): {e}")
|
|
||||||
|
|
||||||
print("[UnitedDH PreAuth] step6: Done filling pre-auth form")
|
print("[UnitedDH PreAuth] step6: Done filling pre-auth form")
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|
||||||
@@ -1018,17 +1014,10 @@ class AutomationUnitedDHPreAuth:
|
|||||||
|
|
||||||
print(f"[UnitedDH PreAuth] step7: Attaching: {abs_path}")
|
print(f"[UnitedDH PreAuth] step7: Attaching: {abs_path}")
|
||||||
try:
|
try:
|
||||||
upload_btn = WebDriverWait(self.driver, 10).until(
|
|
||||||
EC.element_to_be_clickable((By.ID, "upload-document"))
|
|
||||||
)
|
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", upload_btn)
|
|
||||||
upload_btn.click()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
file_input = WebDriverWait(self.driver, 8).until(
|
file_input = WebDriverWait(self.driver, 8).until(
|
||||||
EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))
|
EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].style.display='block';", file_input)
|
self.driver.execute_script("arguments[0].removeAttribute('class');", file_input)
|
||||||
file_input.send_keys(abs_path)
|
file_input.send_keys(abs_path)
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
print(f"[UnitedDH PreAuth] step7: Attached: {os.path.basename(abs_path)}")
|
print(f"[UnitedDH PreAuth] step7: Attached: {os.path.basename(abs_path)}")
|
||||||
@@ -1052,18 +1041,16 @@ class AutomationUnitedDHPreAuth:
|
|||||||
|
|
||||||
submit_btn = WebDriverWait(self.driver, 15).until(
|
submit_btn = WebDriverWait(self.driver, 15).until(
|
||||||
EC.element_to_be_clickable((By.XPATH,
|
EC.element_to_be_clickable((By.XPATH,
|
||||||
"//button[contains(normalize-space(.),'Submit Pre-Auth') or "
|
"//button[contains(normalize-space(.),'Submit Authorization') or "
|
||||||
|
"contains(normalize-space(.),'Submit Pre-Auth') or "
|
||||||
"contains(normalize-space(.),'Submit Pre Authorization') or "
|
"contains(normalize-space(.),'Submit Pre Authorization') or "
|
||||||
"contains(normalize-space(.),'Submit Pre-Authorization') or "
|
"contains(normalize-space(.),'Submit Pre-Authorization') or "
|
||||||
"contains(normalize-space(.),'Submit Claim')] | "
|
"contains(normalize-space(.),'Submit Claim')]"
|
||||||
"//button[contains(@class,'btn-primary') and ("
|
|
||||||
"contains(normalize-space(text()),'Submit Pre') or "
|
|
||||||
"contains(normalize-space(text()),'Submit Claim'))]"
|
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn)
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
submit_btn.click()
|
self.driver.execute_script("arguments[0].click();", submit_btn)
|
||||||
print("[UnitedDH PreAuth] step8: Clicked Submit — waiting for post-submit popup")
|
print("[UnitedDH PreAuth] step8: Clicked Submit — waiting for post-submit popup")
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
@@ -1109,33 +1096,68 @@ class AutomationUnitedDHPreAuth:
|
|||||||
time.sleep(4)
|
time.sleep(4)
|
||||||
|
|
||||||
preauth_number = None
|
preauth_number = None
|
||||||
try:
|
|
||||||
first_ref = WebDriverWait(self.driver, 20).until(
|
# 1. Try URL — pre-auth ID often appears in the URL path
|
||||||
EC.presence_of_element_located((By.XPATH,
|
current_url = self.driver.current_url
|
||||||
"(//table//tr[not(th)]/td[2] | "
|
url_match = re.search(r'/(\d{6,})', current_url)
|
||||||
"//table//tr[td]/td[contains(normalize-space(.),'2026') or "
|
if url_match:
|
||||||
" contains(normalize-space(.),'2025')])[1]"
|
preauth_number = url_match.group(1)
|
||||||
))
|
print(f"[UnitedDH PreAuth] step9: Pre-auth number from URL: {preauth_number!r}")
|
||||||
)
|
|
||||||
ref_text = first_ref.text.strip()
|
# 2. Scan all table cells and log them for debugging
|
||||||
match = re.search(r'\b(\d{14})\b', ref_text)
|
if not preauth_number:
|
||||||
if match:
|
try:
|
||||||
preauth_number = match.group(1)
|
all_cells = self.driver.find_elements(By.XPATH, "//table//tr[td]/td")
|
||||||
else:
|
cell_texts = [c.text.strip() for c in all_cells if c.text.strip()]
|
||||||
match = re.search(r'\b(\d{10,})\b', ref_text)
|
print(f"[UnitedDH PreAuth] step9: Table cells: {cell_texts[:20]}")
|
||||||
if match:
|
|
||||||
preauth_number = match.group(1)
|
# Try each cell — alphanumeric first (e.g. A0260616190876), then pure digits
|
||||||
print(f"[UnitedDH PreAuth] step9: Pre-auth number: {preauth_number!r} (cell: {ref_text!r})")
|
for cell_text in cell_texts:
|
||||||
except Exception as e:
|
# Alphanumeric like A0260616190876 or PA12345678
|
||||||
print(f"[UnitedDH PreAuth] step9: Could not read first-row reference number: {e}")
|
m = re.search(r'\b([A-Z]{1,4}\d{6,})\b', cell_text)
|
||||||
|
if m:
|
||||||
|
preauth_number = m.group(1)
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Pre-auth number (alphanumeric): {preauth_number!r}")
|
||||||
|
break
|
||||||
|
# 14-digit pure numeric ref
|
||||||
|
m = re.search(r'\b(\d{14})\b', cell_text)
|
||||||
|
if m:
|
||||||
|
preauth_number = m.group(1)
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Pre-auth number (14-digit): {preauth_number!r}")
|
||||||
|
break
|
||||||
|
# 8-13 digit fallback
|
||||||
|
m = re.search(r'\b(\d{8,13})\b', cell_text)
|
||||||
|
if m:
|
||||||
|
preauth_number = m.group(1)
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Pre-auth number (8-13 digit): {preauth_number!r}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Table cell scan failed: {e}")
|
||||||
|
|
||||||
|
# 3. Full body scan fallback
|
||||||
|
if not preauth_number:
|
||||||
try:
|
try:
|
||||||
body_text = self.driver.find_element(By.TAG_NAME, "body").text
|
body_text = self.driver.find_element(By.TAG_NAME, "body").text
|
||||||
match = re.search(r'\b(\d{14})\b', body_text)
|
print(f"[UnitedDH PreAuth] step9: Body text (first 500 chars): {body_text[:500]!r}")
|
||||||
if match:
|
m = re.search(r'\b([A-Z]{1,4}\d{6,})\b', body_text)
|
||||||
preauth_number = match.group(1)
|
if m:
|
||||||
print(f"[UnitedDH PreAuth] step9: Pre-auth number (body scan): {preauth_number}")
|
preauth_number = m.group(1)
|
||||||
except Exception:
|
else:
|
||||||
pass
|
m = re.search(r'\b(\d{14})\b', body_text)
|
||||||
|
if m:
|
||||||
|
preauth_number = m.group(1)
|
||||||
|
else:
|
||||||
|
m = re.search(r'\b(\d{8,13})\b', body_text)
|
||||||
|
if m:
|
||||||
|
preauth_number = m.group(1)
|
||||||
|
if preauth_number:
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Pre-auth number (body scan): {preauth_number!r}")
|
||||||
|
else:
|
||||||
|
print("[UnitedDH PreAuth] step9: No pre-auth number found in body")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Body scan failed: {e}")
|
||||||
|
|
||||||
|
print(f"[UnitedDH PreAuth] step9: Final pre-auth number: {preauth_number!r}")
|
||||||
|
|
||||||
shared_downloads = os.path.join(_SERVICE_DIR, "downloads")
|
shared_downloads = os.path.join(_SERVICE_DIR, "downloads")
|
||||||
os.makedirs(shared_downloads, exist_ok=True)
|
os.makedirs(shared_downloads, exist_ok=True)
|
||||||
|
|||||||
@@ -670,23 +670,34 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
print("[UnitedSCO step1] Selecting Billing Entity...")
|
print("[UnitedSCO step1] Selecting Billing Entity...")
|
||||||
billing_selected = False
|
billing_selected = False
|
||||||
try:
|
try:
|
||||||
billing_ng = WebDriverWait(self.driver, 10).until(
|
taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId")
|
||||||
EC.element_to_be_clickable((By.XPATH,
|
if taxonomy_input.is_displayed():
|
||||||
"//label[@for='paymentGroupId']/following-sibling::ng-select | "
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input)
|
||||||
"//label[@for='paymentGroupId']/..//ng-select"
|
taxonomy_input.click()
|
||||||
))
|
print("[UnitedSCO step1] Clicked Billing Entity dropdown")
|
||||||
)
|
time.sleep(1)
|
||||||
# Center in viewport so panel opens downward instead of upward
|
try:
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng)
|
summit_option = WebDriverWait(self.driver, 5).until(
|
||||||
arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
|
EC.element_to_be_clickable((By.XPATH,
|
||||||
arrow.click()
|
"//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]"
|
||||||
first_option = WebDriverWait(self.driver, 5).until(
|
))
|
||||||
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
|
)
|
||||||
)
|
summit_option.click()
|
||||||
option_text = first_option.text.strip()
|
print("[UnitedSCO step1] Selected Billing Entity: Summit Dental Care")
|
||||||
first_option.click()
|
billing_selected = True
|
||||||
print(f"[UnitedSCO step1] Selected Billing Entity: {option_text}")
|
except TimeoutException:
|
||||||
billing_selected = True
|
try:
|
||||||
|
first_option = self.driver.find_element(By.XPATH,
|
||||||
|
"//ng-dropdown-panel//div[contains(@class,'ng-option')]"
|
||||||
|
)
|
||||||
|
option_text = first_option.text.strip()
|
||||||
|
first_option.click()
|
||||||
|
print(f"[UnitedSCO step1] Selected Billing Entity: {option_text}")
|
||||||
|
billing_selected = True
|
||||||
|
except Exception:
|
||||||
|
print("[UnitedSCO step1] No options available in Billing Entity dropdown")
|
||||||
|
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||||
|
time.sleep(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[UnitedSCO step1] Billing Entity selection failed: {e}")
|
print(f"[UnitedSCO step1] Billing Entity selection failed: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user