diff --git a/apps/Backend/src/queue/processors/unitedDHPreAuthProcessor.ts b/apps/Backend/src/queue/processors/unitedDHPreAuthProcessor.ts index c52bd9ae..bd131936 100644 --- a/apps/Backend/src/queue/processors/unitedDHPreAuthProcessor.ts +++ b/apps/Backend/src/queue/processors/unitedDHPreAuthProcessor.ts @@ -148,7 +148,7 @@ export async function runUnitedDHPreAuthProcessor( } 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) { try { diff --git a/apps/Backend/src/routes/insuranceStatusUnitedDHPreAuth.ts b/apps/Backend/src/routes/insuranceStatusUnitedDHPreAuth.ts index 06b9baa5..04891a63 100644 --- a/apps/Backend/src/routes/insuranceStatusUnitedDHPreAuth.ts +++ b/apps/Backend/src/routes/insuranceStatusUnitedDHPreAuth.ts @@ -62,6 +62,24 @@ router.post("/uniteddh-preauth", async (req: Request, res: Response): Promise 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({ patientId: Number(claimData.patientId), appointmentId: claimData.appointmentId ? Number(claimData.appointmentId) : null, @@ -75,9 +93,10 @@ router.post("/uniteddh-preauth", async (req: Request, res: Response): Promise { setForm((prev) => { const next = applyComboToForm( diff --git a/apps/Frontend/src/utils/procedureCombos.js b/apps/Frontend/src/utils/procedureCombos.js index 385e3f9d..6b9767ef 100644 --- a/apps/Frontend/src/utils/procedureCombos.js +++ b/apps/Frontend/src/utils/procedureCombos.js @@ -226,16 +226,31 @@ export const PROCEDURE_COMBOS = { label: "RCT Anterior", codes: ["D3310"], }, + rctAnteriorPostCrown: { + id: "rctAnteriorPostCrown", + label: "RCT Ant/Post/Crown", + codes: ["D3310", "D2954", "D2740"], + }, rctPremolar: { id: "rctPremolar", label: "RCT PreM", codes: ["D3320"], }, + rctPremolarPostCrown: { + id: "rctPremolarPostCrown", + label: "RCT PreM/Post/Crown", + codes: ["D3320", "D2954", "D2740"], + }, rctMolar: { id: "rctMolar", label: "RCT Molar", codes: ["D3330"], }, + rctMolarPostCrown: { + id: "rctMolarPostCrown", + label: "RCT Mol/Post/Crown", + codes: ["D3330", "D2954", "D2740"], + }, postCore: { id: "postCore", label: "Post/Core", @@ -338,7 +353,7 @@ export const COMBO_CATEGORIES = { "plResin", "plCast", ], - Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore", "coreBU"], + Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"], Prosthodontics: ["crown"], Periodontics: ["deepCleaning"], Extractions: [ diff --git a/apps/Frontend/src/utils/procedureCombos.ts b/apps/Frontend/src/utils/procedureCombos.ts index 4544cdfe..e9dbaabd 100755 --- a/apps/Frontend/src/utils/procedureCombos.ts +++ b/apps/Frontend/src/utils/procedureCombos.ts @@ -261,16 +261,31 @@ export const PROCEDURE_COMBOS: Record< label: "RCT Anterior", codes: ["D3310"], }, + rctAnteriorPostCrown: { + id: "rctAnteriorPostCrown", + label: "RCT Ant/Post/Crown", + codes: ["D3310", "D2954", "D2740"], + }, rctPremolar: { id: "rctPremolar", label: "RCT PreM", codes: ["D3320"], }, + rctPremolarPostCrown: { + id: "rctPremolarPostCrown", + label: "RCT PreM/Post/Crown", + codes: ["D3320", "D2954", "D2740"], + }, rctMolar: { id: "rctMolar", label: "RCT Molar", codes: ["D3330"], }, + rctMolarPostCrown: { + id: "rctMolarPostCrown", + label: "RCT Mol/Post/Crown", + codes: ["D3330", "D2954", "D2740"], + }, postCore: { id: "postCore", label: "Post/Core", @@ -380,7 +395,7 @@ export const COMBO_CATEGORIES: Record< "plCast", ], Implants: ["implantFull", "implantFixture", "implantAbutment", "implantCrown"], - Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore", "coreBU"], + Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"], Prosthodontics: ["crown"], Periodontics: ["deepCleaning"], Extractions: [ diff --git a/apps/SeleniumService/selenium_UnitedDH_claimSubmitWorker.py b/apps/SeleniumService/selenium_UnitedDH_claimSubmitWorker.py index fb9e2d2c..ef235412 100644 --- a/apps/SeleniumService/selenium_UnitedDH_claimSubmitWorker.py +++ b/apps/SeleniumService/selenium_UnitedDH_claimSubmitWorker.py @@ -610,22 +610,34 @@ class AutomationUnitedDHClaimSubmit: print("[UnitedDH Claim] step1: Selecting Billing Entity...") billing_selected = False try: - billing_ng = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, - "//label[@for='paymentGroupId']/following-sibling::ng-select | " - "//label[@for='paymentGroupId']/..//ng-select" - )) - ) - self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng) - arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper") - arrow.click() - first_option = WebDriverWait(self.driver, 5).until( - EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option")) - ) - option_text = first_option.text.strip() - first_option.click() - print(f"[UnitedDH Claim] step1: Selected Billing Entity: {option_text}") - billing_selected = True + taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId") + if taxonomy_input.is_displayed(): + self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input) + taxonomy_input.click() + print("[UnitedDH Claim] step1: Clicked Billing Entity dropdown") + time.sleep(1) + try: + summit_option = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable((By.XPATH, + "//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]" + )) + ) + summit_option.click() + print("[UnitedDH Claim] step1: Selected Billing Entity: Summit Dental Care") + 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: print(f"[UnitedDH Claim] step1: Billing Entity selection failed: {e}") diff --git a/apps/SeleniumService/selenium_UnitedDH_preAuthWorker.py b/apps/SeleniumService/selenium_UnitedDH_preAuthWorker.py index a5207eb4..bf973cfa 100644 --- a/apps/SeleniumService/selenium_UnitedDH_preAuthWorker.py +++ b/apps/SeleniumService/selenium_UnitedDH_preAuthWorker.py @@ -566,22 +566,34 @@ class AutomationUnitedDHPreAuth: print("[UnitedDH PreAuth] step1: Selecting Billing Entity...") billing_selected = False try: - billing_ng = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, - "//label[@for='paymentGroupId']/following-sibling::ng-select | " - "//label[@for='paymentGroupId']/..//ng-select" - )) - ) - self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng) - arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper") - arrow.click() - first_option = WebDriverWait(self.driver, 5).until( - EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option")) - ) - option_text = first_option.text.strip() - first_option.click() - print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {option_text}") - billing_selected = True + taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId") + if taxonomy_input.is_displayed(): + self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input) + taxonomy_input.click() + print("[UnitedDH PreAuth] step1: Clicked Billing Entity dropdown") + time.sleep(1) + try: + summit_option = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable((By.XPATH, + "//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]" + )) + ) + summit_option.click() + print("[UnitedDH PreAuth] step1: Selected Billing Entity: Summit Dental Care") + 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: 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") 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") time.sleep(5) @@ -648,7 +661,7 @@ class AutomationUnitedDHPreAuth: for by, selector in preauth_selectors: try: - btn = WebDriverWait(self.driver, 5).until( + btn = WebDriverWait(self.driver, 15).until( EC.element_to_be_clickable((by, selector)) ) self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn) @@ -954,23 +967,6 @@ class AutomationUnitedDHPreAuth: except Exception as 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") return "OK" @@ -1018,17 +1014,10 @@ class AutomationUnitedDHPreAuth: print(f"[UnitedDH PreAuth] step7: Attaching: {abs_path}") 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( 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) time.sleep(1.5) print(f"[UnitedDH PreAuth] step7: Attached: {os.path.basename(abs_path)}") @@ -1052,18 +1041,16 @@ class AutomationUnitedDHPreAuth: submit_btn = WebDriverWait(self.driver, 15).until( 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 Claim')] | " - "//button[contains(@class,'btn-primary') and (" - "contains(normalize-space(text()),'Submit Pre') or " - "contains(normalize-space(text()),'Submit Claim'))]" + "contains(normalize-space(.),'Submit Claim')]" )) ) self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn) 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") time.sleep(3) @@ -1109,33 +1096,68 @@ class AutomationUnitedDHPreAuth: time.sleep(4) preauth_number = None - try: - first_ref = WebDriverWait(self.driver, 20).until( - EC.presence_of_element_located((By.XPATH, - "(//table//tr[not(th)]/td[2] | " - "//table//tr[td]/td[contains(normalize-space(.),'2026') or " - " contains(normalize-space(.),'2025')])[1]" - )) - ) - ref_text = first_ref.text.strip() - match = re.search(r'\b(\d{14})\b', ref_text) - if match: - preauth_number = match.group(1) - else: - match = re.search(r'\b(\d{10,})\b', ref_text) - if match: - preauth_number = match.group(1) - print(f"[UnitedDH PreAuth] step9: Pre-auth number: {preauth_number!r} (cell: {ref_text!r})") - except Exception as e: - print(f"[UnitedDH PreAuth] step9: Could not read first-row reference number: {e}") + + # 1. Try URL — pre-auth ID often appears in the URL path + current_url = self.driver.current_url + url_match = re.search(r'/(\d{6,})', current_url) + if url_match: + preauth_number = url_match.group(1) + print(f"[UnitedDH PreAuth] step9: Pre-auth number from URL: {preauth_number!r}") + + # 2. Scan all table cells and log them for debugging + if not preauth_number: + try: + all_cells = self.driver.find_elements(By.XPATH, "//table//tr[td]/td") + cell_texts = [c.text.strip() for c in all_cells if c.text.strip()] + print(f"[UnitedDH PreAuth] step9: Table cells: {cell_texts[:20]}") + + # Try each cell — alphanumeric first (e.g. A0260616190876), then pure digits + for cell_text in cell_texts: + # Alphanumeric like A0260616190876 or PA12345678 + 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: body_text = self.driver.find_element(By.TAG_NAME, "body").text - match = re.search(r'\b(\d{14})\b', body_text) - if match: - preauth_number = match.group(1) - print(f"[UnitedDH PreAuth] step9: Pre-auth number (body scan): {preauth_number}") - except Exception: - pass + print(f"[UnitedDH PreAuth] step9: Body text (first 500 chars): {body_text[:500]!r}") + m = re.search(r'\b([A-Z]{1,4}\d{6,})\b', body_text) + if m: + preauth_number = m.group(1) + else: + 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") os.makedirs(shared_downloads, exist_ok=True) diff --git a/apps/SeleniumService/selenium_UnitedSCO_eligibilityCheckWorker.py b/apps/SeleniumService/selenium_UnitedSCO_eligibilityCheckWorker.py index c6a789ef..5f2eb6db 100644 --- a/apps/SeleniumService/selenium_UnitedSCO_eligibilityCheckWorker.py +++ b/apps/SeleniumService/selenium_UnitedSCO_eligibilityCheckWorker.py @@ -670,23 +670,34 @@ class AutomationUnitedSCOEligibilityCheck: print("[UnitedSCO step1] Selecting Billing Entity...") billing_selected = False try: - billing_ng = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, - "//label[@for='paymentGroupId']/following-sibling::ng-select | " - "//label[@for='paymentGroupId']/..//ng-select" - )) - ) - # Center in viewport so panel opens downward instead of upward - self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng) - arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper") - arrow.click() - first_option = WebDriverWait(self.driver, 5).until( - EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option")) - ) - option_text = first_option.text.strip() - first_option.click() - print(f"[UnitedSCO step1] Selected Billing Entity: {option_text}") - billing_selected = True + taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId") + if taxonomy_input.is_displayed(): + self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input) + taxonomy_input.click() + print("[UnitedSCO step1] Clicked Billing Entity dropdown") + time.sleep(1) + try: + summit_option = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable((By.XPATH, + "//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'Summit Dental Care')]" + )) + ) + summit_option.click() + print("[UnitedSCO step1] Selected Billing Entity: Summit Dental Care") + 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"[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: print(f"[UnitedSCO step1] Billing Entity selection failed: {e}")