Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
254 lines
10 KiB
Python
254 lines
10 KiB
Python
r"""
|
|
Dental claims run every applicable condition (operator, 2026-10-07 and
|
|
2026-10-12): every "All payers" condition plus the claim's own payer's —
|
|
the same rule as UB/HCFA. Spec:
|
|
docs/superpowers/specs/2026-10-07-dental-claims-run-all-conditions-design.md
|
|
|
|
python tests\test_dental_conditions.py
|
|
|
|
Uses tests/fixtures/dental_claim.html: D0120 (row 0, checked, 45.00) and
|
|
DCONS (row 1, checked, 30.00), ICD 1 = Z01.20, payer DentaQuest.
|
|
"""
|
|
|
|
import sys
|
|
import pathlib
|
|
import inspect
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "dental_claim.html"
|
|
|
|
from ecw_bot.pages import claim_form # noqa: E402
|
|
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
|
from ecw_bot.conditions import (dummy_codes, telehealth, # noqa: E402
|
|
medicare_telehealth, vaccine_admin_codes,
|
|
bh_h_code_modifier, j2930_j2001_update)
|
|
from ecw_bot.conditions.base import run_all # noqa: E402
|
|
|
|
CASES = []
|
|
|
|
|
|
def case(fn):
|
|
CASES.append(fn)
|
|
return fn
|
|
|
|
|
|
def _add_row(page, code, fee="40.00", checked=True, icd1="1"):
|
|
"""One more real service line, cloned from row 0."""
|
|
page.evaluate(
|
|
"""(a) => {
|
|
const rows = document.querySelectorAll(
|
|
'tr[ng-repeat^="CPT in CPTCodes"]');
|
|
const tr = rows[0].cloneNode(true);
|
|
tr.querySelector('[data-fieldname="web_cptcode"]').value = a.code;
|
|
tr.querySelector('[data-fieldname="web_billedFee"]').value = a.fee;
|
|
tr.querySelector('[data-fieldname="web_icd1"]').value = a.icd1;
|
|
const box = tr.querySelector('[ng-click*="updateBillToIns("]');
|
|
box.checked = a.checked;
|
|
rows[rows.length - 1].after(tr);
|
|
}""", {"code": code, "fee": fee, "checked": checked, "icd1": icd1})
|
|
|
|
|
|
def _row(page, index):
|
|
tr = page.locator(sel.SEL_CPT_ROWS).nth(index)
|
|
return (tr.locator(sel.SEL_CPT_CODE).first.input_value(),
|
|
tr.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked(),
|
|
tr.locator(sel.SEL_CPT_BILLED_FEE).first.input_value())
|
|
|
|
|
|
# ── reading the Dental screen ────────────────────────────────────────────────
|
|
|
|
@case
|
|
def extract_reads_the_dental_grid_in_the_ub_row_shape(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
data = claim_form.extract(page)
|
|
assert data["screen"] == "dental", data.get("screen")
|
|
assert data["claim_no"] == "5000000", data["claim_no"]
|
|
cpt = data["cpt"]
|
|
assert [r["code"] for r in cpt] == ["D0120", "DCONS"], cpt
|
|
row = cpt[1]
|
|
assert row["billed"] is True and row["billed_fee"] == "30.00", row
|
|
assert row["icd1"] == "1" and row["units"] == "1", row
|
|
assert row["sdos"] == "09/01/2026", row
|
|
assert row["mod1"] == "" and row["pos"] == "", "no such columns"
|
|
assert data["insurance"][0]["Name"] == "DentaQuest", data["insurance"]
|
|
assert data["icd"] == [{"#": "1", "Code": "Z01.20",
|
|
"Name": "Encounter for dental exam"}], data["icd"]
|
|
assert data["notes"] == "Routine visit.", data["notes"]
|
|
|
|
|
|
@case
|
|
def extract_on_ub_and_hcfa_says_which_screen(page):
|
|
page.goto(FIXTURE.with_name("ub_claim.html").as_uri())
|
|
assert claim_form.extract(page)["screen"] == "ub"
|
|
page.goto(FIXTURE.with_name("hcfa_claim.html").as_uri())
|
|
assert claim_form.extract(page)["screen"] == "hcfa"
|
|
|
|
|
|
@case
|
|
def a_hidden_dental_form_is_never_read(page):
|
|
"""eCW leaves closed forms in the DOM; only the one on screen counts."""
|
|
page.goto(FIXTURE.as_uri())
|
|
page.evaluate("""() => document.getElementById('dentalClaim7777777777777')
|
|
.style.display = 'none'""")
|
|
data = claim_form.extract(page)
|
|
assert data["screen"] != "dental" and data["cpt"] == [], data
|
|
|
|
|
|
# ── the row helpers work on the Dental grid ──────────────────────────────────
|
|
|
|
@case
|
|
def dcons_is_unchecked_and_zeroed_on_the_dental_screen(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
data = claim_form.extract(page)
|
|
data["insurance"] = [{"Name": "DentaQuest"}]
|
|
assert dummy_codes.detect("C1", data) is True
|
|
result = dummy_codes.apply(page, "C1", data)
|
|
assert result.changed is True, result
|
|
assert result.changes == [{"row": 1, "code": "DCONS", "unchecked": True,
|
|
"fee": {"before": "30.00",
|
|
"after": "0.00"}}], result.changes
|
|
assert _row(page, 1) == ("DCONS", False, "0.00")
|
|
assert _row(page, 0) == ("D0120", True, "45.00"), "D0120 untouched"
|
|
|
|
|
|
@case
|
|
def j2001_is_unchecked_and_zeroed_on_the_dental_screen(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_add_row(page, "J2001", fee="12.00")
|
|
data = claim_form.extract(page)
|
|
result = j2930_j2001_update.apply(page, "C1", data)
|
|
assert result.changed is True, result
|
|
assert _row(page, 2) == ("J2001", False, "0.00")
|
|
|
|
|
|
# ── run_all on a Dental claim ────────────────────────────────────────────────
|
|
|
|
def _dental_data(page, payer="DentaQuest", note="Routine dental visit."):
|
|
data = claim_form.extract(page)
|
|
data["insurance"] = [{"Name": payer}]
|
|
data["progress_notes"] = {"opened": "tab", "text": note}
|
|
return data
|
|
|
|
|
|
@case
|
|
def run_all_on_a_dental_claim_runs_the_all_payer_conditions(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_add_row(page, "J2001", fee="12.00")
|
|
_add_row(page, "D1110", fee="0.00")
|
|
report = {}
|
|
results = run_all(page, "C1", _dental_data(page), report=report)
|
|
names = [r.condition_name for r in results]
|
|
for name in ("dummy_codes", "j2930_j2001_update", "zero_fee_uncheck"):
|
|
assert name in names, (name, names, report)
|
|
assert report["failed"] == [] and report["review"] == [], report
|
|
assert _row(page, 1)[1:] == (False, "0.00")
|
|
assert _row(page, 2)[1:] == (False, "0.00")
|
|
assert _row(page, 3)[1] is False, "zero-fee line unchecked"
|
|
assert _row(page, 0) == ("D0120", True, "45.00")
|
|
|
|
|
|
@case
|
|
def a_fix_the_dental_screen_cannot_make_goes_to_review(page):
|
|
"""
|
|
Operator, 2026-10-12: the Dental grid has no POS and no modifier
|
|
columns, so telehealth's POS 2 + modifier cannot be written — the
|
|
claim is Review with the reason, never a silent skip.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
data = _dental_data(page, note="Session Setting (PoS): TeleHealth. "
|
|
"Audio and video telehealth visit.")
|
|
report = {}
|
|
run_all(page, "C1", data, report=report)
|
|
reasons = {r["name"]: r["reason"] for r in report["review"]}
|
|
assert "telehealth" in reasons, report
|
|
assert "Dental" in reasons["telehealth"], reasons
|
|
assert "telehealth" not in report["changed"], report
|
|
|
|
|
|
@case
|
|
def depression_pointer_is_retagged_on_the_dental_icd_fields(page):
|
|
"""#6 on web_icd1-4: Z13.31 only on G8510, taken off D0120."""
|
|
page.goto(FIXTURE.as_uri())
|
|
page.evaluate("""() => {
|
|
const body = document.querySelector('#addTable tbody');
|
|
const tr = body.rows[0].cloneNode(true);
|
|
tr.cells[0].textContent = '2';
|
|
tr.cells[1].querySelector('input').value = 'Z13.31';
|
|
tr.cells[2].textContent = 'Encounter for screening for depression';
|
|
body.rows[0].after(tr);
|
|
document.querySelector('[data-fieldname="web_icd2"]').value = '2';
|
|
}""")
|
|
_add_row(page, "G8510", fee="0.00", icd1="1")
|
|
report = {}
|
|
run_all(page, "C1", _dental_data(page), report=report)
|
|
assert "depression_dx_pointer" in report["changed"], report
|
|
icd = lambda i, n: page.locator(sel.SEL_CPT_ROWS).nth(i).locator(
|
|
sel.SEL_CPT_ICD[n]).first.input_value()
|
|
assert [icd(0, n) for n in range(4)] == ["1", "", "", ""], \
|
|
"Z13.31 (pointer 2) taken off D0120"
|
|
assert icd(2, 0) == "2", "G8510 carries Z13.31"
|
|
|
|
|
|
@case
|
|
def every_condition_that_needs_pos_modifiers_or_new_lines_declares_it():
|
|
for module in (telehealth, medicare_telehealth, vaccine_admin_codes):
|
|
reason = getattr(module, "DENTAL_UNSUPPORTED", "")
|
|
assert reason and "Dental" in reason, module.NAME
|
|
|
|
|
|
@case
|
|
def bh_h_code_modifier_path_is_review_on_dental_but_non_bh_unchecks(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_add_row(page, "H0023", fee="50.00")
|
|
report = {}
|
|
run_all(page, "C1", _dental_data(page, payer="APM2 Medicaid"),
|
|
report=report)
|
|
assert "bh_h_code_modifier" in report["changed"], report
|
|
assert _row(page, 2)[1:] == (False, "0.00")
|
|
|
|
# A BH payer's H code with a modifier: the Dental grid has no modifier
|
|
# column, so there is nothing to read and nothing fires.
|
|
page.goto(FIXTURE.as_uri())
|
|
_add_row(page, "H0023", fee="50.00")
|
|
report = {}
|
|
run_all(page, "C1", _dental_data(page, payer="BH COLORADO ACCESS"),
|
|
report=report)
|
|
assert "bh_h_code_modifier" not in report["fired"], report
|
|
assert _row(page, 2)[1:] == (True, "50.00")
|
|
|
|
|
|
def main() -> int:
|
|
if not FIXTURE.exists():
|
|
print(f" FAIL fixture missing: {FIXTURE}")
|
|
return 1
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
failures = 0
|
|
with sync_playwright() as pw:
|
|
browser = pw.chromium.launch(headless=True)
|
|
page = browser.new_page()
|
|
for fn in CASES:
|
|
name = fn.__name__.replace("_", " ")
|
|
try:
|
|
if len(inspect.signature(fn).parameters) == 0:
|
|
fn()
|
|
else:
|
|
fn(page)
|
|
print(f" ok {name}")
|
|
except AssertionError as exc:
|
|
print(f" FAIL {name}: {exc}")
|
|
failures += 1
|
|
except Exception as exc:
|
|
print(f" ERROR {name}: {type(exc).__name__}: {exc}")
|
|
failures += 1
|
|
browser.close()
|
|
|
|
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
|
return 1 if failures else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|