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:
2026-10-16 23:13:36 -04:00
co-authored by Claude Opus 5.5
parent 0082b7bd53
commit aa5d3bf2d5
@@ -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}]")