T

Skill 详情

Tyche Pro — Full Invoice & Payment Intelligence

Tyche Pro — 发票与财务引擎。生成专业PDF可用发票,追踪多币种付款,计算税费和滞纳金,发送…

来源平台:SkillHub
来源标识:SkillHub/tyche-pro
源文件:原始说明
办公效率 高关注 SkillHub 中 风险 下载 246 SkillHub
来源平台SkillHub
文档版本1.0.4
热度高关注
排名信号下载 246
概述 安装 文档 下载

快速判断

Tyche Pro — 发票与财务引擎。生成专业PDF可用发票,追踪多币种付款,计算税费和滞纳金,发送…

最后校验2026-05-27
来源平台SkillHub
安全提示
下载副本ZIP 可用

适合任务

  • 按 SkillHub 收录说明复用成熟任务流程。
  • 通过下载包离线阅读完整 Skill 内容。
  • 结合热度指标优先评估常用 Skill。

输入与输出

输入:任务目标、上下文材料、文件路径、约束条件或需要处理的内容。

输出:按 Skill 说明生成的文档、代码、检查结果、计划、建议或操作步骤。

示例任务

  • 使用 Tyche Pro — Full Invoice & Payment Intelligence 帮我处理当前任务,并说明需要准备哪些输入。
  • 根据 Tyche Pro — Full Invoice & Payment Intelligence 的说明,先列出使用前的安全检查项。

安装方式

  1. 下载本站提供的 Skill ZIP 并解压。
  2. 把解压后的 Skill 目录放入当前 AI 工具支持的 skills 目录。
  3. 如需在线查看原始内容,可打开 GitHub 的 SKILL.md

在线原始地址:skillhub-tyche-pro/SKILL.md

风险边界

SkillHub 提供了源站安全报告入口,但本站不替代人工审查。使用前仍需检查权限、外部依赖和敏感数据边界。

SKILL.md 文档介绍

Tyche Pro — Invoice & Revenue Dashboard

Everything in Tyche, plus multi-currency support, revenue goal tracking, project code grouping, aged receivables report, and CSV export of all analytics.

Pro features vs free Tyche

| Feature | Tyche (Free) | Tyche Pro |

|---------|-------------|-----------|

| Invoices | Unlimited | Unlimited |

| Late fee calc | ✅ | ✅ + per-invoice override |

| Reminder tiers | 3 tiers | 3 tiers + escalation log |

| Multi-currency | ❌ | ✅ Per-invoice currency |

| Project grouping | ❌ | ✅ Group by project_code |

| Revenue goal | ❌ | ✅ Progress bar |

| Aged receivables | ❌ | ✅ 30/60/90 day buckets |

| Analytics export | ❌ | ✅ Full CSV dashboard |

👉 Get Tyche Pro: openclaw skills install tyche-pro + key at ko-fi.com/s/4e0d922f4c

🔒 Security

All data stays on your machine. No transmission, no cloud.

---

Setup

1. Purchase your license key at ko-fi.com/s/4e0d922f4c ($9 one-time)

  • Or get all 5 Pro skills for $29ko-fi.com/s/7625accf3f (save $16)

2. Install: openclaw skills install tyche-pro

3. Activate: set the LICENSE_KEY environment variable to the key you received

4. Run — you're in

---

Step 1 — Install

pip3 install rich --break-system-packages --quiet

---

Step 2 — Full invoice & revenue dashboard (Pro)

import os, csv, re, json
from datetime import datetime, timedelta
from collections import defaultdict
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress, BarColumn, TextColumn
from rich import box

console = Console()

LICENSE_KEY = os.environ.get("LICENSE_KEY", "").strip()
if not LICENSE_KEY:
    console.print(Panel(
        "[red bold]🔒 Tyche Pro requires a license key.[/red bold]\n\n"
        "Get your key at: [bold cyan]ko-fi.com/s/4e0d922f4c[/bold cyan]\n\n"
        "Or use the free version: [dim]openclaw skills install tyche[/dim]",
        title="License Required", border_style="red"
    ))
    raise SystemExit(1)

