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>
344 lines
12 KiB
Python
344 lines
12 KiB
Python
r"""
|
|
conditions/medicare_billing_screening.py — document #14: on a Medicare claim
|
|
the depression screening codes G8510/G8431 are not billed (unchecked, Billed
|
|
Fee zeroed), and the depression-pointer process (#6) does not run. Only the
|
|
screening codes are touched; every other CPT line is left as it is.
|
|
|
|
python tests\test_conditions_medicare_billing_screening.py
|
|
|
|
Uses hcfa_claim.html: 99213 (row 0), J7298 (row 1), and G8510 (row 2,
|
|
unchecked at 65.00 in the fixture — checked here where a case needs it).
|
|
"""
|
|
|
|
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" / "hcfa_claim.html"
|
|
|
|
from ecw_bot.conditions import medicare_billing_screening as mbs # noqa: E402
|
|
from ecw_bot.conditions import depression_dx_pointer # noqa: E402
|
|
from ecw_bot.conditions.base import run_all # noqa: E402
|
|
from ecw_bot.pages import claim_form # noqa: E402
|
|
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
|
|
|
CASES = []
|
|
|
|
MEDICARE = [{"Name": "HCFA Medicare"}]
|
|
MEDICARE_ADVANTAGE = [{"Name": "AARP Medicare Advantage"}]
|
|
MEDICAID = [{"Name": "APM2 Medicaid"}]
|
|
|
|
|
|
def case(fn):
|
|
CASES.append(fn)
|
|
return fn
|
|
|
|
|
|
def _check_row(page, index):
|
|
page.evaluate(
|
|
"""(i) => {
|
|
const rows = document.querySelectorAll('tr[id^="CPTROW_"]');
|
|
const box = rows[i].querySelector('[ng-click*="updateBillToIns("]');
|
|
box.checked = true;
|
|
box.setAttribute('checked', '');
|
|
}""", index)
|
|
|
|
|
|
def _row_state(page, index):
|
|
row = page.locator(sel.SEL_CPT_ROWS).nth(index)
|
|
return (row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked(),
|
|
row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value())
|
|
|
|
|
|
def _remove_rows_except(page, keep_codes):
|
|
page.evaluate(
|
|
"""(keep) => {
|
|
for (const tr of document.querySelectorAll('tr[id^="CPTROW_"]')) {
|
|
const c = tr.querySelector('[data-fieldname="claimCPTCode"]');
|
|
if (c && !keep.includes(c.value)) tr.remove();
|
|
}
|
|
}""", keep_codes)
|
|
|
|
|
|
# ── detect ───────────────────────────────────────────────────────────────────
|
|
|
|
@case
|
|
def detect_true_for_medicare_with_g8510():
|
|
data = {"insurance": MEDICARE, "cpt": [{"code": "99213"}, {"code": "G8510"}]}
|
|
assert mbs.detect("C1", data) is True
|
|
|
|
|
|
@case
|
|
def detect_true_for_medicare_with_g8431():
|
|
data = {"insurance": MEDICARE, "cpt": [{"code": "G8431"}]}
|
|
assert mbs.detect("C1", data) is True
|
|
|
|
|
|
@case
|
|
def detect_false_for_a_medicare_advantage_plan():
|
|
# Ruling 2026-10-21: the dashboard's Commercial Ins, not Medicare.
|
|
data = {"insurance": MEDICARE_ADVANTAGE, "cpt": [{"code": "G8510"}]}
|
|
assert mbs.detect("C1", data) is False
|
|
|
|
|
|
@case
|
|
def detect_false_for_a_non_medicare_payer():
|
|
data = {"insurance": MEDICAID, "cpt": [{"code": "G8510"}]}
|
|
assert mbs.detect("C1", data) is False
|
|
|
|
|
|
@case
|
|
def detect_false_for_an_unmapped_or_missing_payer():
|
|
assert mbs.detect("C1", {"insurance": [{"Name": "Nobody Knows"}],
|
|
"cpt": [{"code": "G8510"}]}) is False
|
|
assert mbs.detect("C1", {"cpt": [{"code": "G8510"}]}) is False
|
|
|
|
|
|
@case
|
|
def detect_false_for_medicare_without_screening_codes():
|
|
data = {"insurance": MEDICARE, "cpt": [{"code": "99213"}]}
|
|
assert mbs.detect("C1", data) is False
|
|
|
|
|
|
# ── the depression process (#6) stays off for Medicare ──────────────────────
|
|
|
|
_DEPRESSION_ICD = [{"#": "2", "Code": "Z13.31"}]
|
|
|
|
|
|
@case
|
|
def depression_process_does_not_run_for_medicare():
|
|
data = {"insurance": MEDICARE, "icd": _DEPRESSION_ICD,
|
|
"cpt": [{"code": "99213"}, {"code": "G8510"}]}
|
|
assert depression_dx_pointer.detect("C1", data) is False
|
|
|
|
|
|
@case
|
|
def depression_process_still_runs_for_other_payers():
|
|
for ins in (MEDICAID, [{"Name": "Nobody Knows"}], []):
|
|
data = {"insurance": ins, "icd": _DEPRESSION_ICD,
|
|
"cpt": [{"code": "99213"}, {"code": "G8510"}]}
|
|
assert depression_dx_pointer.detect("C1", data) is True, ins
|
|
|
|
|
|
# ── apply ────────────────────────────────────────────────────────────────────
|
|
|
|
@case
|
|
def apply_unchecks_and_zeroes_g8510_and_leaves_other_codes(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_check_row(page, 2)
|
|
before = [_row_state(page, i) for i in (0, 1)]
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is True
|
|
assert result.review == "", result.review
|
|
assert result.changes == [{"row": 2, "code": "G8510", "unchecked": True,
|
|
"fee": {"before": "65.00", "after": "0.00"}}], \
|
|
result.changes
|
|
assert _row_state(page, 2) == (False, "0.00")
|
|
assert [_row_state(page, i) for i in (0, 1)] == before, \
|
|
"99213 and J7298 must be left exactly as they were"
|
|
|
|
|
|
@case
|
|
def apply_zeroes_an_already_unchecked_priced_g8510(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is True
|
|
assert result.changes == [{"row": 2, "code": "G8510",
|
|
"fee": {"before": "65.00", "after": "0.00"}}], \
|
|
result.changes
|
|
|
|
|
|
@case
|
|
def apply_screening_only_zeroes_it_and_asks_for_provider_clarification(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_remove_rows_except(page, ["G8510"])
|
|
_check_row(page, 0)
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is True
|
|
assert _row_state(page, 0) == (False, "0.00")
|
|
assert result.review == mbs.SCREENING_ONLY, result.review
|
|
|
|
|
|
@case
|
|
def apply_is_a_no_op_once_screening_is_already_excluded(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
page.evaluate("""() => {
|
|
const rows = document.querySelectorAll('tr[id^="CPTROW_"]');
|
|
rows[2].querySelector('[data-fieldname="ClaimCPTBilledFee"]').value = '0.00';
|
|
}""")
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is False
|
|
assert result.changes == []
|
|
|
|
|
|
def _set_row(page, index, *, checked=None, fee=None, drop_checkbox=False):
|
|
page.evaluate(
|
|
"""(a) => {
|
|
const tr = document.querySelectorAll('tr[id^="CPTROW_"]')[a.i];
|
|
const box = tr.querySelector('[ng-click*="updateBillToIns("]');
|
|
if (a.drop) box.remove();
|
|
else if (a.checked !== null) {
|
|
box.checked = a.checked;
|
|
if (a.checked) box.setAttribute('checked', '');
|
|
else box.removeAttribute('checked');
|
|
}
|
|
if (a.fee !== null)
|
|
tr.querySelector('[data-fieldname="ClaimCPTBilledFee"]').value = a.fee;
|
|
}""", {"i": index, "checked": checked, "fee": fee,
|
|
"drop": drop_checkbox})
|
|
|
|
|
|
@case
|
|
def screening_only_is_judged_on_the_grid_after_the_other_conditions(page):
|
|
"""
|
|
Review finding 1 (2026-10-12): the read said 99213 was billed, but an
|
|
earlier condition (zero_fee_uncheck, a dummy/J-code exclusion) has
|
|
unchecked it since — once G8510 is gone too, nothing is billed.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_remove_rows_except(page, ["99213", "G8510"])
|
|
_check_row(page, 1)
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
assert data["cpt"][0]["billed"] is True
|
|
_set_row(page, 0, checked=False, fee="0.00")
|
|
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is True
|
|
assert result.review == mbs.SCREENING_ONLY, result.review
|
|
|
|
|
|
@case
|
|
def a_ub_line_with_no_checkbox_at_zero_is_not_billed(page):
|
|
"""UB: no Assign-To-Patient checkbox (billed None) at 0.00."""
|
|
page.goto(FIXTURE.as_uri())
|
|
_remove_rows_except(page, ["99213", "G8510"])
|
|
_check_row(page, 1)
|
|
_set_row(page, 0, drop_checkbox=True, fee="0.00")
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.review == mbs.SCREENING_ONLY, result.review
|
|
|
|
|
|
@case
|
|
def a_billed_line_left_on_the_grid_is_not_screening_only(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_remove_rows_except(page, ["99213", "G8510"])
|
|
_check_row(page, 1)
|
|
_set_row(page, 0, drop_checkbox=True, fee="210.00")
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is True and result.review == "", result.review
|
|
|
|
|
|
@case
|
|
def an_empty_live_grid_declines_instead_of_passing(page):
|
|
"""Review finding 2: the grid did not re-render after the tab click."""
|
|
page.goto(FIXTURE.as_uri())
|
|
_check_row(page, 2)
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
_remove_rows_except(page, [])
|
|
result = mbs.apply(page, "C1", data)
|
|
assert result.changed is False
|
|
assert result.review == mbs.CPT_GRID_NOT_READ, result.review
|
|
|
|
|
|
@case
|
|
def summary_comment_says_fee_zeroed_when_no_checkbox_was_unchecked():
|
|
"""
|
|
Review finding 3, operator 2026-10-12 (option A): on a UB form with no
|
|
Assign-To-Patient checkbox, zeroing the fee is the fix — the comment
|
|
must not claim "unchecked".
|
|
"""
|
|
fee_only = mbs.ConditionResult(
|
|
condition_name=mbs.NAME, changed=True,
|
|
changes=[{"row": 0, "code": "G8510",
|
|
"fee": {"before": "65.00", "after": "0.00"}}])
|
|
text = mbs.summary_comment(fee_only)
|
|
assert "fee zeroed" in text and "unchecked" not in text, text
|
|
|
|
both = mbs.ConditionResult(
|
|
condition_name=mbs.NAME, changed=True,
|
|
changes=[{"row": 0, "code": "G8510", "unchecked": True,
|
|
"fee": {"before": "65.00", "after": "0.00"}}])
|
|
text = mbs.summary_comment(both)
|
|
assert "unchecked" in text and "fee zeroed" in text, text
|
|
|
|
|
|
@case
|
|
def describe_names_the_code_and_the_fee(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_check_row(page, 2)
|
|
data = {"insurance": MEDICARE, "cpt": claim_form.extract(page)["cpt"]}
|
|
text = mbs.describe(mbs.apply(page, "C1", data))
|
|
assert "G8510" in text and "Medicare" in text, text
|
|
assert "65.00" in text and "0.00" in text, text
|
|
|
|
|
|
@case
|
|
def run_all_on_medicare_runs_this_and_not_the_depression_process(page):
|
|
"""
|
|
Against the REAL registry: on a Medicare claim with Z13.31, only this
|
|
condition fires — depression_dx_pointer would otherwise re-check the
|
|
G8510 this one just excluded.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_check_row(page, 2)
|
|
data = claim_form.extract(page)
|
|
data["insurance"] = MEDICARE
|
|
data["icd"] = _DEPRESSION_ICD
|
|
|
|
report = {}
|
|
results = run_all(page, "C1", data, report=report)
|
|
assert "depression_dx_pointer" not in report["fired"], report
|
|
assert [r.condition_name for r in results] == ["medicare_billing_screening"], \
|
|
results
|
|
assert _row_state(page, 2) == (False, "0.00")
|
|
|
|
|
|
def main() -> int:
|
|
if not FIXTURE.exists():
|
|
print(f" FAIL fixture missing: {FIXTURE}")
|
|
return 1
|
|
try:
|
|
from playwright.sync_api import sync_playwright
|
|
except ImportError:
|
|
print(" SKIP playwright is not installed")
|
|
return 0
|
|
|
|
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())
|