پرش به محتوای اصلی

خودکارسازی دفترداری با bea

دفترهای Beancount خود را با bea اسکریپتنویسی کنید: دفتر کل را بهطور صریح مشخص کنید، پاکت JSON را با jq تجزیه کنید، بر اساس کدهای خروجی branching انجام دهید، بدون نظارت اجرا کنید و یک بررسی شبانه زمانبندی کنید.

یک اسکریپت چهار تصمیم را برای bea می‌گیرد: کدام دفترچه را می‌خواند، --json برای خروجی قابل خواندن توسط ماشین، jq برای مقدار مورد نیاز، و کد خروجی که بر اساس آن شاخه‌بندی می‌کند. این راهنما این چهار تصمیم را از ابتدا تا انتها بررسی می‌کند و سپس آن‌ها را برنامه‌ریزی می‌کند.

شما به bea روی ماشینی که کار را اجرا می‌کند و یک دفترچه که قابل دسترسی باشد نیاز دارید. اگر کتاب‌‌های جدید شروع می‌کنید، ابتدا راه‌اندازی سریع CLI را دنبال کنید. هر اطلاعات درباره پرچم‌ها، کلیدهای پاکت و کدهای خروجی در مرجع CLI بی‌کانت جستجو می‌شود و اینجا تکرار نمی‌شود.

انتخاب دفترچه به‌طور صریح​

نام فایل را مشخص کنید. یک فرمان محلی هدف خود را ابتدا از --file، سپس $BEA_FILE، سپس ./main.bean در دایرکتوری کاری حل می‌کند و یک کار زمان‌بندی شده به ندرت جایی اجرا می‌شود که فکر می‌کنید.

bea --file ~/books/main.bean check
BEA_FILE=~/books/main.bean bea check
cd ~/books && bea check

گزینه‌های سراسری قبل از فرمان می‌آیند، مانند bea --file main.bean check. اگر فایل حل‌شده وجود نداشته باشد، فرمان با 2 خارج می‌شود و هر سه منبع را نام می‌برد، بنابراین اشتباه تایپی در ورودی کرون با صدای بلند ناکام می‌شود به جای اینکه کتاب‌های اشتباه را تأیید کند. هدف‌گذاری میزبانی شده از طریق پرچم --ledger هنوز وجود ندارد؛ bea هرگز به طور ضمنی فایلی محلی را آپلود نمی‌کند.

خواندن پاکت JSON​

--json سراسری را اضافه کنید و هر فرمان پشتیبانی شده با همان پاکت پاسخ می‌دهد: bea، target، data، truncated و limit روی فهرست‌های محدود شده. مقادیر رشته‌های اعشاری هستند و تاریخ‌ها ISO YYYY-MM-DD است، بنابراین مقدار بدون دخالت هیچ عدد ممیز شناوری قابل مقایسه مطمئن است. کلیدهای پاکت در مرجع JSON و کدهای خروجی جدولی شده‌اند.

bea --json --file main.bean report income-statement | jq .data.net_profit
bea --json --file main.bean list transaction --limit 2 | jq '.data[0].postings[0].units'
bea --json --file main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee \
  --account Assets:Checking --apply --duplicates skip | jq '.data | {written, ready, duplicates}'

آن سه فرمان — report، list و import — شکل نتایج خود را حفظ می‌کنند، بنابراین مسیر jq نوشته شده در مقابل آن‌ها معتبر باقی می‌ماند. bea --json check و bea --json query نیز امروز پاکت را صادر می‌کنند، اما آن‌ها فرمان‌هایی هستند که به اجرایی‌های بومی بی‌کانت داده می‌شوند، بنابراین یک اسکریپت باید روی وضعیت خروجی check به جای شکل خروجی آن کلیدگذاری کند. سیاست تکراری خود را آگاهانه انتخاب کنید: --duplicates زمانی که یک واردات نیاز به تصمیم دارد هنوز لازم است، همان‌طور که راهنمای واردات توضیح می‌دهد.