INVOICES_FILE  = os.environ.get("INVOICES_FILE", "").strip()
YOUR_NAME      = os.environ.get("YOUR_NAME", "Your Company")
YOUR_EMAIL     = os.environ.get("YOUR_EMAIL", "")
YOUR_ADDRESS   = os.environ.get("YOUR_ADDRESS", "")
PAYMENT_TERMS  = os.environ.get("PAYMENT_TERMS", "Net 30")
CURRENCY       = os.environ.get("CURRENCY", "USD").upper()
PAYMENT_METHOD = os.environ.get("PAYMENT_METHOD", "")
try:
    TAX_RATE       = float(os.environ.get("TAX_RATE", "0"))
    LATE_FEE_RATE  = float(os.environ.get("LATE_FEE_RATE", "1.5"))
    REVENUE_GOAL   = float(os.environ.get("REVENUE_GOAL", "0"))
except ValueError:
    TAX_RATE = LATE_FEE_RATE = REVENUE_GOAL = 0.0

SYM = {"USD": "$", "EUR": "€", "GBP": "£", "CAD": "CA$", "AUD": "AU$"}.get(CURRENCY, "$")
now = datetime.now()

def fmt(amount, sym=None): return f"{sym or SYM}{amount:,.2f}"
def parse_amount(raw):
    c = re.sub(r"[^0-9.]", "", str(raw)) or "0"
    try: return float(c) if c.count(".") <= 1 else 0.0
    except: return 0.0
def parse_date(raw):
    for f in ("%Y-%m-%d","%m/%d/%Y","%d/%m/%Y","%d-%m-%Y"):
        try: return datetime.strptime(raw.strip(), f)
        except: pass
    return None

invoices = []
if INVOICES_FILE and os.path.exists(INVOICES_FILE):
    with open(INVOICES_FILE, encoding="utf-8", errors="replace") as f:
        reader = csv.DictReader(f)
        for i, row in enumerate(reader, 1):
            rk = {k.lower().strip(): v.strip() for k,v in row.items()}
            inv_cur  = rk.get("currency", CURRENCY).upper()
            inv_sym  = {"USD":"$","EUR":"€","GBP":"£","CAD":"CA$","AUD":"AU$"}.get(inv_cur, "$")
            invoices.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 rendered"),
                "amount":       parse_amount(rk.get("amount","0")),
                "due_date":     rk.get("due_date",""),
                "status":       rk.get("status","unpaid").lower(),
                "currency":     inv_cur,
                "sym":          inv_sym,
                "project_code": rk.get("project_code","").upper() or "GENERAL",
            })
else:
    console.print("[yellow]ℹ️  No INVOICES_FILE — using demo data.[/yellow]\n")
    invoices = [
        {"inv_number":"INV-0001","client_name":"Acme Corp","client_email":"billing@acme.com","description":"Website redesign","amount":2500,"due_date":(now-timedelta(days=15)).strftime("%Y-%m-%d"),"status":"overdue","currency":"USD","sym":"$","project_code":"WEB"},
        {"inv_number":"INV-0002","client_name":"Globex Inc","client_email":"ap@globex.com","description":"Consulting retainer","amount":1800,"due_date":(now+timedelta(days=10)).strftime("%Y-%m-%d"),"status":"unpaid","currency":"USD","sym":"$","project_code":"CONSULT"},
        {"inv_number":"INV-0003","client_name":"Initech Ltd","client_email":"pay@initech.com","description":"Logo design","amount":750,"due_date":(now-timedelta(days=5)).strftime("%Y-%m-%d"),"status":"paid","currency":"GBP","sym":"£","project_code":"DESIGN"},
        {"inv_number":"INV-0004","client_name":"Umbrella Co","client_email":"finance@umbrella.co","description":"SEO audit","amount":1200,"due_date":(now-timedelta(days=35)).strftime("%Y-%m-%d"),"status":"overdue","currency":"USD","sym":"$","project_code":"MARKETING"},
        {"inv_number":"INV-0005","client_name":"Soylent Corp","client_email":"ap@soylent.com","description":"App UX design","amount":3400,"due_date":(now+timedelta(days=20)).strftime("%Y-%m-%d"),"status":"unpaid","currency":"EUR","sym":"€","project_code":"DESIGN"},
        {"inv_number":"INV-0006","client_name":"Acme Corp","client_email":"billing@acme.com","description":"Monthly support","amount":500,"due_date":(now-timedelta(days=62)).strftime("%Y-%m-%d"),"status":"overdue","currency":"USD","sym":"$","project_code":"SUPPORT"},
    ]

