229 lines
8.3 KiB
Python
229 lines
8.3 KiB
Python
r"""
|
|
conditions/vaccine_z23_pointer.py — document #45 (EEWA-39, operator
|
|
2026-10-28): "Make sure you are assigning the proper diagnosis code (Z23)
|
|
for ALL VACCINES".
|
|
|
|
Every line with one of the client's 19 vaccine codes (#18's list; not the
|
|
admin codes) carries Z23's ICD pointer — its number in the ICD list — in
|
|
ICD1-4. Missing → the first empty slot, the line's other pointers kept. No
|
|
Z23 on the claim, or no free slot → Review. Other lines are never touched.
|
|
All payers; not Dental.
|
|
|
|
python tests\test_conditions_vaccine_z23_pointer.py
|
|
|
|
Uses hcfa_claim.html; `_inject_vaccine_row()` adds the vaccine lines.
|
|
"""
|
|
|
|
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" / "hcfa_claim.html"
|
|
|
|
from ecw_bot.conditions import vaccine_z23_pointer as cond # noqa: E402
|
|
from ecw_bot.conditions import CONDITIONS # noqa: E402
|
|
from ecw_bot.conditions.base import ConditionResult, run_all # noqa: E402
|
|
from ecw_bot.core import charge_summary as cs # noqa: E402
|
|
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
|
|
|
CASES = []
|
|
|
|
|
|
def case(fn):
|
|
CASES.append(fn)
|
|
return fn
|
|
|
|
|
|
_ICD = [{"#": "1", "Code": "J06.9", "Name": "..."},
|
|
{"#": "2", "Code": "Z23", "Name": "Encounter for immunization"}]
|
|
|
|
|
|
def _claim(*codes, icd=_ICD, screen=None):
|
|
data = {"insurance": [{"Name": "Medicaid"}], "icd": list(icd),
|
|
"cpt": [{"code": c, "checked": True, "fee": "45.00"}
|
|
for c in codes]}
|
|
if screen:
|
|
data["screen"] = screen
|
|
return data
|
|
|
|
|
|
def _inject_vaccine_row(page, code, row_id, icd=("1", "", "", "")):
|
|
page.evaluate(
|
|
"""(args) => {
|
|
const tbody = document.querySelector(
|
|
'#claimCPTCodesTable8888888888888 tbody');
|
|
const addRowTr = tbody.querySelector('tr:last-child');
|
|
const tr = document.createElement('tr');
|
|
tr.id = args.rowId;
|
|
tr.innerHTML =
|
|
'<td><input data-fieldname="claimCPTCode" value="' + args.code + '" readonly></td>'
|
|
+ '<td><input type="checkbox" ng-click="updateBillToIns(3)" checked></td>'
|
|
+ args.icd.map((v, i) => '<td><input data-fieldname="ClaimCPTICD'
|
|
+ (i + 1) + '" value="' + v + '"></td>').join('')
|
|
+ '<td><input data-fieldname="ClaimCPTBilledFee" value="45.00"></td>';
|
|
tbody.insertBefore(tr, addRowTr);
|
|
}""", {"code": code, "rowId": row_id, "icd": list(icd)})
|
|
|
|
|
|
def _icd_of(page, n):
|
|
row = page.locator(sel.SEL_CPT_ROWS).nth(n)
|
|
return [row.locator(s).first.input_value() for s in sel.SEL_CPT_ICD]
|
|
|
|
|
|
# ── detect (no browser) ──────────────────────────────────────────────────────
|
|
|
|
@case
|
|
def detect_a_vaccine_code_on_any_payer():
|
|
assert cond.detect("C1", _claim("99213", "90707"))
|
|
data = _claim("90656")
|
|
data["insurance"] = [{"Name": "Medicare"}]
|
|
assert cond.detect("C1", data), "Medicare too"
|
|
|
|
|
|
@case
|
|
def detect_even_without_z23_so_the_claim_goes_to_review():
|
|
assert cond.detect("C1", _claim("90707", icd=_ICD[:1]))
|
|
|
|
|
|
@case
|
|
def admin_codes_alone_or_no_vaccine_never_fire_it():
|
|
assert not cond.detect("C1", _claim("99213", "90471", "90460"))
|
|
assert not cond.detect("C1", _claim("99213"))
|
|
assert not cond.detect("C1", {})
|
|
|
|
|
|
@case
|
|
def not_on_a_dental_claim():
|
|
assert not cond.detect("C1", _claim("90707", screen="dental"))
|
|
|
|
|
|
@case
|
|
def z23_is_found_with_or_without_a_dot():
|
|
for code in ("Z23", "z23", " Z23. ", "Z23.0"):
|
|
data = _claim("90707", icd=[{"#": "3", "Code": code}])
|
|
assert cond.z23_pointer(data) == "3", code
|
|
assert cond.z23_pointer(_claim("90707", icd=[{"#": "1",
|
|
"Code": "Z23.5"}])) is None
|
|
|
|
|
|
# ── apply (browser) ──────────────────────────────────────────────────────────
|
|
|
|
@case
|
|
def the_pointer_goes_in_the_first_empty_slot_of_each_vaccine_line(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
|
_inject_vaccine_row(page, "90656", "CPTROW_4", ("", "", "", ""))
|
|
first_before = _icd_of(page, 0)
|
|
result = cond.apply(page, "C1", _claim("99213", "90707", "90656"))
|
|
assert isinstance(result, ConditionResult)
|
|
assert result.changed and not result.review, result
|
|
assert [c["code"] for c in result.changes] == ["90707", "90656"]
|
|
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
|
assert _icd_of(page, 4) == ["2", "", "", ""]
|
|
assert _icd_of(page, 0) == first_before, "99213 must be left alone"
|
|
|
|
|
|
@case
|
|
def a_line_that_already_carries_it_is_left_alone(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "2", "", ""))
|
|
result = cond.apply(page, "C1", _claim("90707"))
|
|
assert result.changed is False and not result.review, result
|
|
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
|
|
|
|
|
@case
|
|
def no_z23_on_the_claim_is_review_and_nothing_changes(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
|
result = cond.apply(page, "C1", _claim("90707", icd=_ICD[:1]))
|
|
assert result.changed is False
|
|
assert "Z23 not on the claim" in result.review, result
|
|
assert _icd_of(page, 3) == ["1", "", "", ""]
|
|
|
|
|
|
@case
|
|
def a_line_with_no_free_slot_is_review_the_others_still_fixed(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "3", "4", "5"))
|
|
_inject_vaccine_row(page, "90656", "CPTROW_4", ("1", "", "", ""))
|
|
result = cond.apply(page, "C1", _claim("90707", "90656"))
|
|
assert [c["code"] for c in result.changes] == ["90656"], result
|
|
assert "no free pointer slot" in result.review and "90707" in \
|
|
result.review, result
|
|
assert _icd_of(page, 3) == ["1", "3", "4", "5"]
|
|
|
|
|
|
@case
|
|
def the_comment_names_the_codes(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3")
|
|
text = cond.summary_comment(cond.apply(page, "C1", _claim("90707")))
|
|
assert "Z23" in text and "90707" in text, text
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3")
|
|
text = cond.describe(cond.apply(page, "C1", _claim("90707")))
|
|
assert "90707" in text and "Z23" in text, text
|
|
|
|
|
|
# ── wiring ───────────────────────────────────────────────────────────────────
|
|
|
|
@case
|
|
def it_is_registered_before_priced_line_check():
|
|
names = [m.NAME for m in CONDITIONS]
|
|
assert "vaccine_z23_pointer" in names
|
|
assert names.index("vaccine_z23_pointer") < names.index(
|
|
"priced_line_check"), names
|
|
|
|
|
|
@case
|
|
def the_charge_summary_has_a_fallback_comment():
|
|
assert "Z23" in cs.COMMENTS["vaccine_z23_pointer"]
|
|
|
|
|
|
@case
|
|
def run_all_adds_the_pointer(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_vaccine_row(page, "90707", "CPTROW_3", ("1", "", "", ""))
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
results = run_all(page, "C1", _claim("90707"))
|
|
assert "vaccine_z23_pointer" in [r.condition_name for r in results], \
|
|
results
|
|
assert _icd_of(page, 3) == ["1", "2", "", ""]
|
|
|
|
|
|
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())
|