Files
VasanthandClaude Opus 5.5 3942fad6cd Add rpa-bot-handoff skill: ops guide for bot handover
The skill scans a bot with subagents (never runs it) and writes
HANDOVER.md, an 11-section ops guide in the operator's order: where is
what, how to run, how long to wait, after the run, how to see an
exception, exception list, how you will know, how to tell us, don'ts,
issues seen before. Dev material (check against 12 failure classes from
Tebra Auto Post, gaps with file:line, incidents) goes to HANDOVER-DEV.md.

Also adds ADR-0004, glossary terms, five fixture bots and a handoff eval
runner (--resume, --regrade) with results from 47 runs of the earlier
13-section version. The grader still expects that structure and must be
updated before the next eval run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 15:01:42 +05:30

102 lines
3.5 KiB
Python

"""Ledger reconciler: compares the bank statement Excel with LedgerPro and posts missing entries.
Shipped as LedgerBot.exe (PyInstaller) and started through launcher.exe."""
import json
import logging
import os
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
import pyautogui
from openpyxl import Workbook, load_workbook
VERSION = "3.2.0" # shown in the window title "LedgerBot v3.2.0"
FROZEN = getattr(sys, "frozen", False)
BASE = Path(sys.executable).parent if FROZEN else Path(__file__).parent
ASSETS = Path(getattr(sys, "_MEIPASS", BASE)) / "assets"
SHORTCUT_DIRS = [Path(os.environ.get("USERPROFILE", "")) / "Desktop", Path(r"C:\Users\Public\Desktop")]
CONFIDENCE = 0.9 # image match; buttons were captured at 100% display scaling, 1920x1080
FIND_WAIT_S = 20
TOLERANCE = 0.01
logging.basicConfig(filename=BASE / "logs" / f"ledger_{datetime.now():%Y%m%d}.log", level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("ledger")
def find_shortcut():
for d in SHORTCUT_DIRS:
lnk = d / "LedgerPro.lnk"
if lnk.exists():
return lnk
raise RuntimeError("LedgerPro shortcut not found on the Desktop")
def click(image):
end = time.time() + FIND_WAIT_S
while time.time() < end:
hit = pyautogui.locateCenterOnScreen(str(ASSETS / image), confidence=CONFIDENCE)
if hit:
pyautogui.click(hit)
return
time.sleep(1)
pyautogui.screenshot(str(BASE / "logs" / f"notfound_{image}"))
raise RuntimeError(f"Button {image} not found on screen")
def ledger_entries(path):
"""LedgerPro export (File > Export > Excel) saved by the bot to data/ledger_export.xlsx."""
ws = load_workbook(path, data_only=True).active
return {str(r[0]): float(r[1]) for r in ws.iter_rows(min_row=2, values_only=True) if r[0]}
def reconcile(statement, ledger):
for ref, amount in statement.items():
if ref not in ledger:
yield ref, amount, "Missing", "Not in LedgerPro - posting"
elif abs(ledger[ref] - amount) > TOLERANCE:
yield ref, amount, "Mismatch", f"LedgerPro has {ledger[ref]:.2f}"
else:
yield ref, amount, "OK", ""
def post_entry(ref, amount):
click("new_entry.png")
pyautogui.write(ref)
pyautogui.press("tab")
pyautogui.write(f"{amount:.2f}")
click("save_btn.png")
def main(statement_path):
(BASE / "BOT_WAS_RUN").write_text("1")
subprocess.Popen(["cmd", "/c", "start", "", str(find_shortcut())])
time.sleep(15)
click("export_excel.png")
time.sleep(10)
ledger = ledger_entries(BASE / "data" / "ledger_export.xlsx")
ws_in = load_workbook(statement_path, data_only=True).active
statement = {str(r[0]): float(r[1]) for r in ws_in.iter_rows(min_row=2, values_only=True) if r[0]}
out = Workbook()
ws = out.active
ws.append(["Reference", "Amount", "Result", "Note"])
counts = {"OK": 0, "Missing": 0, "Mismatch": 0, "Error": 0}
for ref, amount, result, note in reconcile(statement, ledger):
if result == "Missing":
try:
post_entry(ref, amount)
note = "Posted"
except RuntimeError as e:
result, note = "Error", str(e)
counts[result] += 1
ws.append([ref, amount, result, note])
log.info("%s %s %s", ref, result, note)
out.save(BASE / "data" / "output" / f"Reconcile_{datetime.now():%Y%m%d_%H%M}.xlsx")
print(json.dumps({"summary": counts}))
if __name__ == "__main__":
main(sys.argv[1])