# Enrich
for inv in invoices:
    sub = inv["amount"]
    tax = sub * TAX_RATE / 100
    inv["subtotal"] = sub
    inv["tax"]      = tax
    inv["total"]    = sub + tax
    due = parse_date(inv["due_date"])
    inv["due_dt"]    = due
    inv["days_late"] = max(0, (now - due).days) if due and inv["status"] in ("overdue","unpaid") else 0
    inv["late_fee"]  = inv["total"] * LATE_FEE_RATE / 100 * (inv["days_late"] / 30) if inv["days_late"] > 0 and LATE_FEE_RATE else 0

# Financials (USD-equivalent totals for summary)
total_invoiced    = sum(i["total"] for i in invoices)
total_paid        = sum(i["total"] for i in invoices if i["status"] == "paid")
total_outstanding = total_invoiced - total_paid
overdue_invoices  = [i for i in invoices if i["days_late"] > 0]
total_late_fees   = sum(i["late_fee"] for i in invoices)

# Header
console.print()
console.print(Panel.fit(
    f"[bold yellow]⚖️💎 Tyche Pro — Revenue Dashboard[/bold yellow]\n"
    f"Invoiced: [white]{fmt(total_invoiced)}[/white]  Received: [green]{fmt(total_paid)}[/green]  "
    f"Outstanding: [red]{fmt(total_outstanding)}[/red]  Late fees: [orange3]{fmt(total_late_fees)}[/orange3]",
    border_style="yellow"
))

# Revenue goal progress
if REVENUE_GOAL > 0:
    pct = min(total_paid / REVENUE_GOAL * 100, 100)
    bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
    console.print(Panel(
        f"[cyan]{bar}[/cyan]  [yellow]{pct:.1f}%[/yellow]  {fmt(total_paid)} / {fmt(REVENUE_GOAL)} goal",
        title="Revenue Goal", border_style="green"
    ))

# Status table
console.print()
tbl = Table(title="Invoice Status", box=box.ROUNDED, border_style="yellow")
tbl.add_column("Inv #",    width=10, style="dim")
tbl.add_column("Project",  width=10, style="magenta")
tbl.add_column("Client",   width=16, style="cyan")
tbl.add_column("Cur",      width=5)
tbl.add_column("Total",    width=12, justify="right")
tbl.add_column("Due",      width=12, style="dim")
tbl.add_column("Late",     width=8,  justify="right", style="red")
tbl.add_column("Fee",      width=10, justify="right", style="orange3")
tbl.add_column("Status",   width=10)

SC = {"paid":"green","unpaid":"yellow","overdue":"red","partial":"cyan"}
for inv in sorted(invoices, key=lambda x: -(x["days_late"] or 0)):
    sc   = SC.get(inv["status"],"white")
    late = f"{inv['days_late']}d" if inv["days_late"] else "—"
    fee  = fmt(inv["late_fee"], inv["sym"]) if inv["late_fee"] else "—"
    tbl.add_row(
        inv["inv_number"], inv["project_code"], inv["client_name"][:14],
        inv["currency"], fmt(inv["total"], inv["sym"]), inv["due_date"],
        late, fee, f"[{sc}]{inv['status'].title()}[/{sc}]"
    )
console.print(tbl)

# Pro: Aged receivables (30/60/90+ day buckets)
console.print()
buckets = {"Current (0-30d)": [], "31-60 days": [], "61-90 days": [], "90+ days": []}
for inv in overdue_invoices:
    d = inv["days_late"]
    if d <= 30:   buckets["Current (0-30d)"].append(inv)
    elif d <= 60: buckets["31-60 days"].append(inv)
    elif d <= 90: buckets["61-90 days"].append(inv)
    else:         buckets["90+ days"].append(inv)

aged_tbl = Table(title="Aged Receivables", box=box.SIMPLE, border_style="red")
aged_tbl.add_column("Bucket",  style="cyan", width=18)
aged_tbl.add_column("Count",   width=8, justify="right")
aged_tbl.add_column("Amount",  width=14, justify="right", style="red")
for bucket, invs in buckets.items():
    total = sum(i["total"] for i in invs)
    aged_tbl.add_row(bucket, str(len(invs)), fmt(total) if invs else "—")
console.print(aged_tbl)

