fix: Deltains claim tooth surface multi-select
Open the toothSurfaces MUI listbox (retrying if it doesn't appear), check each surface letter by option text via JS and verify it ends up selected, then close the popover with Escape or a backdrop click so its overlay no longer blocks the fee field. Waits for the tooth-number listbox to close first so its options aren't misread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -505,6 +505,83 @@ class AutomationDeltaInsClaimSubmit:
|
|||||||
print(f"[Deltains Claim step4] Warning: could not fill {label} (#{element_id}): {e}")
|
print(f"[Deltains Claim step4] Warning: could not fill {label} (#{element_id}): {e}")
|
||||||
return None
|
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):
|
def step4_fill_claim_form(self):
|
||||||
"""
|
"""
|
||||||
Fill each service line: date of service, procedure code, and fee
|
Fill each service line: date of service, procedure code, and fee
|
||||||
@@ -582,34 +659,26 @@ class AutomationDeltaInsClaimSubmit:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Deltains Claim step4] Could not fill tooth number: {e}")
|
print(f"[Deltains Claim step4] Could not fill tooth number: {e}")
|
||||||
|
|
||||||
# Tooth surfaces — MUI multi-select; each letter (B, D, L, M, O...)
|
# Tooth surfaces — MUI multi-select; each letter (B, D, F, I, L,
|
||||||
# is a checkbox option that stays open until clicked elsewhere.
|
# M, O) is a checkbox option in a popover listbox that stays
|
||||||
|
# open until Escape / a backdrop click.
|
||||||
if surface:
|
if surface:
|
||||||
try:
|
try:
|
||||||
surface_select = WebDriverWait(self.driver, 8).until(
|
surface_select = WebDriverWait(self.driver, 8).until(
|
||||||
EC.element_to_be_clickable((By.ID, f"serviceLines.{idx}.toothSurfaces"))
|
EC.element_to_be_clickable((By.ID, f"serviceLines.{idx}.toothSurfaces"))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", surface_select)
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", surface_select)
|
||||||
surface_select.click()
|
surface_letters = []
|
||||||
time.sleep(0.5)
|
for c in surface:
|
||||||
surface_letters = [c for c in surface if c.isalpha()]
|
if c.isalpha() and c not in surface_letters:
|
||||||
for letter in surface_letters:
|
surface_letters.append(c)
|
||||||
try:
|
self._select_tooth_surfaces(surface_select, surface_letters, idx)
|
||||||
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)
|
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
print(f"[Deltains Claim step4] surface[{idx}]: select not present for this procedure code")
|
print(f"[Deltains Claim step4] surface[{idx}]: select not present for this procedure code")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Deltains Claim step4] Could not fill tooth surfaces: {e}")
|
print(f"[Deltains Claim step4] Could not fill tooth surfaces: {e}")
|
||||||
|
finally:
|
||||||
|
self._close_mui_popover()
|
||||||
|
|
||||||
if billed:
|
if billed:
|
||||||
self._fill_by_id(f"serviceLines.{idx}.fee", billed, f"fee[{idx}]")
|
self._fill_by_id(f"serviceLines.{idx}.fee", billed, f"fee[{idx}]")
|
||||||
|
|||||||
Reference in New Issue
Block a user