توقف روی کد خروجی مناسب​

بر اساس وضعیت شاخه‌بندی کنید و شیء خطا را قبل از تلاش مجدد برای نوشتن بخوانید. در حالت --json یک شکست هیچ چیزی به stdout نمی‌نویسد و دقیقاً یک شیء به stderr می‌فرستد که error.category آن کلاس را نام‌گذاری می‌کند: validation (1)، usage (2)، auth (3)، conflict (4).

#!/usr/bin/env bash
set -euo pipefail
 
out=$(mktemp)
err=$(mktemp)
status=0
 
bea --json --file main.bean report income-statement >"$out" 2>"$err" || status=$?
 
case "$status" in
  0) jq -r '.data.net_profit | to_entries[] | "net profit: \(.value) \(.key)"' "$out" ;;
  4) echo "conflict — inspect the ledger before retrying" >&2
     jq -r '.error.message' "$err" >&2
     exit 4 ;;
  *) jq -r '.error | "\(.category) (exit \(.exit_code)): \(.message)"' "$err" >&2
     exit "$status" ;;
esac

خروجی 4 همان چیزی است که یک اسکریپت هرگز نباید کورکورانه دوباره امتحان کند: این به معنای بروز تعارض یا ناشناخته بودن نتیجه است، مانند ویرایش خارجی که در میانه نوشتن می‌رسد یا هدفی از نوع init که قبلاً وجود دارد. دفتر کل را بررسی کنید و سپس از یک خواندن تازه دوباره تلاش کنید. خروجی 1 شکست‌های اعتبارسنجی و هر خطای زمان اجرا دیگری را پوشش می‌دهد؛ error.details خطاهای جداگانه دفتر کل را حمل می‌کند و error.result واکنش‌های نوشتن جزئی را نشان می‌دهد. خروجی غیر صفر هرگز تضمین نمی‌کند که چیزی تغییر نکرده باشد.

اجرا بدون ترمینال​

bea به تنهایی متوقف می‌شود و درخواست نمی‌کند. --no-input همیشه زمانی صادر می‌شود که stdin ترمینال نباشد، وقتی --json تنظیم شده باشد و وقتی CI درست باشد — 1، true، yes یا on. در این حالت، نبود تأیید با خروج 2 شکست می‌خورد به جای اینکه برای همیشه منتظر بماند.

CI=true BEA_NO_UPDATE_NOTIFIER=1 bea --json --file main.bean report balance-sheet
bea --json --file main.bean list transaction --limit 100 --sort oldest

خواندن‌ها در ترمینال نرمگیرانه هستند و در هر جای دیگر سختگیرانه. وقتی دفتر کل خطاهای بارگذاری دارد، query، list و report با 1 زیر --json، زیر stdout پایپ شده، زیر CI درست، یا با --strict خروجی می‌دهند؛ گزینه --allow-errors دستور را ارسال کنید تا جواب جزئی را بپذیرد، که همچنین ledger_valid: false را تنظیم می‌کند و ledger_errors را در JSON پر می‌کند. --strict عکس آن است: حتی در ترمینال جواب جزئی را نمی‌پذیرد، که برای وقتی که یک انسان همان اسکریپت را دستی اجرا می‌کند مطلوب است. bea check --allow-errors ندارد — گزارش خطا وظیفه کامل آن است — و همیشه با یافتن خطا 1 خروج می‌دهد. BEA_NO_UPDATE_NOTIFIER=1 را برای بی‌صدا کردن اعلامیه به‌روزرسانی غیرفعال تنظیم کنید؛ CI درست از قبل همین کار را می‌کند.

زمان‌بندی بررسی​

هر شب اعتبارسنجی را اجرا کنید و اجازه دهید کد خروج هشدار باشد. هر دو بلوک زیر قالب هستند — مسیرها، زمان‌بندی و اجرای برنامه متعلق به شماست.

