diff --git a/apps/SeleniumService/selenium_CCA_preAuthWorker.py b/apps/SeleniumService/selenium_CCA_preAuthWorker.py index 00f7aebc..758a717e 100644 --- a/apps/SeleniumService/selenium_CCA_preAuthWorker.py +++ b/apps/SeleniumService/selenium_CCA_preAuthWorker.py @@ -363,6 +363,20 @@ class AutomationCCAPreAuth: if not verified: return "ERROR: 'Verify Eligibility' button not found" + # Multiple records popup: pick the first member (top Select) + try: + sel_btn = WebDriverWait(self.driver, 12).until( + EC.element_to_be_clickable((By.ID, "buttonSelectMember0")) + ) + print("[CCA PreAuth step2] Multiple records popup — clicking top Select") + try: + sel_btn.click() + except Exception: + self.driver.execute_script("arguments[0].click();", sel_btn) + time.sleep(2) + except TimeoutException: + print("[CCA PreAuth step2] No multiple-records popup") + print("[CCA PreAuth step2] Waiting for eligibility result...") try: WebDriverWait(self.driver, 25).until( @@ -681,108 +695,107 @@ class AutomationCCAPreAuth: # ── Code: click td[2] to activate → find ServicesCODE by ID globally ── code_filled = False - for xpath in [ - f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[2]", - f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[2]", - f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[2]", - ]: - try: - code_cell = self.driver.find_element(By.XPATH, xpath) - self.driver.execute_script( - "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", code_cell) - code_cell.click() - time.sleep(0.5) - code_input = WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.ID, "ServicesCODE"))) - code_input.send_keys(code) - time.sleep(1.5) - print(f"[CCA PreAuth step3] Row {idx+1}: code={code!r} entered") - code_filled = True + for _ca in range(1, 4): + for xpath in [ + f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[2]", + f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[2]", + f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[2]", + ]: + try: + code_cell = self.driver.find_element(By.XPATH, xpath) + self.driver.execute_script( + "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", code_cell) + code_cell.click() + time.sleep(0.5) + code_input = WebDriverWait(self.driver, 5).until( + EC.presence_of_element_located((By.ID, "ServicesCODE"))) + code_input.send_keys(code) + time.sleep(1.5) + print(f"[CCA PreAuth step3] Row {idx+1}: code={code!r} entered") + code_filled = True + break + except Exception as ce: + print(f"[CCA PreAuth step3] Row {idx+1}: code attempt {_ca}/3 {ce.__class__.__name__}") + continue + + if code_filled: break - except Exception: - continue + time.sleep(1.5) if not code_filled: - print(f"[CCA PreAuth step3] Row {idx+1}: Code fill failed — skipping row") - continue + print(f"[CCA PreAuth step3] Row {idx+1}: Code fill failed") + return (f"ERROR: failed to fill code for service line {idx+1} " + f"(code={code}) — pre-auth NOT submitted") - # ── Tooth: click td[4] in Services grid → wait for input → clear → type ── + # ── Tooth: click td[4] → wait for input → clear → type (retry on stale/not-ready) ── if tooth: - try: - from selenium.webdriver.common.action_chains import ActionChains - tooth_cell = None - tooth_cell_xpaths = [ - f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[4]", - f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[4]", - f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[4]", - ] - # Re-find cell fresh each attempt to avoid StaleElement - for xpath in tooth_cell_xpaths: - try: - tooth_cell = self.driver.find_element(By.XPATH, xpath) - break - except Exception: - continue - - if tooth_cell: + from selenium.webdriver.common.action_chains import ActionChains + tooth_done = False + time.sleep(0.8) # grid re-renders the row after the code is entered + for _ta in range(1, 5): + try: + tooth_cell = None + for xpath in [ + f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[4]", + f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[4]", + f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[4]", + ]: + try: + tooth_cell = self.driver.find_element(By.XPATH, xpath) + break + except Exception: + continue + if not tooth_cell: + raise Exception("tooth cell not found") self.driver.execute_script( "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", tooth_cell) - tooth_cell.click() - time.sleep(0.3) + if _ta >= 3: + ActionChains(self.driver).double_click(tooth_cell).perform() + else: + tooth_cell.click() + time.sleep(0.4) - # Wait until an input becomes active/focused inside the cell + # Fresh lookup every time: prefer the pre-rendered ServicesTOOTH input tooth_input = None - for _ in range(10): + for _ in range(8): + cands = [e for e in self.driver.find_elements(By.ID, "ServicesTOOTH") + if e.is_displayed()] + if cands: + tooth_input = cands[0] + break active = self.driver.switch_to.active_element - tag = active.tag_name.lower() if active else "" - if tag == "input": + if active.tag_name.lower() == "input": tooth_input = active break - # Try clicking again to trigger edit mode - try: - tooth_cell = self.driver.find_element(By.XPATH, tooth_cell_xpaths[0]) - except Exception: - pass - try: - tooth_cell.click() - except Exception: - pass - time.sleep(0.2) + time.sleep(0.25) + if not tooth_input: + raise Exception("tooth input not active") - if tooth_input: - # Clear → Type → Verify (with JS fallback) - tooth_input.click() + try: tooth_input.send_keys(Keys.CONTROL + "a") tooth_input.send_keys(Keys.DELETE) - time.sleep(0.1) for ch in str(tooth): tooth_input.send_keys(ch) time.sleep(0.05) - time.sleep(0.2) - # Verify value was entered; fall back to JS inject if empty - entered = tooth_input.get_attribute("value") or "" - if entered.strip() == "": - self.driver.execute_script( - "arguments[0].value = arguments[1];" - "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));" - "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));", - tooth_input, str(tooth)) - time.sleep(0.2) - print(f"[CCA PreAuth step3] Row {idx+1}: tooth={tooth!r} entered") - else: - # Fallback: JS inject directly into active element - active = self.driver.switch_to.active_element + except Exception: + pass + time.sleep(0.2) + if (tooth_input.get_attribute("value") or "").strip() != str(tooth): self.driver.execute_script( - "arguments[0].value = arguments[1];" + "arguments[0].focus();arguments[0].value = arguments[1];" "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));" "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));", - active, str(tooth)) + tooth_input, str(tooth)) time.sleep(0.2) - print(f"[CCA PreAuth step3] Row {idx+1}: tooth={tooth!r} entered via JS fallback") - else: - print(f"[CCA PreAuth step3] Row {idx+1}: Tooth cell not found — skipping") - except Exception as e: - print(f"[CCA PreAuth step3] Row {idx+1}: Tooth fill failed: {e} — skipping tooth") + print(f"[CCA PreAuth step3] Row {idx+1}: tooth={tooth!r} entered") + tooth_done = True + break + except Exception as te: + print(f"[CCA PreAuth step3] Row {idx+1}: tooth attempt {_ta}/4: {te.__class__.__name__}") + time.sleep(1.0) + if not tooth_done: + return (f"ERROR: failed to fill tooth for service line {idx+1} " + f"(code={code}) — pre-auth NOT submitted") # ── Billed Amount: click TD cell to activate edit mode (turn yellow), # wait for input, delete default 0.00, type amount ── @@ -807,38 +820,52 @@ class AutomationCCAPreAuth: billed_cell.click() time.sleep(0.3) - # Wait until the input activates (cell turns yellow = edit mode) + # Activate the editor and type. "element not interactable" happens when the + # grid hasn't finished the previous row, so re-click and retry. + from selenium.webdriver.common.action_chains import ActionChains + js_set = ( + "arguments[0].focus();arguments[0].value = arguments[1];" + "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));" + "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));") billed_input = None - for _ in range(10): + for attempt in range(1, 5): try: - billed_input = WebDriverWait(self.driver, 1).until( - EC.element_to_be_clickable((By.ID, "ServicesBILLED_AMOUNT"))) + cands = [e for e in self.driver.find_elements(By.ID, "ServicesBILLED_AMOUNT") + if e.is_displayed()] + if not cands: + raise Exception("no visible ServicesBILLED_AMOUNT input") + inp = cands[0] + try: + inp.send_keys(Keys.CONTROL + "a") + inp.send_keys(Keys.DELETE) + for ch in str(billed_str): + inp.send_keys(ch) + time.sleep(0.05) + except Exception as ke: + print(f"[CCA PreAuth step3] Row {idx+1}: typing failed ({ke.__class__.__name__}), using JS") + self.driver.execute_script(js_set, inp, str(billed_str)) + time.sleep(0.2) + if (inp.get_attribute("value") or "").strip() in ("", "0.00", "0"): + self.driver.execute_script(js_set, inp, str(billed_str)) + time.sleep(0.2) + billed_input = inp break - except Exception: - pass - try: - billed_cell = self.driver.find_element(By.XPATH, billed_cell_xpaths[0]) - billed_cell.click() - except Exception: - pass - time.sleep(0.2) + except Exception as ae: + print(f"[CCA PreAuth step3] Row {idx+1}: billed attempt {attempt}/4: {ae.__class__.__name__}") + time.sleep(0.8) + try: + billed_cell = self.driver.find_element(By.XPATH, billed_cell_xpaths[0]) + self.driver.execute_script( + "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", billed_cell) + if attempt >= 2: + ActionChains(self.driver).double_click(billed_cell).perform() + else: + billed_cell.click() + time.sleep(0.5) + except Exception: + pass if billed_input: - billed_input.send_keys(Keys.DELETE) - time.sleep(0.3) - for ch in str(billed_str): - billed_input.send_keys(ch) - time.sleep(0.05) - time.sleep(0.2) - # Verify; fallback to JS if empty - entered = billed_input.get_attribute("value") or "" - if entered.strip() == "": - self.driver.execute_script( - "arguments[0].value = arguments[1];" - "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));" - "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));", - billed_input, str(billed_str)) - time.sleep(0.2) print(f"[CCA PreAuth step3] Row {idx+1}: billed={billed_str!r} entered") else: print(f"[CCA PreAuth step3] Row {idx+1}: Billed Amount input not activated — skipping")