diff --git a/apps/SeleniumService/selenium_DeltaIns_claimSubmitWorker.py b/apps/SeleniumService/selenium_DeltaIns_claimSubmitWorker.py index 7a6f2d94..bac8fc1b 100644 --- a/apps/SeleniumService/selenium_DeltaIns_claimSubmitWorker.py +++ b/apps/SeleniumService/selenium_DeltaIns_claimSubmitWorker.py @@ -505,6 +505,83 @@ class AutomationDeltaInsClaimSubmit: print(f"[Deltains Claim step4] Warning: could not fill {label} (#{element_id}): {e}") return None + _SURFACE_OPTIONS_JS = """ + const box = document.querySelector('[role="listbox"]'); + if (!box) return null; + return Array.from(box.querySelectorAll('[role="option"], li')).map(o => { + const cb = o.querySelector('input[type="checkbox"]'); + return { + text: (o.textContent || '').trim().toUpperCase(), + selected: o.getAttribute('aria-selected') === 'true' || !!(cb && cb.checked), + }; + }); + """ + + _SURFACE_CLICK_JS = """ + const letter = arguments[0]; + const box = document.querySelector('[role="listbox"]'); + if (!box) return false; + const opt = Array.from(box.querySelectorAll('[role="option"], li')) + .find(o => (o.textContent || '').trim().toUpperCase() === letter); + if (!opt) return false; + opt.scrollIntoView({block: 'nearest'}); + opt.click(); + return true; + """ + + def _select_tooth_surfaces(self, surface_select, letters, idx): + """Open the toothSurfaces multi-select and check each letter, verifying + the option ends up selected (aria-selected / checkbox checked).""" + # Make sure the tooth-number listbox is gone so we don't read its options + self._close_mui_popover() + options = None + for _ in range(3): + surface_select.click() + try: + options = WebDriverWait(self.driver, 4).until( + lambda d: d.execute_script(self._SURFACE_OPTIONS_JS) or False) + break + except TimeoutException: + print(f"[Deltains Claim step4] surface[{idx}]: listbox did not open, retrying") + if not options: + print(f"[Deltains Claim step4] surface[{idx}]: could not open surface listbox") + return + print(f"[Deltains Claim step4] surface[{idx}]: options={[o['text'] for o in options]}") + + for letter in letters: + state = {o["text"]: o["selected"] for o in (self.driver.execute_script(self._SURFACE_OPTIONS_JS) or [])} + if letter not in state: + print(f"[Deltains Claim step4] surface[{idx}]: option '{letter}' not found") + continue + if state[letter]: + print(f"[Deltains Claim step4] surface[{idx}]: '{letter}' already checked") + continue + self.driver.execute_script(self._SURFACE_CLICK_JS, letter) + time.sleep(0.3) + state = {o["text"]: o["selected"] for o in (self.driver.execute_script(self._SURFACE_OPTIONS_JS) or [])} + print(f"[Deltains Claim step4] surface[{idx}]: '{letter}' checked={state.get(letter)}") + + shown = (surface_select.text or "").strip() + print(f"[Deltains Claim step4] surface[{idx}]: field now shows {shown!r}") + + def _close_mui_popover(self): + """Close any open MUI Select popover so its backdrop stops covering + the rest of the form.""" + for attempt in range(3): + if not self.driver.find_elements(By.CSS_SELECTOR, '[role="listbox"]'): + return + try: + if attempt == 0: + self.driver.switch_to.active_element.send_keys(Keys.ESCAPE) + else: + self.driver.execute_script(""" + const bd = document.querySelector('.MuiPopover-root .MuiBackdrop-root, .MuiMenu-root .MuiBackdrop-root'); + if (bd) bd.click(); + """) + except Exception as e: + print(f"[Deltains Claim step4] close popover attempt {attempt} failed: {e}") + time.sleep(0.4) + def step4_fill_claim_form(self): """ Fill each service line: date of service, procedure code, and fee @@ -582,34 +659,26 @@ class AutomationDeltaInsClaimSubmit: except Exception as e: print(f"[Deltains Claim step4] Could not fill tooth number: {e}") - # Tooth surfaces — MUI multi-select; each letter (B, D, L, M, O...) - # is a checkbox option that stays open until clicked elsewhere. + # Tooth surfaces — MUI multi-select; each letter (B, D, F, I, L, + # M, O) is a checkbox option in a popover listbox that stays + # open until Escape / a backdrop click. if surface: try: surface_select = WebDriverWait(self.driver, 8).until( EC.element_to_be_clickable((By.ID, f"serviceLines.{idx}.toothSurfaces")) ) self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", surface_select) - surface_select.click() - time.sleep(0.5) - surface_letters = [c for c in surface if c.isalpha()] - for letter in surface_letters: - try: - opt = WebDriverWait(self.driver, 5).until( - EC.element_to_be_clickable((By.XPATH, - f"//li[@role='option'][normalize-space(.)='{letter}']")) - ) - opt.click() - time.sleep(0.2) - print(f"[Deltains Claim step4] surface[{idx}]: checked '{letter}'") - except TimeoutException: - print(f"[Deltains Claim step4] surface[{idx}]: option '{letter}' not found") - self.driver.find_element(By.TAG_NAME, "body").send_keys(Keys.ESCAPE) - time.sleep(0.3) + surface_letters = [] + for c in surface: + if c.isalpha() and c not in surface_letters: + surface_letters.append(c) + self._select_tooth_surfaces(surface_select, surface_letters, idx) except TimeoutException: print(f"[Deltains Claim step4] surface[{idx}]: select not present for this procedure code") except Exception as e: print(f"[Deltains Claim step4] Could not fill tooth surfaces: {e}") + finally: + self._close_mui_popover() if billed: self._fill_by_id(f"serviceLines.{idx}.fee", billed, f"fee[{idx}]")