# crontab -e — 07:15 daily; cron mails you only when bea exits nonzero
15 7 * * * BEA_NO_UPDATE_NOTIFIER=1 /opt/homebrew/bin/bea --file /home/alice/books/main.bean check
name: ledger
on:
  schedule:
    - cron: "15 7 * * *"
  push:
jobs:
  check:
    runs-on: ubuntu-latest
    env:
      BEA_NO_UPDATE_NOTIFIER: "1"
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v5
      - run: uv tool install beancount-io==0.2.0
      - run: bea --file main.bean check
      - run: bea --json --file main.bean report balance-sheet > balance-sheet.json

اولین فرمان حمایت‌شده توسط موتور، موتور مدیریت‌شده را دانلود می‌کند، پس اجرای برنامه نیاز به دسترسی به شبکه دارد. برای استفاده مجدد از آن در چند وظیفه، ~/.local/share/bea/engine را با کلیدی حاوی سیستم عامل اجرا کننده، معماری، نسخه پایتون و نسخه پین‌شده bea کش کنید. وقتی که کار باید قابل تولید مجدد باشد نسخه را پین کنید، و وقتی ترجیح می‌دهید نسخه‌های منتشرشده را دنبال کنید، پین را بردارید. CI در GitHub Actions از قبل درست است، بنابراین درخواست‌ها خاموش و اعلان به‌روزرسانی ساکت است قبل از تنظیم هر چیزی. مرحله قالب‌بندی عمداً اینجا نیست. یک کار زمان‌بندی شده نباید فایل‌هایی که نیازی به بازنویسی ندارند را دوباره بنویسد، پس در قلاب پیش‌تعهد از bea format main.bean --check استفاده کنید که چیزی لمس نمی‌کند و وقتی فایلی نیاز به قالب‌بندی دارد 1 خروج می‌دهد.

استفاده از مدرک میزبانی شده در یک کار​

تنظیم BEA_TOKEN، از فروشگاه رمز مخفی ارائه‌دهنده CI خود، و رد کردن ورود مرورگر به طور کامل. توکن از محیط خوانده می‌شود و هرگز روی دیسک نوشته نمی‌شود، بنابراین هیچ چیزی در دایرکتوری خانه رانر برای یافتن توسط کار بعدی قرار نمی‌گیرد.

export BEA_TOKEN="$YOUR_CI_SECRET"
bea --json cloud status

خروجی 0 به این معنی است که اعتبارنامه حل شده و پاکت نام حسابی را که به آن تعلق دارد نشان می‌دهد؛ خروجی 3 با error.category از auth یعنی این که حل نشده است، و پیام تفاوت بین اعتبارنامه تنظیم نشده و رد شده را نشان می‌دهد. bea cloud logout هیچ کاری روی توکنی که به این شکل ارائه شده انجام نمی‌دهد — نه آن را باطل می‌کند و نه تنظیم آن را حذف می‌کند، زیرا ممکن است کار دیگری آن را به اشتراک بگذارد — بنابراین توکن نشت کرده را از داشبورد باطل کنید. دستورهای محلی اصلاً به اعتبارنامه نیاز ندارند؛ تنها bea cloud و bea ask به سرویس میزبانی شده متصل می‌شوند. فهرست کامل متغیرها در مراجعه تنظیمات موجود است.

همه پاسخ‌ها به شکل JSON نیستند. bea ask حالت JSON را کاملاً رد می‌کند، bea cloud login به انسان نیاز دارد، و bea cloud logout یا bea cloud ledger clone موفق هیچ شیء موفقیت JSON‌ای برنمی‌گردانند — به جای آن وضعیت خروج آن‌ها را بخوانید. خروجی راهنما، نسخه و تکمیل پوسته متنی باقی می‌ماند.

مراحل بعدی​

منبع: https://beancount.io/fa/docs/Solutions/automate-bookkeeping-with-bea