Initial import: eCW Charge Validation bot and results Portal
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>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,269 @@
|
||||
r"""
|
||||
conditions/dental_payer_check.py — document #13: Dental codes are billed
|
||||
only to DentaQuest, Delta Dental or CHP+ Dental — on any insurance row,
|
||||
secondary included. None of them on a claim with a billed Dental code: the
|
||||
Claim Status is set to Patient (PAT) where the run may write, else Review
|
||||
"set it by hand"; no payer read: Review (operator, 2026-10-16 / 2026-10-21).
|
||||
|
||||
python tests\test_conditions_dental_payer_check.py
|
||||
|
||||
Uses tests/fixtures/dental_claim.html (D0120 row 0, DCONS row 1) for the
|
||||
run_all case.
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
import pathlib
|
||||
import inspect
|
||||
import contextlib
|
||||
|
||||
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.conditions import dental_payer_check as cond # noqa: E402
|
||||
from ecw_bot.conditions import CONDITIONS # noqa: E402
|
||||
from ecw_bot.conditions.base import run_all # noqa: E402
|
||||
from ecw_bot.core import charge_summary as cs # noqa: E402
|
||||
from ecw_bot.pages import claim_form # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def _row(code, billed=True, fee="85.00"):
|
||||
return {"code": code, "billed": billed, "billed_fee": fee, "units": "1"}
|
||||
|
||||
|
||||
def _claim(payer, *rows):
|
||||
return {"insurance": [{"Name": payer}] if payer is not None else [],
|
||||
"cpt": list(rows)}
|
||||
|
||||
|
||||
# ── recognition ──────────────────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def a_dental_code_is_d_and_four_digits():
|
||||
for code in ("D0120", "D1110", " d0274 ", "D9999"):
|
||||
assert cond.is_dental_code(code), code
|
||||
for code in ("DCONS", "Dscprit", "DPIMP", "D012", "D01200", "99213",
|
||||
"G8510", "", None):
|
||||
assert not cond.is_dental_code(code), code
|
||||
|
||||
|
||||
@case
|
||||
def the_three_payers_are_allowed_and_no_other():
|
||||
for payer in ("DentaQuest", "CHP+ DENTAQUEST", "Delta Dental",
|
||||
"Delta Dental of Colorado", "DELTA DENTAL PPO",
|
||||
" delta dental of oregon "):
|
||||
assert cond.is_allowed_payer(payer), payer
|
||||
for payer in ("Aetna Dental PPO", "Dental Cigna", "MetLife Dental Claims",
|
||||
"Medicaid", "Anthem Blue Cross Blue Shield",
|
||||
"Dental Slide D", "", None):
|
||||
assert not cond.is_allowed_payer(payer), payer
|
||||
|
||||
|
||||
# ── needs_review (no browser) ────────────────────────────────────────────────
|
||||
|
||||
@case
|
||||
def allowed_payers_need_nothing():
|
||||
for payer in ("DentaQuest", "CHP+ DENTAQUEST", "Delta Dental of Colorado"):
|
||||
data = _claim(payer, _row("D0120"), _row("D1110"))
|
||||
assert cond.detect("C1", data) is False
|
||||
assert cond.needs_review("C1", data) is None, payer
|
||||
|
||||
|
||||
@case
|
||||
def an_allowed_secondary_payer_is_enough():
|
||||
"""Operator 2026-10-21: secondary insurance counts."""
|
||||
data = _claim("Medicaid", _row("D0120"))
|
||||
data["insurance"].append({"Name": "Delta Dental of Colorado"})
|
||||
assert cond.detect("C1", data) is False
|
||||
assert cond.needs_review("C1", data) is None
|
||||
|
||||
|
||||
@case
|
||||
def another_payer_is_detected():
|
||||
for payer in ("Aetna Dental PPO", "Medicaid", "Cigna HealthCare Of Colorado"):
|
||||
data = _claim(payer, _row("D0120"), _row("DCONS"), _row("D1110"))
|
||||
assert cond.detect("C1", data) is True, payer
|
||||
assert cond.needs_review("C1", data) is None, payer
|
||||
|
||||
|
||||
@case
|
||||
def no_payer_read_is_review_not_a_status_change():
|
||||
for data in (_claim("", _row("D0120")), _claim(None, _row("D0120"))):
|
||||
assert cond.detect("C1", data) is False
|
||||
why = cond.needs_review("C1", data)
|
||||
assert why and "no payer read" in why and "D0120" in why, why
|
||||
|
||||
|
||||
@case
|
||||
def only_billed_dental_lines_count():
|
||||
# Unchecked at 0.00, or checked at 0.00 (zero_fee_uncheck unchecks it):
|
||||
# nothing billed, nothing to do.
|
||||
for row in (_row("D0120", billed=False, fee="0.00"),
|
||||
_row("D0120", billed=True, fee="0.00")):
|
||||
assert cond.detect("C1", _claim("Medicaid", row)) is False, row
|
||||
# Dummy codes only: not dental codes.
|
||||
data = _claim("Medicaid", _row("DCONS"), _row("DPIMP"), _row("99213"))
|
||||
assert cond.detect("C1", data) is False
|
||||
|
||||
|
||||
@case
|
||||
def it_is_registered():
|
||||
assert "dental_payer_check" in [m.NAME for m in CONDITIONS]
|
||||
assert cs.COMMENTS.get("dental_payer_check")
|
||||
|
||||
|
||||
# ── run_all on the Dental screen (browser) ──────────────────────────────────
|
||||
|
||||
def _dental_read(page, payer):
|
||||
data = claim_form.extract(page)
|
||||
data["screen"] = "dental"
|
||||
data["insurance"] = [{"Name": payer}]
|
||||
data["progress_notes"] = {"opened": "tab", "text": "Routine dental visit."}
|
||||
return data
|
||||
|
||||
|
||||
def _outcome(page, payer):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
data = _dental_read(page, payer)
|
||||
report = {}
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
results = run_all(page, "C1", data, report=report)
|
||||
data["_conditions"] = {"fired": [r.condition_name for r in results],
|
||||
"saved": False, "failed": report["failed"],
|
||||
"review": report["review"],
|
||||
"comments": report["comments"]}
|
||||
return cs.claim_outcome(True, data), report
|
||||
|
||||
|
||||
@case
|
||||
def a_dental_claim_to_aetna_dental_is_review_set_by_hand(page):
|
||||
# The Dental screen is never written (claims not saved): Review.
|
||||
outcome, report = _outcome(page, "Aetna Dental PPO")
|
||||
notes = [r["reason"] for r in report["review"]
|
||||
if r["name"] == "dental_payer_check"]
|
||||
assert len(notes) == 1 and "D0120" in notes[0], report["review"]
|
||||
assert "set it to Patient by hand" in notes[0], notes
|
||||
assert outcome["Category"] == "Review", outcome
|
||||
assert "Patient" in outcome["Comments"], outcome
|
||||
|
||||
|
||||
# ── the Claim Status dropdown (browser) ─────────────────────────────────────
|
||||
|
||||
_STATUS = """
|
||||
<select id="status" ng-model="claimRP.claimRPStatus">
|
||||
<option value="A">Active</option><option value="PAT">Not this one</option>
|
||||
</select>
|
||||
<select id="claimStatusSel1" ng-model="ClaimData.ClaimStatus"
|
||||
ng-change="ClaimStautsChange('ChangeEvent')">
|
||||
<option value=""></option>
|
||||
<option value="string:CMC">Ready to Submit (Electronic)</option>
|
||||
<option value="string:PAT">Patient</option>
|
||||
<option value="string:108ME" selected>Medcor Ready</option>
|
||||
</select>
|
||||
"""
|
||||
|
||||
|
||||
def _write(page, html, data, save=True):
|
||||
from ecw_bot import settings as _cfg
|
||||
page.set_content(html)
|
||||
orig = _cfg.SAVE_CORRECTIONS
|
||||
_cfg.SAVE_CORRECTIONS = save
|
||||
try:
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
return cond.apply(page, "C1", data)
|
||||
finally:
|
||||
_cfg.SAVE_CORRECTIONS = orig
|
||||
|
||||
|
||||
def _value(page, selector):
|
||||
return page.locator(selector).input_value()
|
||||
|
||||
|
||||
@case
|
||||
def live_write_sets_the_claim_status_to_patient(page):
|
||||
data = _claim("Aetna Dental PPO", _row("D0120"))
|
||||
result = _write(page, _STATUS, data)
|
||||
assert result.changed is True and result.review == "", result
|
||||
assert _value(page, "#claimStatusSel1") == "string:PAT"
|
||||
assert _value(page, "#status") == "A", "the other status field untouched"
|
||||
c = result.changes[0]
|
||||
assert (c["before"], c["after"], c["label"]) == ("108ME", "PAT", "Patient")
|
||||
assert cond.summary_comment(result) == (
|
||||
"Dental code(s) D0120 billed to Aetna Dental PPO — not DentaQuest / "
|
||||
"Delta Dental / CHP+ Dental - Claim Status set to Patient")
|
||||
assert "108ME -> PAT (Patient)" in cond.describe(result)
|
||||
|
||||
|
||||
@case
|
||||
def the_ub_dropdown_with_plain_values_works_too(page):
|
||||
html = ('<select id="claimStatusSel2" ng-model="selectedClaimStatusCodes">'
|
||||
'<option value="108ME" selected>Medcor Ready</option>'
|
||||
'<option value="PAT">Patient</option></select>')
|
||||
result = _write(page, html, _claim("Medicaid", _row("D1110")))
|
||||
assert result.changed and _value(page, "#claimStatusSel2") == "PAT"
|
||||
|
||||
|
||||
@case
|
||||
def verify_changes_nothing_and_says_set_it_by_hand(page):
|
||||
result = _write(page, _STATUS, _claim("Medicaid", _row("D0120")),
|
||||
save=False)
|
||||
assert result.changed is False
|
||||
assert "set it to Patient by hand" in result.review, result.review
|
||||
assert _value(page, "#claimStatusSel1") == "string:108ME"
|
||||
|
||||
|
||||
@case
|
||||
def no_patient_option_or_no_dropdown_is_review(page):
|
||||
html = ('<select ng-model="ClaimData.ClaimStatus">'
|
||||
'<option value="108ME" selected>Medcor Ready</option></select>')
|
||||
result = _write(page, html, _claim("Medicaid", _row("D0120")))
|
||||
assert result.changed is False and "set it by hand" in result.review
|
||||
result = _write(page, "<p>no form</p>", _claim("Medicaid", _row("D0120")))
|
||||
assert result.changed is False and "no Claim Status box" in result.review
|
||||
|
||||
|
||||
@case
|
||||
def a_dental_claim_to_dentaquest_gets_no_payer_review(page):
|
||||
_, report = _outcome(page, "DentaQuest")
|
||||
assert not [r for r in report["review"]
|
||||
if r["name"] == "dental_payer_check"], report["review"]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
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())
|
||||
Reference in new issue
Block a user