# Pro: Project grouping
console.print()
proj_totals = defaultdict(lambda: {"invoiced":0,"paid":0,"outstanding":0,"count":0})
for inv in invoices:
    p = inv["project_code"]
    proj_totals[p]["invoiced"]     += inv["total"]
    proj_totals[p]["count"]        += 1
    if inv["status"] == "paid":
        proj_totals[p]["paid"]     += inv["total"]
    else:
        proj_totals[p]["outstanding"] += inv["total"]

proj_tbl = Table(title="Revenue by Project", box=box.SIMPLE, border_style="magenta")
proj_tbl.add_column("Project",     style="magenta", width=14)
proj_tbl.add_column("Invoices",    width=10, justify="right")
proj_tbl.add_column("Invoiced",    width=14, justify="right")
proj_tbl.add_column("Paid",        width=14, justify="right", style="green")
proj_tbl.add_column("Outstanding", width=14, justify="right", style="red")
for proj, data in sorted(proj_totals.items(), key=lambda x: -x[1]["invoiced"]):
    proj_tbl.add_row(proj, str(data["count"]), fmt(data["invoiced"]),
                     fmt(data["paid"]), fmt(data["outstanding"]))
console.print(proj_tbl)

# Reminder emails
overdue_remind = [i for i in invoices if i["days_late"] > 0]
if overdue_remind:
    console.print()
    for inv in overdue_remind:
        late_line = f" A late fee of {fmt(inv['late_fee'], inv['sym'])} has accrued." if inv["late_fee"] else ""
        if inv["days_late"] <= 7:
            tier, colour = "Friendly Reminder", "yellow"
            body = f"Invoice {inv['inv_number']} for {fmt(inv['total'], inv['sym'])} was due {inv['due_date']}.{late_line} If you've sent payment, please disregard."
        elif inv["days_late"] <= 21:
            tier, colour = "Firm Reminder", "orange3"
            body = f"Invoice {inv['inv_number']} for {fmt(inv['total'], inv['sym'])} is now {inv['days_late']} days overdue.{late_line} Please arrange payment or contact me to discuss."
        else:
            tier, colour = "FINAL NOTICE", "red"
            body = f"FINAL NOTICE: Invoice {inv['inv_number']} for {fmt(inv['total'], inv['sym'])} is {inv['days_late']} days overdue.{late_line} Please remit within 48 hours or contact me immediately."
        console.print(Panel(
            f"To: {inv['client_email']}\nSubject: {tier} — {inv['inv_number']} — {inv['client_name']}\n\nDear {inv['client_name']},\n\n{body}\n\nKind regards,\n{YOUR_NAME}",
            title=f"[{colour}]{tier}[/{colour}] — {inv['client_name']} ({inv['days_late']}d overdue)",
            border_style=colour
        ))

# Save outputs
date_str    = now.strftime("%Y-%m-%d")
report_file = f"tyche_pro_report_{date_str}.md"
csv_file    = f"tyche_pro_analytics_{date_str}.csv"

with open(report_file, "w", encoding="utf-8") as f:
    f.write(f"# ⚖️💎 Tyche Pro Report — {date_str}\n\n")
    f.write(f"**Invoiced:** {fmt(total_invoiced)}  **Received:** {fmt(total_paid)}  **Outstanding:** {fmt(total_outstanding)}\n\n")
    f.write("## Invoice Status\n\n| Inv # | Project | Client | Total | Due | Days Late | Status |\n|---|---|---|---|---|---|---|\n")
    for inv in sorted(invoices, key=lambda x: -(x["days_late"] or 0)):
        f.write(f"| {inv['inv_number']} | {inv['project_code']} | {inv['client_name']} | {fmt(inv['total'],inv['sym'])} | {inv['due_date']} | {inv['days_late'] or '—'} | {inv['status'].title()} |\n")

with open(csv_file, "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["inv_number","client","project","currency","total","days_late","late_fee","status"])
    for inv in invoices:
        writer.writerow([inv["inv_number"],inv["client_name"],inv["project_code"],inv["currency"],
                         f"{inv['total']:.2f}",inv["days_late"],f"{inv['late_fee']:.2f}",inv["status"]])

console.print()
console.print(Panel(
    f"[green]✅ Done![/green]  [cyan]{report_file}[/cyan]  |  [cyan]{csv_file}[/cyan]",
    border_style="green"
))
建议反馈