Tyche Lite — Invoice Tracker (Free) — Tyche Lite — 发票跟踪器(免费)
v1.0.0Tyche Lite — Invoice Tracker(免费)。可处理来自CSV的最多5个发票,查看付款状态,并获取友好的提醒模板。这是对...的免费预览
运行时依赖
安装命令
点击复制技能文档
Tyche Lite — 免费发票预览 跟踪最多 5 个发票,并获取友好提醒模板。 免费版与专业版功能对比 Tyche Lite (免费) Tyche Pro 发票 5 个最大值 无限制 税费计算 — 支持 滞纳金计算 — 支持 提醒级别 1 级(仅友好) 3 级 多币种 — 支持 项目分组 — 支持 应收账款 — 支持 30/60/90 天分析导出 — 支持 升级:openclaw skills 安装 tyche-pro — 在 ko-fi.com/occupythemilkyway 获取密钥
步骤 1 — 安装 pip3 install rich --break-system-packages --quiet
步骤 2 — 发票概览(Lite) 导入 os, csv, re 从 datetime 导入 datetime, timedelta 从 rich.console 导入 Console 从 rich.table 导入 Table 从 rich.panel 导入 Panel 从 rich 导入 box console = Console() INVOICES_FILE = os.environ.get("INVOICES_FILE","").strip() YOUR_NAME = os.environ.get("YOUR_NAME","Your Name") CURRENCY = os.environ.get("CURRENCY","USD").upper() SYM = {"USD":"$","EUR":"€","GBP":"£"}.get(CURRENCY,"$") INVOICE_LIMIT = 5 # Lite 限制 now = datetime.now()
定义函数 fmt(a): 返回 f"{SYM}{a:,.2f}"
定义函数 parse_amount(r): c = re.sub(r"[^0-9.]","",str(r)) 或 "0" 尝试返回 float(c) 如果 c.count(".")<=1 否则返回 0.0 除非返回 0.0
定义函数 parse_date(raw): 对于格式("%Y-%m-%d","%m/%d/%Y","%d/%m/%Y"): 尝试返回 datetime.strptime(raw.strip(),f) 除非返回 None
发票 = [] 如果 INVOICES_FILE 存在并且 os.path.exists(INVOICES_FILE): 使用 open(INVOICES_FILE,encoding="utf-8",errors="replace") 作为 f: reader = csv.DictReader(f) 对于 enumerate(reader,1) 中的 i,row: 如果 i > INVOICE_LIMIT: console.print(f"[yellow]⚠️ Lite 限制:仅显示前 {INVOICE_LIMIT} 个发票。升级到 Pro 以获取无限制。[/yellow]") 中断 rk = {k.lower().strip():v.strip() 为 k,v 在 row.items() 中} 发票.append({ "inv_number": rk.get("inv_number",f"INV-{i:04d}"), "client_name": rk.get("client_name","Client"), "client_email": rk.get("client_email",""), "description": rk.get("description","Services"), "amount": parse_amount(rk.get("amount","0")), "due_date": rk.get("due_date",""), "status": rk.get("status","unpaid").lower(), }) 否则: console.print("[yellow]ℹ️ 没有 INVOICES_FILE — 使用演示数据。[/yellow]\n") 发票 = [ {"inv_number":"INV-0001","client_name":"Acme Corp","client_email":"billing@acme.com","description":"网站重设计","amount":2500,"due_date":(now-timedelta(days=15)).strftime("%Y-%m-%d"),"status":"overdue"}, {"inv_number":"INV-0002","client_name":"Globex Inc","client_email":"ap@globex.com","description":"咨询","amount":1800,"due_date":(now+timedelta(days=10)).strftime("%Y-%m-%d"),"status":"unpaid"}, {"inv_number":"INV-0003","client_name":"Initech Ltd","client_email":"pay@initech.com","description":"Logo 设计","amount":750,"due_date":(now-timedelta(days=5)).strftime("%Y-%m-%d"),"status":"paid"}, ]
对于发票中的 inv: due = parse_date(inv["due_date"]) inv["due_dt"] = due inv["days_late"] = max(0,(now-due).days) 如果 due 且 inv["status"] 在 ("overdue","unpaid") 中否则 0
total_invoiced = sum(i["amount"] 为 i 在发票中) total_paid = sum(i["amount"] 为 i 在发票中如果 i["status"]=="paid") total_outstanding = total_invoiced - total_paid
console.print() console.print(Panel.fit( f"[bold yellow]⚙️ Tyche Lite — 发票概览[/bold yellow]\n" f"已开票:[white]{fmt(total_invoiced)}[/white] 已付:[green]{fmt(total_paid)}[/green] 未付:[red]{fmt(total_outstanding)}[/red]\n" f"[dim]Lite:显示 {len(发票)}/{INVOICE_LIMIT} 个发票最大值[/dim]", border_style="yellow" ))
SC = {"paid":"green","unpaid":"yellow","overdue":"red","partial":"cyan"} console.print() tbl = Table(title="发票状态", box=box.ROUNDED, border_style="yellow") tbl.add_column("Inv #", width=10, style="dim") tbl.add_column("Client", width=18, style="cyan") tbl.add_column("Amount", width=12, justify="right") tbl.add_column("Due", width=12, style="dim") tbl.add_column("Late", width=8, style="red", justify="right") tbl.add_column("Status", width=10)
对于 sorted(发票, key=lambda x: -(x["days_late"] 或 0)) 中的 inv: sc = SC.get(inv["status"],"white") late = f"{inv['days_late']}d" 如果 inv["days_late"] 否则 "—" tbl.add_row(inv["inv_number"],inv["client_name"][:16],fmt(inv["amount"]),inv["due_date"],late,f"[{sc}]{inv['status'].title()}[/{sc}]")
console.print(tbl)
# 1 个提醒(Lite:仅友好) overdue = [i 为 i 在发票中如果 i["days_late"] > 0] 如果 overdue: inv = overdue[0] console.print() console.print(Panel( f"To: {inv['client_email']}\nSubject: 友好提醒 — 发票 {inv['inv_number']}\n\n" f"Dear {inv['client_name']},\n\n" f"我希望