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:
ManikandanandClaude Opus 5.5 committed 2026-10-23 18:38:17 +05:30
commit ba15a574ab
205 files changed
+56772

No files matched your search

+865
View File
@@ -0,0 +1,865 @@
r"""
core/charge_summary.py — the 24-column Charge Summary sheet written at the
end of every claims run.
python tests\test_charge_summary.py
No browser, no eCW: a fictional report is written to a temp folder.
Review items D1 and D10 (2026-10-05): a TEST_CLAIM_IDS_FILE run writes
Charge Summary_TEST_<date>.xlsx, and a same-day file held open (as Excel
holds it) no longer costs the run its sheet — it lands under a `__HHMMSS`
name instead.
Review items D2/St1 (unit D, 2026-10-05): a third Category, "Review" /
"Needs manual review", for every claim the bot did not really check. The
Pass/Fail cases below now pass a fully-read UB claim (`_ub()`: CPT rows, a
progress note that was read, conditions that ran) — before, a bare
`{"_conditions": {"fired": []}}` was enough for Pass, and that bare dict
(no note, no rows) is exactly what now reads Review.
"""
import io
import re
import sys
import pathlib
import tempfile
import contextlib
ROOT = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
from ecw_bot import settings as cfg # noqa: E402
from ecw_bot.core import charge_summary as cs, spreadsheet # noqa: E402
from ecw_bot.flows import export_medcor_ready as flow # noqa: E402
CASES = []
REPORT_HEADER = ["", "CLAIMTYPE", "CLAIMS#", "SERVICE DATE", "PVDR", "PATIENT",
"PAYER", "STATUS", "CHARGES", "PMTS/ADJS", "Adjustment",
"WithHeld", "Balance", "Visit Type", "Account Number",
"Provider Name", "Guarantor Name"]
def _row(claim, payer="Medicaid", pmts="35.00", adj="5.00", ctype="U"):
return ["", ctype, claim, "09/01/2026", "AB", "Test, Patient", payer,
"Medcor Ready", "275.00", pmts, adj, "0.00", "240.00",
"Acute Care Walk In", "123456", "Doe, Jane", "Test, Patient"]
REPORT = [REPORT_HEADER, _row("1000001"), _row("1000002", payer="DentaQuest",
ctype="D"), _row("1000001"), _row("1000003", payer="Unmapped Payer X")]
MAPPING = {"Medicaid": "Medicaid", "DentaQuest": "Dentaquest"}
def case(fn):
CASES.append(fn)
return fn
def _build(outcomes=None):
return cs.build_rows(REPORT, 0, 2, outcomes or {}, "10/01/2026",
"eCW Bot", MAPPING)
def _col(name):
return cs.HEADERS.index(name)
def _ub(fired=(), saved=False, **extra):
"""A UB/HCFA claim the bot fully read: CPT rows, a progress note it
actually read, and the conditions run with `fired` changing something.
`extra` overrides or adds keys (`_conditions` included)."""
data = {"claim_no": "1",
"cpt": [{"code": "99213", "checked": True, "fee": "100.00"}],
"progress_notes": {"opened": "modal", "text": "Office visit."},
"_conditions": {"fired": list(fired), "saved": saved,
"failed": [], "review": [], "comments": {}}}
data.update(extra)
return data
# ── claim_outcome ────────────────────────────────────────────────────────────
@case
def not_found_or_not_read_leaves_the_tracking_columns_blank():
assert cs.claim_outcome(False, {}) == {}
assert cs.claim_outcome(True, {}) == {}
@case
def clean_comment_is_the_teams_exact_wording():
# Operator, 2026-10-06 — copied from the team's sheet, character for
# character ("Pending with Errors", not the earlier "Medcor Ready").
assert cs.CLEAN == ("Clean claims (claims that have no errors, placed "
"in Pending with Errors)"), cs.CLEAN
@case
def nothing_fired_is_a_clean_pass():
o = cs.claim_outcome(True, _ub())
assert o == {"Comments": cs.CLEAN, "Current Status": "Ready to Submit",
"Category": "Pass"}, o
@case
def a_fired_condition_is_a_fail_with_its_comment():
o = cs.claim_outcome(True, _ub(["depression_dx_pointer", "dummy_codes"]))
assert o["Category"] == "Fail", o
assert "ICD pointer issue" in o["Comments"], o
assert "DCONS/Dscprit" in o["Comments"], o
assert o["Current Status"] == "Corrections pending (not saved)", o
@case
def a_saved_correction_reads_ready_to_submit():
o = cs.claim_outcome(True, _ub(["telehealth"], saved=True))
assert o["Current Status"] == "Ready to Submit", o
@case
def dental_high_risk_and_expired_sliding_fee_are_findings():
o = cs.claim_outcome(True, {
"high_risk": {"high_risk": True},
"sliding_fee": {"verdict": "expired", "reason": "DOS after range"}})
assert o["Category"] == "Fail", o
assert cs.HIGH_RISK in o["Comments"], o
assert "Sliding fee EXPIRED" in o["Comments"], o
@case
def dental_clean_with_active_sliding_fee_is_a_pass():
o = cs.claim_outcome(True, {"high_risk": {"high_risk": False},
"sliding_fee": {"verdict": "active"}})
assert o["Category"] == "Pass" and o["Comments"] == cs.CLEAN, o
@case
def dental_sliding_fee_none_is_noted_but_still_a_pass():
o = cs.claim_outcome(True, {"high_risk": {"high_risk": False},
"sliding_fee": {"verdict": "none",
"reason": "no dates"}})
assert o["Category"] == "Pass", o
assert "Sliding fee NONE" in o["Comments"], o
# ── claim_outcome: Review (review items D2/St1, unit D) ─────────────────────
REVIEW = {"Current Status": "Needs manual review", "Category": "Review"}
def _review(data):
o = cs.claim_outcome(True, data)
assert {k: o.get(k) for k in REVIEW} == REVIEW, o
return o["Comments"]
@case
def a_form_that_could_not_be_read_is_review_not_blank():
c = _review({"claim_no": "1", "_error": "Timeout 30000ms exceeded."})
assert c == "Bot could not read the claim (Timeout 30000ms exceeded.)", c
@case
def a_form_that_never_finished_loading_is_review_with_its_reason():
c = _review(_ub(_not_evaluated="HCFA form: no CPT row after 20s",
_conditions=None, cpt=[]))
assert c.startswith("HCFA form: no CPT row after 20s"), c
# The flow skipped the conditions ON PURPOSE — not APPLY_CORRECTIONS=0.
assert "APPLY_CORRECTIONS" not in c, c
@case
def apply_corrections_off_is_review():
data = _ub()
del data["_conditions"]
assert _review(data) == "Conditions not run (APPLY_CORRECTIONS=0)"
@case
def a_progress_note_never_read_is_review_in_every_state():
for note, state in ((None, "not opened"), ({"opened": "nothing"}, "nothing"),
({"opened": "absent"}, "absent"),
({"opened": "disabled"}, "disabled")):
data = _ub()
if note is None:
del data["progress_notes"]
else:
data["progress_notes"] = note
c = _review(data)
assert c == (f"Progress note not read ({state}) — telehealth not "
f"checked"), (note, c)
@case
def a_progress_note_that_raised_is_review_with_its_first_line():
c = _review(_ub(progress_notes={"opened": "iframe",
"error": "Target closed\nCall log: ..."}))
assert c == ("Progress note not read (error: Target closed) — telehealth "
"not checked"), c
@case
def zero_cpt_rows_read_is_review():
assert _review(_ub(cpt=[])) == "No CPT rows were read"
@case
def a_condition_that_raised_is_review():
c = _review(_ub(_conditions={"fired": [], "saved": False, "comments": {},
"review": [],
"failed": [{"name": "telehealth",
"stage": "apply",
"error": "Timeout"}]}))
assert c == "telehealth failed: Timeout", c
@case
def a_condition_review_reason_is_review():
c = _review(_ub(_conditions={
"fired": [], "saved": False, "comments": {}, "failed": [],
"review": [{"name": "dummy_codes",
"reason": "CPT/HCPCS tab could not be opened — not "
"checked"}]}))
assert c == ("dummy_codes: CPT/HCPCS tab could not be opened — not "
"checked"), c
@case
def dental_unreadable_billing_notes_is_review():
c = _review({"claim_no": "1", "high_risk": {"high_risk": None,
"match": ""}})
assert c == "High risk: Billing Notes could not be read", c
@case
def dental_sliding_fee_unknown_is_review_not_a_pass():
c = _review({"high_risk": {"high_risk": False},
"sliding_fee": {"verdict": "unknown",
"reason": "no Patient Info"}})
assert c == "Sliding fee UNKNOWN (no Patient Info)", c
@case
def a_dental_claim_is_not_asked_for_conditions_notes_or_cpt_rows():
"""Dental never runs the conditions or opens a note — by design."""
o = cs.claim_outcome(True, {"claim_no": "1",
"high_risk": {"high_risk": False}})
assert o["Category"] == "Pass" and o["Comments"] == cs.CLEAN, o
@case
def a_dental_claim_that_ran_the_conditions_carries_both_in_one_row():
"""
Operator, 2026-10-12: a Dental claim runs the conditions too — its row
has the condition comments AND the Dental findings, and the same
claim-level checks as UB/HCFA (it did read a note and CPT rows).
"""
data = _ub(["dummy_codes"], screen="dental",
high_risk={"high_risk": True, "match": ""},
sliding_fee={"verdict": "active"})
o = cs.claim_outcome(True, data)
assert o["Category"] == "Fail", o
assert "DCONS/Dscprit" in o["Comments"], o
assert cs.HIGH_RISK in o["Comments"], o
assert o["Current Status"] == "Corrections pending (not saved)", o
assert cs.is_dental(data)
@case
def a_dental_read_is_asked_for_its_note_and_rows_like_any_claim():
data = _ub(screen="dental", high_risk={"high_risk": False, "match": ""},
progress_notes={"opened": "disabled"})
c = _review(data)
assert "Progress note not read (disabled)" in c, c
no_cond = {k: v for k, v in data.items() if k != "_conditions"}
no_cond["progress_notes"] = {"opened": "modal", "text": "x"}
assert "Conditions not run" in _review(no_cond), _review(no_cond)
@case
def a_dental_claim_that_never_finished_loading_is_review():
c = _review({"high_risk": {"high_risk": True},
"_not_evaluated": "Dental form: no service line after 20s"})
# The finding first, then the reason.
assert c == (f"{cs.HIGH_RISK}; Dental form: no service line after 20s"), c
@case
def review_wins_over_fail_and_carries_both():
"""A fired condition on a claim whose note was never read."""
c = _review(_ub(["dummy_codes"], progress_notes={"opened": "disabled"}))
assert c == (f"{cs.COMMENTS['dummy_codes']}; Progress note not read "
f"(disabled) — telehealth not checked"), c
@case
def every_reason_is_listed_in_order():
data = _ub(["telehealth"], cpt=[], progress_notes={"opened": "nothing"})
data["_conditions"]["failed"] = [{"name": "j2930_j2001_update",
"stage": "detect", "error": "boom"}]
data["_conditions"]["review"] = [{"name": "vaccine_admin_codes",
"reason": "payer unmapped"}]
c = _review(data)
assert c.split("; ") == [
cs.COMMENTS["telehealth"],
"Progress note not read (nothing) — telehealth not checked",
"No CPT rows were read",
"j2930_j2001_update failed: boom",
"vaccine_admin_codes: payer unmapped"], c
@case
def a_fail_uses_the_conditions_own_comment_when_it_has_one():
data = _ub(["telehealth", "dummy_codes"])
data["_conditions"]["comments"] = {"telehealth": "Telehealth (POS 02 set)"}
o = cs.claim_outcome(True, data)
assert o["Category"] == "Fail", o
assert o["Comments"] == ("Telehealth (POS 02 set); "
+ cs.COMMENTS["dummy_codes"]), o
@case
def a_partial_note_with_no_sections_is_review():
"""
Unit D review, finding 1 (2026-10-05): the header arrived and nothing
under it — no Procedure Codes, so telehealth saw nothing (the D8 false
Clean). This test pinned Pass before; it is Review now.
"""
c = _review(_ub(progress_notes={"opened": "modal", "text": "x",
"partial": True, "partial_sections": 0}))
assert c == "Progress note partial (0 sections) — telehealth not checked", c
@case
def a_partial_note_with_some_sections_is_review_too():
"""Sections never settled: the Procedure Codes may still be missing."""
c = _review(_ub(progress_notes={"opened": "modal", "text": "x",
"partial": True, "partial_sections": 3}))
assert c == ("Progress note may be partial (3 section(s)) — telehealth "
"may not be fully checked"), c
@case
def a_partial_note_with_an_unknown_section_count_has_no_question_mark():
"""
Unit F review, finding 4 (2026-10-05): these reasons are also the
on-screen "Needs review" lines, and D9 keeps "?" off the client's box.
"""
c = _review(_ub(progress_notes={"opened": "modal", "text": "x",
"partial": True}))
assert c == ("Progress note may be partial (section count unknown) — "
"telehealth may not be fully checked"), c
assert "?" not in c, c
@case
def a_partial_note_keeps_the_fired_conditions_comment_first():
c = _review(_ub(["telehealth"],
progress_notes={"opened": "tab", "text": "x",
"partial": True, "partial_sections": 0}))
assert c == (f"{cs.COMMENTS['telehealth']}; Progress note partial "
f"(0 sections) — telehealth not checked"), c
@case
def first_line_is_the_one_shared_helper():
"""
Unit D review, finding 4: core/text.first_line replaced three copies
(conditions/base, core/charge_summary, the flow's inline one).
"""
from ecw_bot.core.text import first_line
from ecw_bot.conditions import base
assert first_line(TimeoutError("Timeout 30000ms\n call log:\n x")) == \
"Timeout 30000ms"
assert first_line(ValueError()) == "ValueError" # empty message
assert first_line(" boom \nmore") == "boom"
assert first_line("") == ""
assert base.first_line is first_line and cs.first_line is first_line
assert flow.first_line is first_line
# Final review, 2026-10-05: the three private copies that were left
# (claim_form, spreadsheet, vaccine_admin_codes) are gone too. Two of
# them returned "" for a message that starts with a newline.
from ecw_bot.pages import claim_form, progress_note
from ecw_bot.core import spreadsheet
from ecw_bot.conditions import vaccine_admin_codes
for mod in (claim_form, progress_note, spreadsheet, vaccine_admin_codes):
assert mod.first_line is first_line, mod.__name__
assert not hasattr(mod, "_first_line"), mod.__name__
assert first_line(RuntimeError("\nreal first line\nmore")) == \
"real first line"
# ── Final review, 2026-10-05 ─────────────────────────────────────────────────
@case
def a_saved_claim_that_is_review_says_it_was_saved():
"""
"Needs manual review" alone hid a save that had happened: the reviewer
could treat the claim as untouched and redo or undo live edits.
"""
o = cs.claim_outcome(True, _ub(["zero_fee_uncheck"], saved=True,
progress_notes={"opened": "disabled"}))
assert o["Category"] == "Review", o
assert o["Current Status"] == "Needs manual review", o
assert o["Comments"].startswith(f"{cs.SAVED_NOTE}; "
f"{cs.COMMENTS['zero_fee_uncheck']}"), o
unsaved = cs.claim_outcome(True, _ub(["zero_fee_uncheck"],
progress_notes={"opened": "disabled"}))
assert cs.SAVED_NOTE not in unsaved["Comments"], unsaved
@case
def a_search_that_raised_is_review_not_a_blank_row():
data = {"claim_no": "1", "_search_error": "Lookup never became visible"}
o = cs.claim_outcome(False, data)
assert o == {"Comments": "Bot could not search the claim (Lookup never "
"became visible)",
"Current Status": "Needs manual review",
"Category": "Review"}, o
# A claim really not in the queue stays blank.
assert cs.claim_outcome(False, {}) == {}
@case
def an_error_after_the_conditions_ran_is_worded_by_stage():
data = {"claim_no": "1", "_error": "describe blew up",
"_conditions": {"fired": ["dummy_codes"], "saved": False,
"failed": [], "review": [], "comments": {}}}
o = cs.claim_outcome(True, data)
assert o["Category"] == "Review", o
assert o["Comments"] == (f"{cs.COMMENTS['dummy_codes']}; Bot failed after "
f"the conditions ran (describe blew up)"), o
plain = cs.claim_outcome(True, {"claim_no": "1", "_error": "x"})
assert plain["Comments"] == "Bot could not read the claim (x)", plain
@case
def cpt_rows_with_no_icd_rows_is_review():
"""depression_dx_pointer reads the ICD list; an empty one is unchecked."""
o = cs.claim_outcome(True, _ub(icd=[]))
assert o["Category"] == "Review", o
assert o["Comments"] == "No ICD rows were read", o
assert cs.claim_outcome(True, _ub(icd=[{"code": "R69"}]))["Category"] \
== "Pass"
# Zero CPT rows already says it; no second ICD line on top.
reasons = cs.review_reasons(_ub(cpt=[], icd=[]))
assert reasons == ["No CPT rows were read"], reasons
@case
def a_note_skipped_on_a_form_that_never_loaded_is_named():
reasons = cs.review_reasons(_ub(progress_notes={"opened": "skipped"}))
assert reasons == ["Progress note not read (skipped) — telehealth not "
"checked"], reasons
@case
def the_telehealth_fallback_comments_claim_nothing_that_may_not_have_happened():
"""
Used only when a summary_comment hook raises: they must not say "G2025
added" (it can fail), "93 or 95" (Medicaid is GT) or "unchecked".
"""
for name in ("telehealth", "medicare_telehealth"):
text = cs.COMMENTS[name]
for false_claim in ("G2025 added", "93 or 95", "unchecked"):
assert false_claim not in text, (name, text)
assert "see the log" in text, text
@case
def a_settled_note_is_still_a_pass():
"""Control: `partial` absent (the note settled) — Pass as before."""
o = cs.claim_outcome(True, _ub(progress_notes={"opened": "modal",
"text": "x"}))
assert o["Category"] == "Pass", o
# ── build_rows ───────────────────────────────────────────────────────────────
@case
def header_is_the_formats_24_columns_in_order():
rows = _build()
assert rows[0] == cs.HEADERS and len(rows[0]) == 24
assert "CLAIMTYPE" not in rows[0]
@case
def one_row_per_claim_numbered_in_report_order():
rows = _build()
assert [r[_col("CLAIMS#")] for r in rows[1:]] == ["1000001", "1000002",
"1000003"]
assert [r[_col("S.No")] for r in rows[1:]] == [1, 2, 3]
@case
def report_columns_are_copied_and_group_and_refunds_are_derived():
r = _build()[1]
assert r[_col("PAYER")] == "Medicaid" and r[_col("CHARGES")] == "275.00"
assert r[_col("Guarantor Name")] == "Test, Patient"
assert r[_col("Group Name")] == "Medicaid"
assert r[_col("Pmts/Refunds")] == "30.00", r # 35.00 - 5.00
@case
def unmapped_payer_gets_a_blank_group():
assert _build()[3][_col("Group Name")] == ""
@case
def tracking_columns_fill_only_for_claims_the_run_read():
outcomes = {"1000001": cs.claim_outcome(True, _ub())}
rows = _build(outcomes)
read, unread = rows[1], rows[2]
assert read[_col("Category")] == "Pass"
assert read[_col("Completed On")] == "10/01/2026"
assert read[_col("Recevied date")] == "10/01/2026"
assert read[_col("Assigned To ")] == "eCW Bot"
for h in ("Recevied date", "Assigned To ", "Completed On", "Comments",
"Current Status", "Category"):
assert unread[_col(h)] == "", (h, unread)
@case
def a_claim_id_only_test_sheet_still_builds():
rows = cs.build_rows([["Claim No"], ["9000002"], ["9000001"]], 0, 0,
{}, "10/01/2026", "eCW Bot", MAPPING)
assert [r[_col("CLAIMS#")] for r in rows[1:]] == ["9000002", "9000001"]
assert rows[1][_col("PAYER")] == "" and rows[1][_col("Pmts/Refunds")] == ""
# ── write ────────────────────────────────────────────────────────────────────
@case
def write_saves_a_readable_sheet_and_keeps_a_same_day_file_aside():
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
report = tmp / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
out = cs.write(report, {}, tmp, "10-01-2026", "eCW Bot")
assert out.name == "Charge Summary_10-01-2026.xlsx"
back = spreadsheet.read_rows(out)
# read_rows strips cells, so the format's "Assigned To " reads back
# without its trailing space.
assert back[0] == [h.strip() for h in cs.HEADERS], back[0]
assert len(back) == 4, back
cs.write(report, {}, tmp, "10-01-2026", "eCW Bot")
kept = list(tmp.glob("Charge Summary_10-01-2026__*.xlsx"))
assert len(kept) == 1, kept
@case
def file_name_is_live_or_TEST():
assert cs.file_name("10-01-2026") == "Charge Summary_10-01-2026.xlsx"
assert cs.file_name("10-01-2026", test_mode=True) == \
"Charge Summary_TEST_10-01-2026.xlsx"
@case
def test_mode_writes_the_TEST_file_and_leaves_the_live_one_alone():
"""D1: a test run must never push the live same-day summary aside."""
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
report = tmp / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
live = cs.write(report, {}, tmp, "10-01-2026", "eCW Bot")
live_bytes = live.read_bytes()
out = cs.write(report, {}, tmp, "10-01-2026", "eCW Bot",
test_mode=True)
assert out == tmp / "Charge Summary_TEST_10-01-2026.xlsx", out
assert len(spreadsheet.read_rows(out)) == 4
assert live.read_bytes() == live_bytes, "the live summary changed"
assert not list(tmp.glob("Charge Summary_10-01-2026__*.xlsx")), \
"the live summary was pushed aside by a test run"
@case
def a_same_day_summary_open_in_excel_does_not_lose_this_runs_sheet():
"""
D10: the earlier summary is held open (on Windows a Python handle
refuses the rename with WinError 32, as Excel does). The new sheet goes
to a `__HHMMSS` name, the open file is untouched, nothing raises, and
the path returned is the new file — the one the flow logs.
"""
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
report = tmp / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
first = cs.write(report, {}, tmp, "10-01-2026", "eCW Bot")
first_bytes = first.read_bytes()
outcomes = {"1000001": cs.claim_outcome(True, _ub())}
with open(first, "rb"):
out = cs.write(report, outcomes, tmp, "10-01-2026", "eCW Bot")
assert out != first, "wrote over the open file's name"
assert re.fullmatch(r"Charge Summary_10-01-2026__\d{6}\.xlsx",
out.name), out.name
assert first.read_bytes() == first_bytes, "the open file was changed"
back = spreadsheet.read_rows(out)
assert len(back) == 4, back
assert back[1][_col("Category")] == "Pass", back[1]
FALLBACK_NAME = "Charge Summary_10-01-2026__101010.xlsx"
def _flow_write(test_file, returns=FALLBACK_NAME):
"""
_write_charge_summary with charge_summary.write replaced by a recorder
that returns `returns`, and msgbox.info_box by one that records instead
of popping a real Windows box. Returns (args, kwargs it was called with,
printed text, [(title, body)] of boxes shown).
"""
calls = []
shown = []
def _record(*args, **kwargs):
calls.append((args, kwargs))
# By default, as if the usual name was open in Excel: the log must
# name THIS, and the operator must be told.
return pathlib.Path(returns)
orig = (cs.write, cfg.WRITE_CHARGE_SUMMARY, cfg.TEST_CLAIM_IDS_FILE,
flow.msgbox.info_box)
printed = io.StringIO()
try:
cs.write = _record
cfg.WRITE_CHARGE_SUMMARY = True
cfg.TEST_CLAIM_IDS_FILE = test_file
flow.msgbox.info_box = lambda t, b: shown.append((t, b))
with contextlib.redirect_stdout(printed):
flow._write_charge_summary({"file": pathlib.Path("r.xlsx")},
{"1": {"Category": "Pass"}})
finally:
(cs.write, cfg.WRITE_CHARGE_SUMMARY, cfg.TEST_CLAIM_IDS_FILE,
flow.msgbox.info_box) = orig
assert len(calls) == 1, calls
return calls[0][0], calls[0][1], printed.getvalue(), shown
@case
def flow_passes_test_mode_when_the_ids_came_from_a_test_sheet():
_, kwargs, _, _ = _flow_write("C:/somewhere/Standard Claims.xlsx")
assert kwargs.get("test_mode") is True, kwargs
@case
def flow_passes_test_mode_off_for_a_live_run():
_, kwargs, _, _ = _flow_write("")
assert kwargs.get("test_mode") is False, kwargs
@case
def flow_logs_the_file_actually_written():
_, _, printed, _ = _flow_write("")
line = "[SUMMARY] 1 claim(s) filled in -> " + FALLBACK_NAME
assert line in printed, printed
@case
def flow_shows_a_box_when_the_summary_went_to_another_name():
"""
Fix round 1 (review item D10, 2026-10-05): the person with the usual
file open in Excel is at the desk, so the fallback name goes in a box,
not only the log — else they present the stale sheet they have open.
"""
_, _, _, shown = _flow_write("")
assert len(shown) == 1, shown
title, body = shown[0]
assert title == "eCW Bot — Charge summary saved under another name", title
usual = cs.file_name(cfg.TODAY)
assert f"could not be saved as {usual}" in body, body
assert FALLBACK_NAME in body, body
# Fix round 2: the box must not promise the earlier file was untouched —
# a failed move-back can leave a half-file under the usual name.
assert "left as it was" not in body, body
@case
def flow_shows_no_box_when_the_summary_has_its_usual_name():
_, _, _, shown = _flow_write("", returns=cs.file_name(cfg.TODAY))
assert shown == [], shown
@case
def flow_shows_no_box_for_a_test_run_under_the_TEST_name():
_, _, _, shown = _flow_write("C:/somewhere/Standard Claims.xlsx",
returns=cs.file_name(cfg.TODAY, True))
assert shown == [], shown
@case
def flow_write_never_raises_on_a_bad_report():
shown = []
old = flow.msgbox.info_box
flow.msgbox.info_box = lambda t, b: shown.append(t)
try:
flow._write_charge_summary({"file": pathlib.Path("no_such_file.xlsx")},
{})
finally:
flow.msgbox.info_box = old
assert shown, "a failed write should show a box, not raise"
# ── per-claim writes (2026-10-12: the summary grows claim by claim) ─────────
@case
def the_report_is_read_once_while_it_is_unchanged():
"""Built after every claim, twice (Excel + DB): one read, not 2 x N."""
with tempfile.TemporaryDirectory() as tmp:
report = pathlib.Path(tmp) / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
reads = []
real = spreadsheet.read_rows
spreadsheet.read_rows = lambda p: reads.append(p) or real(p)
try:
first = cs.build(report, {}, "eCW Bot")
cs.build(report, {"1000001": cs.claim_outcome(True, _ub())},
"eCW Bot")
assert len(reads) == 1, reads
spreadsheet.write_xlsx(report, REPORT[:2])
again = cs.build(report, {}, "eCW Bot")
finally:
spreadsheet.read_rows = real
assert len(reads) == 2, "a changed report must be read again"
assert len(first) == 4 and len(again) == 2, (first, again)
@case
def write_into_rewrites_this_runs_own_file_with_no_backup():
"""
After the first claim, the sheet goes back into the file that claim
wrote — no `__HHMMSS` copy per claim; only the first write keeps an
earlier same-day file aside.
"""
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
report = tmp / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
first = cs.write(report, {"1000001": cs.claim_outcome(True, _ub())},
tmp, "10-01-2026", "eCW Bot")
out = cs.write(report, {"1000001": cs.claim_outcome(True, _ub()),
"1000002": cs.claim_outcome(True, _ub())},
tmp, "10-01-2026", "eCW Bot", into=first)
assert out == first, out
assert not list(tmp.glob("Charge Summary_10-01-2026__*.xlsx")), \
"a backup was made for this run's own file"
back = spreadsheet.read_rows(out)
assert back[1][_col("Category")] == "Pass", back[1]
assert back[2][_col("Category")] == "Pass", back[2]
@case
def write_into_a_locked_file_goes_to_a_new_name():
"""The operator opened the in-progress summary in Excel mid-run."""
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
report = tmp / "report.xlsx"
spreadsheet.write_xlsx(report, REPORT)
first = cs.write(report, {}, tmp, "10-01-2026", "eCW Bot")
first_bytes = first.read_bytes()
real = spreadsheet.write_xlsx
def locked(path, rows):
if pathlib.Path(path) == first:
raise PermissionError(13, "Permission denied")
return real(path, rows)
spreadsheet.write_xlsx = locked
try:
with contextlib.redirect_stdout(io.StringIO()) as printed:
out = cs.write(report, {"1000001": cs.claim_outcome(True,
_ub())},
tmp, "10-01-2026", "eCW Bot", into=first)
finally:
spreadsheet.write_xlsx = real
assert re.fullmatch(r"Charge Summary_10-01-2026__\d{6}\.xlsx",
out.name), out.name
assert first.read_bytes() == first_bytes, "the locked file changed"
assert spreadsheet.read_rows(out)[1][_col("Category")] == "Pass"
assert "open or locked" in printed.getvalue(), printed.getvalue()
def _flow_write_twice(first_returns, second_returns):
"""_write_charge_summary called for two claims of one run."""
calls, shown = [], []
returns = [pathlib.Path(first_returns), pathlib.Path(second_returns)]
def _record(*args, **kwargs):
calls.append(kwargs)
return returns[len(calls) - 1]
orig = (cs.write, cfg.WRITE_CHARGE_SUMMARY, cfg.TEST_CLAIM_IDS_FILE,
flow.msgbox.info_box)
info = {"file": pathlib.Path("r.xlsx")}
try:
cs.write = _record
cfg.WRITE_CHARGE_SUMMARY = True
cfg.TEST_CLAIM_IDS_FILE = ""
flow.msgbox.info_box = lambda t, b: shown.append((t, b))
with contextlib.redirect_stdout(io.StringIO()):
flow._write_charge_summary(info, {"1": {"Category": "Pass"}})
flow._write_charge_summary(info, {"1": {"Category": "Pass"},
"2": {"Category": "Fail"}})
finally:
(cs.write, cfg.WRITE_CHARGE_SUMMARY, cfg.TEST_CLAIM_IDS_FILE,
flow.msgbox.info_box) = orig
return calls, shown
@case
def flow_second_claim_writes_into_the_file_the_first_one_wrote():
usual = cs.file_name(cfg.TODAY)
calls, shown = _flow_write_twice(usual, usual)
assert calls[0].get("into") is None, calls
assert calls[1].get("into") == pathlib.Path(usual), calls
assert shown == [], shown
@case
def flow_shows_the_another_name_box_once_not_per_claim():
calls, shown = _flow_write_twice(FALLBACK_NAME, FALLBACK_NAME)
assert calls[1].get("into") == pathlib.Path(FALLBACK_NAME), calls
assert len(shown) == 1, shown
@case
def flow_shows_the_box_again_when_the_run_moves_to_a_new_name():
"""Mid-run, the operator opened this run's file: the sheet moves on."""
usual = cs.file_name(cfg.TODAY)
_, shown = _flow_write_twice(usual, FALLBACK_NAME)
assert len(shown) == 1 and FALLBACK_NAME in shown[0][1], shown
@case
def flow_write_error_box_shows_once_per_run():
shown = []
old = flow.msgbox.info_box
flow.msgbox.info_box = lambda t, b: shown.append(t)
info = {"file": pathlib.Path("no_such_file.xlsx")}
try:
with contextlib.redirect_stdout(io.StringIO()):
flow._write_charge_summary(info, {})
flow._write_charge_summary(info, {})
finally:
flow.msgbox.info_box = old
assert len(shown) == 1, shown
def main() -> int:
failures = 0
for fn in CASES:
name = fn.__name__.replace("_", " ")
try:
fn()
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
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
return 1 if failures else 0
if __name__ == "__main__":
sys.exit(main())