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

چگونه اسکریپت‌های پایتون Beancount و Fava را خودکار می‌کنند

Beancount و Fava قابل اسکریپت‌نویسی باقی می‌مانند: از پایتون برای خودکارسازی گزارش‌ها، موجودی‌ها و گردش کارهای سفارشی در برابر دفتر کل خود استفاده کنید.

Beancount (یک ابزار حسابداری دوطرفه متن‌ساده) و Fava (رابط وب آن) بسیار قابل‌توسعه و قابل‌اسکریپت هستند. طراحی آن‌ها به شما اجازه می‌دهد با نوشتن اسکریپت‌های پایتون، کارهای مالی را خودکار کنید، گزارش‌های سفارشی بسازید و هشدارها را تنظیم کنید. به گفته یکی از کاربران، «من واقعاً دوست دارم داده‌هایم را در چنین قالب مناسبی داشته باشم، و دوست دارم که بتوانم کارها را به دلخواه خودم خودکار کنم. هیچ API‌ای مثل یک فایل روی دیسک شما نیست؛ یکپارچه‌سازی با آن آسان است.» این راهنما به ساخت گردش‌کارهای قابل‌اسکریپت می‌پردازد — از خودکارسازی مناسب برای مبتدیان تا افزونه‌های پیشرفته Fava.

یک نمونه دفترکل زنده را کاوش کنید:

باز کردن Example Ledger در برگه جدید

با خط فرمان bea شروع کنید​

پیش از نوشتن هر کد پایتونی، بررسی کنید که آیا bea از پیش کار را انجام می‌دهد. این ابزار دفترکل را اعتبارسنجی می‌کند، پرس‌وجوهای BQL را اجرا می‌کند، چهار گزارش مالی را تولید می‌کند و خروجی‌های بانکی را وارد می‌کند، و سوئیچ سراسری --json هر یک از این‌ها را به یک پاکت قابل‌تجزیه تبدیل می‌کند که پوسته شما می‌تواند آن را به jq بفرستد. کدهای خروجی آن همان قراردادی است که یک کار زمان‌بندی‌شده بر اساس آن شاخه می‌زند، بنابراین cron یا CI به هیچ اسکریپت بارگذار نیازی ندارد. برای اطلاع از نحوه تشخیص هدف، پاکت و شاخه‌بندی بر اساس کد خروج، خودکارسازی دفترداری با bea را ببینید و زمانی که به محاسبه‌ای سفارشی نیاز داشتید که CLI آن را ارائه نمی‌دهد، به اینجا بازگردید.

شروع کار: اجرای Beancount به عنوان اسکریپت پایتون​

برای اسکریپت‌های پایتون سفارشی زیر، کتابخانه‌های اسکریپت‌نویسی را نصب کنید (pip install beancount beanquery beangulp). گردش‌کارهای فرمان bea از موتور مدیریت‌شده استفاده می‌کنند؛ برای نصب آن شروع سریع CLI را دنبال کنید. از آنجا که Beancount با پایتون نوشته شده است، می‌توانید از آن به عنوان یک کتابخانه در اسکریپت‌های خود استفاده کنید. اسکریپت‌های زیر با Beancount 3.2.3، beanquery 0.2.0 و beangulp 0.2.0 اجرا شده‌اند. رویکرد کلی چنین است:

  • دفترکل Beancount خود را بارگذاری کنید: از بارگذار Beancount برای تجزیه فایل .beancount به اشیاء پایتون استفاده کنید. برای مثال:

    from beancount import loader
    entries, errors, options = loader.load_file("myledger.beancount")
    if errors:
        for error in errors:
            print(error)
        raise SystemExit(1)

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

  • از زبان پرس‌وجوی Beancount (BQL) بهره ببرید: به جای پیمایش دستی، می‌توانید پرس‌وجوهای شبه‌SQL را روی داده‌ها اجرا کنید. پرس‌وجوها در بسته جداگانه beanquery قرار دارند. هیچ ماژول beancount.query در Beancount 3.2.3 وجود ندارد. برای مثال، برای دریافت کل هزینه‌ها به تفکیک ماه، ورودی‌های بارگذاری‌شده را متصل کنید و پرس‌وجو را مستقیماً اجرا کنید:

    import beanquery
     
    conn = beanquery.connect("beancount:", entries=entries, errors=errors, options=options)
    cur = conn.execute(
        "SELECT year, month, sum(position) WHERE account ~ 'Expenses' GROUP BY year, month"
    )
    for row in cur.fetchall():
        print(row)

    این از beanquery برای تجمیع داده‌ها استفاده می‌کند. همان موتوری است که پشت bea query قرار دارد، اما در اینجا آن را درون یک اسکریپت فراخوانی می‌کنید. این کار از فراخوانی مکرر یک فرمان خارجی در یک حلقه جلوگیری می‌کند.

  • یک ساختار پروژه تنظیم کنید: اسکریپت‌های خود را در کنار دفترکل سازمان‌دهی کنید. یک چیدمان رایج این است که دایرکتوری‌هایی برای واردکننده‌ها (برای واکشی/تجزیه داده‌های خارجی)، گزارش‌ها یا پرس‌وجوها (برای اسکریپت‌های تحلیل) و اسناد (برای ذخیره صورت‌حساب‌های دانلودشده) داشته باشید. برای مثال، یکی از کاربران چنین نگه می‌دارد:

    • importers/ – اسکریپت‌های واردکننده پایتون سفارشی (همراه با تست‌ها)،
    • queries/ – اسکریپت‌هایی برای تولید گزارش‌ها (قابل اجرا با python3 queries/...)،
    • documents/ – فایل‌های CSV/PDF دانلودشده بانک که بر اساس حساب سازمان‌دهی شده‌اند.

با این چیدمان، می‌توانید اسکریپت‌ها را به صورت دستی اجرا کنید (مثلاً python3 queries/cash_flow.py) یا آن‌ها را زمان‌بندی کنید (از طریق cron یا یک اجراکننده وظیفه) تا گردش‌کار خود را خودکار کنید.

خودکارسازی وظایف تطبیق​

تطبیق به معنای اطمینان از مطابقت دفترکل شما با سوابق خارجی (صورت‌حساب‌های بانکی، گزارش‌های کارت اعتباری و غیره) است. دفترکل متن‌ساده و API پایتون Beancount امکان خودکارسازی بخش بزرگی از این فرآیند را فراهم می‌کنند.

وارد کردن و تطبیق تراکنش‌ها (مبتدی)​

برای مبتدیان، رویکرد توصیه‌شده استفاده از واردکننده‌های بسته جداگانه beangulp است. Beancount 3 ماژول ورود v2 و فرمان استخراج آن را حذف کرد. شما یک کلاس کوچک پایتون می‌نویسید که از beangulp.Importer ارث‌بری می‌کند تا یک قالب معین (CSV، OFX، PDF و غیره) را تجزیه کرده و تراکنش‌ها را تولید کند. آن را در یک اسکریپت ورود کوتاه ثبت کنید، سپس آن را از طریق bea ingest در موتور مدیریت‌شده اجرا کنید:

  • یک واردکننده بنویسید (یک کلاس پایتون با متدهای identify()، account() و extract()) برای قالب CSV بانک خود.
  • یک اسکریپت ورود اضافه کنید که واردکننده‌های شما را ثبت می‌کند. bea ingest فرمان‌های identify، extract و archive این اسکریپت را اجرا می‌کند. برای مثال، یک گردش‌کار extract را روی همه فایل‌های موجود در ~/Downloads اجرا می‌کند و تراکنش‌ها را به یک فایل موقت می‌نویسد.
  • به صورت دستی تراکنش‌ها را از فایل موقت بازبینی و در دفترکل اصلی خود کپی کنید، سپس bea check را اجرا کنید تا اطمینان یابید مانده‌ها تطبیق می‌شوند.

یک مثال حداقلی: یک statement.csv با ستون‌های date,description,amount که این واردکننده (checking_importer.py) آن را تجزیه می‌کند:

import csv
import datetime
from beancount.core import data
from beancount.core.amount import Amount
from beancount.core.number import D
import beangulp
 
 
class CheckingImporter(beangulp.Importer):
    def identify(self, filepath: str) -> bool:
        return filepath.endswith("statement.csv")
 
    def account(self, filepath: str) -> str:
        return "Assets:Bank:Checking"
 
    def extract(self, filepath: str, existing):
        entries = []
        with open(filepath, newline="") as f:
            for row in csv.DictReader(f):
                date = datetime.date.fromisoformat(row["date"])
                amount = Amount(D(row["amount"]), "USD")
                meta = data.new_metadata(filepath, 0)
                entries.append(
                    data.Transaction(
                        meta, date, "*", None, row["description"],
                        data.EMPTY_SET, data.EMPTY_SET, [
                            data.Posting("Expenses:Food:Groceries", amount,
                                         None, None, None, None),
                            data.Posting("Assets:Bank:Checking",
                                         Amount(-amount.number, "USD"),
                                         None, None, None, None),
                        ]))
        return entries

اسکریپت ورود (ingest.py) آن را به هم متصل می‌کند:

from checking_importer import CheckingImporter
from beangulp import Ingest
 
ingest = Ingest([CheckingImporter()])
 
if __name__ == "__main__":
    ingest()

آن را روی یک فایل دانلودشده اجرا کنید. برای یک CSV محلی هیچ اعتبارنامه‌ای لازم نیست. ابتدا کتابخانه سیستمی libmagic را نصب کنید. فرمان فعال‌سازی یک‌باره، Beangulp را در موتور مدیریت‌شده دانلود می‌کند:

bea engine enable beangulp
bea ingest identify --config ingest.py statement.csv
bea ingest extract --config ingest.py statement.csv -o new.beancount

identify برای فایل، checking_importer.CheckingImporter را گزارش می‌دهد. extract تراکنش‌ها را در قالب Beancount می‌نویسد:

2024-01-08 * "Grocery Store"
  Expenses:Food:Groceries   120.00 USD
  Assets:Bank:Checking     -120.00 USD

new.beancount را بازبینی کنید، ورودی‌ها را در دفترکل اصلی خود کپی کنید و bea check را اجرا کنید.

برای یک کار یک‌باره، از واردکننده صرف‌نظر کنید

برای تبدیل یک صورت‌حساب واحد نیازی به نوشتن واردکننده ندارید. فایل را در مبدل CSV به Beancount بچسبانید، یا برای دانلودهای .ofx، .qfx و .qif از OFX و QIF به Beancount استفاده کنید. هر دو کاملاً در مرورگر شما اجرا می‌شوند، بنابراین صورت‌حساب هرگز از دستگاه شما خارج نمی‌شود.

اگرچه این فرآیند هنوز شامل یک مرحله بازبینی است، بخش بزرگی از کار طاقت‌فرسای تجزیه و قالب‌بندی ورودی‌ها خودکار می‌شود. اسکریپت‌های واردکننده همچنین می‌توانند به طور خودکار دسته‌بندی اختصاص دهند و حتی اظهارنامه‌های مانده (بیانیه‌های مانده‌های مورد انتظار) را تنظیم کنند تا اختلاف‌ها را بگیرند. برای مثال، پس از وارد کردن، ممکن است خطی مانند 2025-04-30 balance Assets:Bank:Checking 1234.56 USD داشته باشید که مانده پایانی را اظهار می‌کند. وقتی bea check را اجرا می‌کنید، Beancount تأیید می‌کند که همه این اظهارنامه‌های مانده درست هستند، و اگر تراکنشی گم شده یا تکراری باشد هر خطایی را علامت‌گذاری می‌کند. این یک روش مطلوب است: برای هر دوره صورت‌حساب، اظهارنامه‌های مانده را خودکار تولید کنید تا کامپیوتر اختلاف‌های تطبیق‌نشده را برای شما تشخیص دهد.

اسکریپت‌های تطبیق سفارشی (متوسط)​

برای کنترل بیشتر، می‌توانید یک اسکریپت پایتون سفارشی بنویسید تا فهرست تراکنش‌های یک بانک (CSV یا از طریق API) را با ورودی‌های دفترکل خود مقایسه کنید:

  1. داده خارجی را بخوانید: فایل CSV بانک را با استفاده از ماژول csv پایتون (یا Pandas) تجزیه کنید. داده‌ها را به فهرستی از تراکنش‌ها نرمال کنید، مثلاً هرکدام با یک تاریخ، مبلغ و توضیح.
  2. تراکنش‌های دفترکل را بارگذاری کنید: از loader.load_file همان‌طور که پیش‌تر نشان داده شد استفاده کنید تا همه ورودی‌های دفترکل را بگیرید. این فهرست را به حساب مورد نظر (مثلاً حساب جاری شما) و شاید بازه تاریخ صورت‌حساب محدود کنید.
  3. مقایسه کنید و عدم تطابق‌ها را بیابید:
  • برای هر تراکنش خارجی، بررسی کنید که آیا ورودی یکسانی در دفترکل وجود دارد (تطبیق بر اساس تاریخ و مبلغ، شاید توضیح). اگر یافت نشد، آن را به عنوان «جدید» علامت بزنید و احتمالاً به صورت یک تراکنش قالب‌بندی‌شده Beancount برای بازبینی شما خروجی دهید.
  • برعکس، هر ورودی دفترکل در آن حساب را که در منبع خارجی ظاهر نمی‌شود شناسایی کنید – این‌ها می‌توانند خطاهای ورود داده یا تراکنش‌هایی باشند که هنوز از بانک تسویه نشده‌اند.
  1. نتایج را خروجی دهید: یک گزارش چاپ کنید یا یک قطعه .beancount جدید با تراکنش‌های گمشده بسازید.

به عنوان مثال، یک اسکریپت جامعه‌ای به نام reconcile.py دقیقاً همین کار را می‌کند: با گرفتن یک فایل Beancount و یک CSV ورودی، فهرستی از تراکنش‌های جدیدی که باید وارد شوند را چاپ می‌کند، و همچنین هر ثبت دفترکل موجودی که در ورودی نیست (که بالقوه نشانه طبقه‌بندی نادرست است). با چنین اسکریپتی، تطبیق ماهانه می‌تواند به همین سادگی باشد که آن را اجرا کنید و سپس تراکنش‌های پیشنهادی را به دفترکل خود بیفزایید. یکی از کاربران Beancount اشاره می‌کند که آن‌ها «هر ماه روی همه حساب‌ها یک فرآیند تطبیق انجام می‌دهند» و از مجموعه‌ای رو به رشد از کد پایتون برای حذف بخش زیادی از کار دستی در وارد کردن و تطبیق داده‌ها استفاده می‌کنند.

نکته: در طول تطبیق، برای دقت از ابزارهای Beancount بهره ببرید:

  • از اظهارنامه‌های مانده همان‌طور که اشاره شد استفاده کنید تا بررسی‌های خودکار روی مانده‌های حساب داشته باشید.
  • در صورت تمایل از دستور pad استفاده کنید، که می‌تواند برای اختلاف‌های جزئی گرد کردن به طور خودکار ورودی‌های متوازن‌کننده درج کند (با احتیاط استفاده کنید).
  • برای واردکننده یا منطق تطبیق خود تست واحد بنویسید (Beancount کمک‌تابع‌های تست ارائه می‌دهد). برای مثال، یک گردش‌کار شامل گرفتن یک CSV نمونه، نوشتن تست‌های شکست‌خورده با تراکنش‌های مورد انتظار، و سپس پیاده‌سازی واردکننده تا زمانی که همه تست‌ها پاس شوند بود. این تضمین می‌کند که اسکریپت واردسازی شما برای موارد مختلف به درستی کار می‌کند.

تولید گزارش‌ها و خلاصه‌های سفارشی​

در حالی که Fava بسیاری از گزارش‌های استاندارد (صورت سود و زیان، ترازنامه و غیره) را ارائه می‌دهد، می‌توانید با اسکریپت‌ها گزارش‌های سفارشی بسازید. این‌ها می‌توانند از خروجی‌های ساده کنسول تا فایل‌ها یا نمودارهای قالب‌بندی‌شده غنی متغیر باشند.

پرس‌وجوی داده‌ها برای گزارش‌ها (مبتدی)​

در سطح پایه، می‌توانید از زبان پرس‌وجوی Beancount (BQL) برای گرفتن داده خلاصه و چاپ یا ذخیره آن استفاده کنید. برای مثال:

  • خلاصه جریان نقدی: از یک پرس‌وجو برای محاسبه جریان نقدی خالص استفاده کنید. «جریان نقدی» می‌تواند به عنوان تغییر در مانده حساب‌های معین در یک دوره تعریف شود. با استفاده از BQL، ممکن است چنین انجام دهید:

    SELECT year, month, sum(position)
    WHERE account ~ 'Income' OR account ~ 'Expenses'
    GROUP BY year, month

    این همه ثبت‌های درآمد و هزینه را به تفکیک ماه خالص می‌کند. با ~ و یک عبارت باقاعده فیلتر کنید: LIKE در beanquery 0.2.0 یک خطای نحوی است. ثبت‌ها position دارند، نه amount. هر سطر یک Inventory را نگه می‌دارد، بنابراین هر ارز به جای تبدیل شدن، جداگانه فهرست می‌شود. درآمد منفی و هزینه‌ها مثبت وارد می‌شوند. می‌توانید این را از طریق bea query یا از طریق API پایتون beanquery که پیش‌تر نشان داده شد اجرا کنید و سپس نتیجه را قالب‌بندی کنید.

  • گزارش هزینه دسته‌بندی: مجموع هزینه‌ها به تفکیک دسته را پرس‌وجو کنید:

    SELECT account, sum(position)
    WHERE account ~ 'Expenses'
    GROUP BY account
    ORDER BY sum(position) ASC

    این جدولی از هزینه‌ها به تفکیک دسته به دست می‌دهد. هر مجموع یک Inventory در ارز اصلی خود است. تجمیع را در round() نپیچید: تابع round(inventory, int) وجود ندارد، بنابراین round(sum(position), 2) کامپایل نمی‌شود. می‌توانید چندین پرس‌وجو را در یک اسکریپت اجرا کنید و نتایج را به صورت متن، CSV یا حتی JSON برای پردازش بیشتر خروجی دهید.

یکی از کاربران «بی‌اهمیت» یافت که داده‌های مالی را با Fava یا با اسکریپت‌ها تحلیل کند، و اشاره کرد که آن‌ها از یک اسکریپت پایتون برای بیرون کشیدن داده از Beancount از طریق زبان پرس‌وجو و سپس قرار دادن آن در یک DataFrame پانداس برای تهیه یک گزارش سفارشی استفاده می‌کنند. برای مثال، می‌توانید مجموع‌های ماهانه را با یک پرس‌وجو واکشی کنید و سپس از Pandas/Matplotlib برای رسم نمودار جریان نقدی در طول زمان استفاده کنید. ترکیب BQL و کتابخانه‌های علم داده به شما امکان می‌دهد گزارش‌هایی فراتر از آنچه Fava به طور پیش‌فرض ارائه می‌دهد بسازید.

گزارش‌دهی پیشرفته (نمودارها، عملکرد و غیره)​

برای نیازهای پیشرفته‌تر، اسکریپت‌های شما می‌توانند معیارهایی مانند عملکرد سرمایه‌گذاری را محاسبه کنند یا خروجی‌های بصری بسازند:

  • عملکرد سرمایه‌گذاری (IRR/XIRR): از آنجا که دفترکل شما شامل همه جریان‌های نقدی (خرید، فروش، سود سهام) است، می‌توانید نرخ بازده پرتفوی را محاسبه کنید. برای مثال، می‌توانید اسکریپتی بنویسید که تراکنش‌های حساب‌های سرمایه‌گذاری شما را فیلتر کند و سپس نرخ بازده داخلی را محاسبه کند. کتابخانه‌هایی (یا فرمول‌هایی) برای محاسبه IRR با داشتن داده جریان نقدی وجود دارد. برخی افزونه‌های Fava توسعه‌یافته توسط جامعه (مانند PortfolioSummary یا fava_investor) دقیقاً همین کار را می‌کنند و IRR و معیارهای دیگر را برای پرتفوی‌های سرمایه‌گذاری محاسبه می‌کنند. به عنوان یک اسکریپت، می‌توانید از یک تابع IRR (از NumPy یا تابع خودتان) روی سری مشارکت‌ها/برداشت‌ها به علاوه ارزش پایانی استفاده کنید.

  • معیارهای چنددوره‌ای یا سفارشی: می‌خواهید گزارشی از نرخ پس‌انداز خود (نسبت پس‌انداز به درآمد) در هر ماه داشته باشید؟ یک اسکریپت پایتون می‌تواند دفترکل را بارگذاری کند، همه حساب‌های درآمد و همه حساب‌های هزینه را جمع بزند، سپس پس‌انداز = درآمد - هزینه‌ها و درصد را محاسبه کند. این می‌تواند یک جدول زیبا خروجی دهد یا حتی یک گزارش HTML/Markdown برای سوابق شما تولید کند.

  • بصری‌سازی: می‌توانید نمودارها را خارج از Fava تولید کنید. برای مثال، از matplotlib یا altair در یک اسکریپت برای ساخت نمودار ارزش خالص در طول زمان با استفاده از داده دفترکل استفاده کنید. از آنجا که دفترکل همه مانده‌های تاریخی را دارد (یا می‌توانید با پیمایش ورودی‌ها آن‌ها را تجمیع کنید)، می‌توانید نمودارهای سری زمانی تولید کنید. این نمودارها را به صورت تصویر یا HTML تعاملی ذخیره کنید. (اگر تصویرسازی درون‌برنامه‌ای را ترجیح می‌دهید، برای افزودن نمودارها درون Fava بخش افزونه Fava را در ادامه ببینید.)

گزینه‌های خروجی: تصمیم بگیرید که گزارش را چگونه تحویل دهید:

  • برای تحلیل یک‌باره، چاپ روی صفحه یا ذخیره در یک فایل CSV/Excel ممکن است کافی باشد.
  • برای داشبوردها، تولید یک فایل HTML با داده‌ها را در نظر بگیرید (احتمالاً با استفاده از یک کتابخانه قالب مثل Jinja2 یا حتی فقط نوشتن Markdown) که می‌توانید در مرورگر باز کنید.
  • همچنین می‌توانید با Jupyter Notebooks برای یک محیط گزارش‌گیری تعاملی یکپارچه شوید، هرچند این بیشتر برای کاوش است تا خودکارسازی.

راه‌اندازی هشدارها از دفتر کل شما​

یکی دیگر از کاربردهای قدرتمند گردش‌کارهای قابل‌اسکریپت، تنظیم هشدارها بر اساس شرایط موجود در داده‌های مالی شماست. از آنجا که دفترکل شما به طور منظم به‌روزرسانی می‌شود (و می‌تواند مواردی با تاریخ آینده مانند صورت‌حساب‌های آینده یا بودجه‌ها را شامل شود)، می‌توانید آن را با یک اسکریپت پیمایش کنید و از رویدادهای مهم مطلع شوید.

هشدارهای موجودی کم حساب​

برای جلوگیری از برداشت بیش از موجودی یا حفظ حداقل مانده، ممکن است بخواهید در صورت افتادن هر حساب (مثلاً جاری یا پس‌انداز) زیر یک آستانه، هشداری دریافت کنید. در ادامه نحوه پیاده‌سازی این کار آمده است:

  1. مانده‌های فعلی را تعیین کنید: پس از بارگذاری entries از طریق بارگذار، آخرین مانده حساب‌های مورد نظر را محاسبه کنید. می‌توانید این کار را با تجمیع ثبت‌ها یا استفاده از یک پرس‌وجو انجام دهید. برای مثال، از یک پرس‌وجوی BQL برای مانده یک حساب مشخص استفاده کنید:

    SELECT sum(position) WHERE account = 'Assets:Bank:Checking'

    این مانده فعلی آن حساب (مجموع همه ثبت‌های آن) را بازمی‌گرداند. از طرف دیگر، از توابع داخلی Beancount برای ساختن ترازنامه استفاده کنید. برای مثال:

    from beancount.core import realization
    tree = realization.realize(entries)
    acct = realization.get_or_create(tree, "Assets:Bank:Checking")
    balance = acct.balance  # an Inventory of commodities

    فقط ورودی‌ها را پاس دهید: پارامتر دوم min_accounts است، نه نقشه گزینه‌ها. سپس مقدار عددی را استخراج کنید (مثلاً balance.get_currency_units('USD') مقدار Decimal را به دلار آمریکا بازمی‌گرداند). مانند یک تجمیع پرس‌وجو، مانده هر ارز را جداگانه نگه می‌دارد. با این حال، استفاده از پرس‌وجو برای بیشتر موارد ساده‌تر است.

  2. آستانه را بررسی کنید: مانده را با حد از پیش تعریف‌شده خود مقایسه کنید. اگر کمتر بود، یک هشدار راه‌اندازی کنید.

  3. اعلان را راه‌اندازی کنید: این می‌تواند به سادگی چاپ یک هشدار روی کنسول باشد، اما برای هشدارهای واقعی ممکن است ایمیل یا اعلان پوش ارسال کنید. می‌توانید با ایمیل (از طریق smtplib) یا سرویسی مانند IFTTT یا API وب‌هوک اسلک برای ارسال هشدار یکپارچه شوید. برای مثال:

    if balance < 1000:
        send_email("Low balance alert", f"Account XYZ balance is {balance}")

    (تابع send_email را با جزئیات سرور ایمیل خود پیاده‌سازی کنید.)

با اجرای روزانه این اسکریپت (از طریق یک کار cron یا زمان‌بند وظیفه ویندوز)، هشدارهای پیشگیرانه دریافت می‌کنید. از آنجا که از دفترکل استفاده می‌کند، می‌تواند همه تراکنش‌ها از جمله مواردی که تازه اضافه کرده‌اید را در نظر بگیرد.

مهلت‌های پرداخت آینده​

اگر از Beancount برای پیگیری صورت‌حساب‌ها یا مهلت‌ها استفاده می‌کنید، می‌توانید پرداخت‌های آینده را علامت‌گذاری کنید و اسکریپت‌ها به شما یادآوری کنند. دو راه برای نمایش تعهدات پیش‌رو در Beancount:

  • رویدادها: Beancount از دستور event برای یادداشت‌های تاریخ‌دار دلخواه پشتیبانی می‌کند. برای مثال:

    2025-05-10 event "BillDue" "Mortgage payment due"

    این بر مانده‌ها تأثیر نمی‌گذارد اما تاریخی را با یک برچسب ثبت می‌کند. یک اسکریپت می‌تواند entries را برای ورودی‌های Event جست‌وجو کند که در آن‌ها Event.type == "BillDue" (یا هر نوع سفارشی که انتخاب می‌کنید) و بررسی کند آیا تاریخ در بازه مثلاً ۷ روز آینده از امروز است. اگر بله، یک هشدار راه‌اندازی کنید (ایمیل، اعلان یا حتی یک پنجره بازشو).

  • تراکنش‌های آینده: برخی افراد تراکنش‌های تاریخ آینده (تاریخ‌گذاری‌شده برای آینده) را برای вещی مانند پرداخت‌های زمان‌بندی‌شده وارد می‌کنند. این‌ها تا زمانی که تاریخ بگذرد در مانده‌ها ظاهر نمی‌شوند (مگر اینکه گزارش‌ها را از تاریخ آینده اجرا کنید). یک اسکریپت می‌تواند تراکنش‌های تاریخ‌گذاری‌شده در آینده نزدیک را جست‌وجو و فهرست کند.

با استفاده از این‌ها، می‌توانید یک اسکریپت «یادآور» بسازید که هنگام اجرا، فهرستی از کارها یا صورت‌حساب‌های سررسید نزدیک را خروجی دهد. اگر می‌خواهید به طور خودکار یادآورهایی در آنجا بسازید، با API‌هایی مانند Google Calendar یا یک مدیر وظیفه یکپارچه شوید.

تشخیص ناهنجاری​

فراتر از آستانه‌ها یا تاریخ‌های شناخته‌شده، می‌توانید هشدارهای سفارشی برای الگوهای غیرعادی اسکریپت‌نویسی کنید. برای مثال، اگر یک هزینه معمولاً ماهانه رخ نداده (شاید فراموش کرده‌اید صورتحسابی را پرداخت کنید)، یا اگر هزینه یک دسته در این ماه به طور غیرعادی بالا باشد، اسکریپت شما می‌تواند آن را علامت‌گذاری کند. این معمولاً شامل پرس‌وجوی داده‌های اخیر و مقایسه با تاریخچه است (که ممکن است موضوعی پیشرفته باشد – احتمالاً با استفاده از آمار یا ML).

در عمل، بسیاری از کاربران برای گرفتن ناهنجاری‌ها (تراکنش‌های غیرمنتظره) به تطبیق تکیه می‌کنند. اگر اعلان‌های بانکی دریافت می‌کنید (مانند ایمیل برای هر تراکنش)، می‌توانید آن‌ها را با یک اسکریپت تجزیه کنید و به طور خودکار به Beancount اضافه کنید، یا حداقل تأیید کنید که ثبت شده‌اند. یکی از علاقه‌مندان حتی بانک خود را طوری تنظیم کرد که ایمیل‌های هشدار تراکنش ارسال کند، با این برنامه که آن‌ها را تجزیه و به طور خودکار به دفترکل بیفزاید. این نوع هشدار مبتنی بر رویداد می‌تواند تضمین کند که هیچ تراکنشی ثبت‌نشده نمی‌ماند.

گسترش Fava با پلاگین‌ها و نماهای سفارشی​

Fava از قبل از طریق سیستم افزونه‌اش قابل‌اسکریپت است. اگر می‌خواهید خودکارسازی یا گزارش‌های شما مستقیماً در رابط وب یکپارچه شوند، می‌توانید یک افزونه Fava (که پلاگین هم نامیده می‌شود) با پایتون بنویسید.

افزونه‌های Fava چگونه کار می‌کنند: یک افزونه یک ماژول پایتون است که کلاسی را تعریف می‌کند که از fava.ext.FavaExtensionBase ارث‌بری می‌کند. آن را در فایل Beancount خود از طریق یک گزینه سفارشی ثبت می‌کنید. برای مثال، اگر فایلی به نام myextension.py با کلاس MyAlerts(FavaExtensionBase) دارید، می‌توانید آن را با افزودن این خط به دفترکل خود فعال کنید:

1970-01-01 custom "fava-extension" "myextension"

وقتی Fava بارگذاری می‌کند، آن ماژول را وارد کرده و کلاس MyAlerts شما را مقداردهی اولیه می‌کند.

افزونه‌ها می‌توانند چندین کار انجام دهند:

  • قلاب‌ها: آن‌ها می‌توانند به رویدادهای چرخه عمر Fava متصل شوند. برای مثال، after_load_file() بعد از بارگذاری دفترکل فراخوانی می‌شود. می‌توانید از این برای اجرای بررسی‌ها یا پیش‌محاسبه داده استفاده کنید. اگر می‌خواستید بررسی مانده کم را درون Fava پیاده‌سازی کنید، after_load_file می‌توانست روی مانده‌های حساب پیمایش کند و شاید هشدارها را ذخیره کند (البته آشکارسازی آن‌ها در رابط کاربری ممکن است به کار بیشتری نیاز داشته باشد، مانند پرتاب یک FavaAPIError یا استفاده از Javascript برای نمایش اعلان).
  • گزارش‌ها/صفحات سفارشی: اگر کلاس افزونه شما یک ویژگی report_title تنظیم کند، Fava یک صفحه جدید در نوار کناری برای آن اضافه می‌کند. سپس یک قالب (HTML/Jinja2) برای محتوای آن صفحه ارائه می‌دهید. به این ترتیب نماهای کاملاً جدیدی می‌سازید، مانند یک داشبورد یا خلاصه‌ای که Fava به طور پیش‌فرض ندارد. افزونه می‌تواند هر داده‌ای که نیاز دارد را جمع‌آوری کند (می‌توانید به self.ledger دسترسی داشته باشید که همه ورودی‌ها، مانده‌ها و غیره را دارد) و سپس قالب را رندر کند.

برای مثال، افزونه داخلی portfolio_list در Fava صفحه‌ای اضافه می‌کند که موقعیت‌های پرتفوی شما را فهرست می‌کند. افزونه‌های جامعه‌ای فراتر می‌روند:

  • داشبوردها: پلاگین fava-dashboards امکان تعریف نمودارها و پنل‌های سفارشی (با استفاده از کتابخانه‌هایی مانند Apache ECharts) را می‌دهد. یک پیکربندی YAML از پرس‌وجوها را می‌خواند، آن‌ها را از طریق Beancount اجرا می‌کند و یک صفحه داشبورد پویا در Fava تولید می‌کند. در اصل، داده Beancount و یک کتابخانه نمودارسازی JavaScript را به هم می‌پیوندد تا تصویرسازی‌های تعاملی تولید کند.
  • تحلیل پرتفوی: افزونه PortfolioSummary (مشارکت کاربر) خلاصه‌های سرمایه‌گذاری را محاسبه می‌کند (گروه‌بندی حساب‌ها، محاسبه IRR و غیره) و آن‌ها را در رابط کاربری Fava نمایش می‌دهد.
  • بازبینی تراکنش: افزونه دیگری به نام fava-review به بازبینی تراکنش‌ها در طول زمان کمک می‌کند (مثلاً برای اطمینان از اینکه رسیدی را از دست نداده‌اید).

برای ساختن یک افزونه ساده خودتان، با ارث‌بری از FavaExtensionBase شروع کنید. برای مثال، یک افزونه حداقلی که یک صفحه اضافه می‌کند می‌تواند چنین باشد:

from fava.ext import FavaExtensionBase
 
class HelloReport(FavaExtensionBase):
    report_title = "Hello World"
 
    def __init__(self, ledger, config):
        super().__init__(ledger, config)
        # any initialization, perhaps parse config if provided
 
    def after_load_file(self):
        # (optional) run after ledger is loaded
        print("Ledger loaded with", len(self.ledger.entries), "entries")

اگر این را در hello.py قرار دهید و custom "fava-extension" "hello" را به دفترکل خود بیفزایید، Fava یک صفحه جدید «Hello World» نشان می‌دهد (همچنین به یک فایل قالب HelloReport.html در زیرپوشه templates نیاز دارید تا محتوای صفحه را تعریف کند، مگر اینکه افزونه فقط از قلاب‌ها استفاده کند). قالب می‌تواند از داده‌هایی که به کلاس افزونه پیوست می‌کنید استفاده کند. Fava از قالب‌های Jinja2 استفاده می‌کند، بنابراین می‌توانید داده‌های خود را در آن قالب به یک جدول HTML یا نمودار رندر کنید.

توجه: سیستم افزونه Fava قدرتمند است اما «ناپایدار» در نظر گرفته می‌شود (ممکن است تغییر کند). اگر صفحات سفارشی می‌سازید، به آشنایی با توسعه وب (HTML/JS) نیاز دارد. اگر هدف شما صرفاً اجرای اسکریپت‌ها یا تحلیل‌هاست، ممکن است نگه داشتن آن‌ها به عنوان اسکریپت‌های خارجی آسان‌تر باشد. از افزونه‌های Fava زمانی استفاده کنید که یک تجربه درون‌برنامه‌ای متناسب برای گردش‌کار خود می‌خواهید.

یکپارچه‌سازی APIها و داده‌های شخص ثالث​

یکی از مزایای گردش‌کارهای قابل‌اسکریپت، توانایی کشیدن داده‌های خارجی است. در ادامه یکپارچه‌سازی‌های رایج آمده است:

برای قیمت‌های ارزش‌گذاری میزبانی‌شده، Live Prices شامل‌های مدیریت‌شده را بدون اسکریپت زمان‌بندی‌شده واکشی قیمت ارائه می‌دهد. جفت دارایی‌های پشتیبانی‌شده و یک ارز مظنه را در انتخابگر انتخاب کنید. گردش‌کارهای محلی مبتنی بر فایل در زیر همچنان برای Beancount بالادستی، Fava و گزارش‌های بازتولیدپذیر مفید هستند. یک تازه‌سازی مدیریت‌شده در دفترکل شما یک commit گیت ایجاد نمی‌کند.

  • نرخ ارز و کالاها: Beancount بالادستی خودش قیمت‌ها را واکشی نمی‌کند، اما یک دستور price برای شما فراهم می‌کند تا نرخ‌ها را تأمین کنید. می‌توانید واکشی این قیمت‌ها را خودکار کنید. برای مثال، یک اسکریپت می‌تواند از یک API (Yahoo Finance، Alpha Vantage و غیره) آخرین نرخ ارز یا قیمت سهام را پرس‌وجو کند و یک ورودی قیمت به دفترکل شما بیفزاید:

    2025-04-30 price BTC 30000 USD
    2025-04-30 price EUR 1.10 USD

    ابزارهایی مانند bea price وجود دارند که توسط Beanprice در موتور مدیریت‌شده پشتیبانی می‌شوند و مظنه‌های روزانه را واکشی کرده و آن‌ها را در قالب Beancount خروجی می‌دهند. می‌توانید آن را یک بار با bea engine enable beanprice فعال کنید، سپس bea price main.beancount را برای اجرای هر شب زمان‌بندی کنید تا فایل شامل prices.beancount را به‌روزرسانی کند. یا از پایتون استفاده کنید: مثلاً با کتابخانه requests برای فراخوانی یک API. مستندات Beancount پیشنهاد می‌کند که برای دارایی‌های معامله‌شده در بازار عمومی، می‌توانید «کدی را فراخوانی کنید که قیمت‌ها را دانلود کرده و دستورها را برای شما بنویسد.» به عبارت دیگر، بگذارید یک اسکریپت جست‌وجو را انجام دهد و خطوط price را درج کند، به جای اینکه خودتان به صورت دستی این کار را کنید.

  • داده پرتفوی سهام: مشابه نرخ ارز، می‌توانید با API‌ها برای واکشی داده تفصیلی سهام یا سود سهام یکپارچه شوید. برای مثال، API یاهو فایننس (یا کتابخانه‌های جامعه‌ای مانند yfinance) می‌تواند داده تاریخی یک نماد را بازیابی کند. یک اسکریپت می‌تواند دفترکل شما را با تاریخچه قیمت ماهانه برای هر سهمی که دارید به‌روزرسانی کند و گزارش‌های تاریخی دقیق ارزش بازار را ممکن سازد. برخی افزونه‌های سفارشی (مانند fava_investor) حتی داده قیمت را در لحظه برای نمایش واکشی می‌کنند، اما ساده‌ترین راه این است که قیمت‌ها را به طور منظم در دفترکل وارد کنید.

  • API‌های بانکی (بانکداری باز/Plaid): به جای دانلود فایل‌های CSV، می‌توانید از API‌ها برای واکشی خودکار تراکنش‌ها استفاده کنید. سرویس‌هایی مانند Plaid حساب‌های بانکی را تجمیع می‌کنند و دسترسی برنامه‌نویسی به تراکنش‌ها را ممکن می‌سازند. در یک راه‌اندازی پیشرفته، می‌توانید اسکریپت پایتونی داشته باشید که از API Plaid برای کشیدن تراکنش‌های جدید به صورت روزانه و ذخیره آن‌ها در یک فایل (یا وارد کردن مستقیم به دفترکل) استفاده کند. یکی از کاربران قدرتمند سیستمی ساخت که در آن Plaid به خط لوله واردسازی آن‌ها تغذیه می‌شود و دفترهایشان را تقریباً خودکار می‌کند. آن‌ها اشاره می‌کنند که «هیچ چیز مانع ثبت‌نام شما در API Plaid و انجام همین کار به صورت محلی نمی‌شود» — یعنی می‌توانید یک اسکریپت محلی برای گرفتن داده بانکی بنویسید، سپس از منطق واردکننده Beancount خود برای تجزیه آن به ورودی‌های دفترکل استفاده کنید. برخی مناطق API‌های بانکداری باز ارائه‌شده توسط بانک‌ها دارند؛ آن‌ها را می‌توان به طور مشابه استفاده کرد.

  • API‌های دیگر: ممکن است ابزارهای بودجه‌بندی را یکپارچه کنید (صادرات بودجه‌های برنامه‌ریزی‌شده برای مقایسه با واقعیت‌ها در Beancount)، یا از یک API OCR برای خواندن رسیدها و تطبیق خودکار آن‌ها با تراکنش‌ها استفاده کنید. از آنجا که اسکریپت‌های شما دسترسی کامل به اکوسیستم پایتون دارند، می‌توانید همه چیز را از سرویس‌های ایمیل (برای ارسال هشدارها) تا Google Sheets (مثلاً به‌روزرسانی یک شیت با معیارهای مالی ماهانه) تا اپلیکیشن‌های پیام‌رسان (ارسال یک گزارش خلاصه به خودتان از طریق ربات تلگرام) یکپارچه کنید.

هنگام استفاده از API‌های شخص ثالث، به یاد داشته باشید که اعتبارنامه‌های خود را ایمن کنید (از متغیرهای محیطی یا فایل‌های پیکربندی برای کلیدهای API استفاده کنید)، و خطاها (مشکلات شبکه، از کار افتادن API) را به زیبایی در اسکریپت‌های خود مدیریت کنید. اغلب عاقلانه است که داده‌ها را ذخیره موقت کنید (برای مثال، نرخ‌های ارز واکشی‌شده را ذخیره کنید تا نرخ تاریخی یکسان را مکرراً درخواست نکنید).

بهترین روش‌ها برای اسکریپت‌های ماژولار و قابل نگهداری​

هنگام ساخت گردش‌کارهای قابل‌اسکریپت، کد خود را سازمان‌یافته و مقاوم نگه دارید:

  • ماژولار بودن: دغدغه‌های مختلف را به اسکریپت‌ها یا ماژول‌های مختلف تقسیم کنید. برای مثال، اسکریپت‌های جداگانه برای «واردسازی/تطبیق داده» در مقابل «تولید گزارش» در مقابل «هشدارها» داشته باشید. حتی می‌توانید یک بسته کوچک پایتون برای دفترکل خود با ماژول‌هایی مانند ledger_import.py، ledger_reports.py و غیره بسازید. این کار درک و تست هر بخش را آسان‌تر می‌کند.

  • پیکربندی: از هاردکد کردن مقادیر خودداری کنید. برای вещی مانند نام حساب‌ها، آستانه‌ها، کلیدهای API، بازه‌های تاریخ و غیره از یک فایل پیکربندی یا متغیرهای ابتدای اسکریپت استفاده کنید. این کار تنظیم بدون ویرایش عمیق کد را آسان می‌کند. برای مثال، در ابتدا LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000} را تعریف کنید و اسکریپت هشدار شما می‌تواند روی این دیکشنری حلقه بزند.

  • تست: با خودکارسازی مالی خود مانند کد حیاتی رفتار کنید — چون واقعاً همین‌طور است! برای منطق پیچیده تست بنویسید. Beancount برخی کمک‌تابع‌های تست (که به طور داخلی برای تست واردکننده استفاده می‌شود) ارائه می‌دهد که می‌توانید از آن‌ها برای شبیه‌سازی ورودی‌های دفترکل بهره ببرید. حتی بدون چارچوب‌های فانتزی، می‌توانید یک CSV ساختگی و تراکنش‌های خروجی مورد انتظار داشته باشید و تأیید کنید که اسکریپت واردسازی شما ورودی‌های درست را تولید می‌کند. اگر از pytest استفاده می‌کنید، می‌توانید این تست‌ها را به راحتی یکپارچه کنید (همان‌طور که Alex Watt از طریق یک فرمان just test که pytest را می‌پیچد انجام داد).

  • کنترل نسخه: دفترکل و اسکریپت‌های خود را تحت کنترل نسخه (git) نگه دارید. این نه تنها پشتیبان‌گیری و تاریخچه به شما می‌دهد، بلکه شما را تشویق می‌کند تغییرات را به روشی کنترل‌شده انجام دهید. می‌توانید نسخه‌های «اسکریپت‌های مالی» خود را برچسب‌گذاری کنید یا هنگام اشکال‌زدایی یک مشکل، تفاوت‌ها را بازبینی کنید. برخی کاربران حتی سوابق مالی خود را در Git پیگیری می‌کنند تا تغییرات در طول زمان را ببینند. فقط مراقب باشید که داده‌های حساس (مانند فایل‌های صورت‌حساب خام یا کلیدهای API) را در مخزن خود نادیده بگیرید.

  • مستندسازی: گردش‌کارهای سفارشی خود را برای خودِ آینده مستند کنید. یک README در مخزن شما که توضیح دهد چگونه محیط را تنظیم کنید، چگونه هر اسکریپت را اجرا کنید و هر کدام چه کاری انجام می‌دهد، پس از گذشت ماه‌ها بسیار ارزشمند خواهد بود. همچنین کد خود را کامنت‌گذاری کنید، به ویژه هر منطق حسابداری غیرشهودی یا تعامل با API.

  • نگهداری پلاگین‌های Fava: اگر یک افزونه Fava می‌نویسید، آن را ساده نگه دارید. Fava ممکن است تغییر کند، بنابراین افزونه‌های کوچک‌تر با عملکرد هدفمند آسان‌تر به‌روزرسانی می‌شوند. از تکرار بیش از حد منطق خودداری کنید – هر جا ممکن است از موتور پرس‌وجوی Beancount یا توابع کمکی موجود استفاده کنید، به جای هاردکد کردن محاسباتی که ممکن است به تغییرات دفترکل حساس باشد.

  • امنیت: از آنجا که اسکریپت‌های شما ممکن است داده‌های حساس را مدیریت کرده و به سرویس‌های خارجی متصل شوند، با احتیاط با آن‌ها رفتار کنید. کلیدهای API را افشا نکنید و اجرای خودکارسازی خود را روی یک دستگاه امن در نظر بگیرید. اگر از یک راه‌حل میزبانی‌شده یا ابری (مانند زمان‌بندی GitHub Actions یا یک سرور برای اجرای Fava) استفاده می‌کنید، اطمینان حاصل کنید که داده دفترکل شما در حالت سکون رمزنگاری شده است و با پیامدهای حریم خصوصی آن راحت هستید.

با پیروی از این روش‌ها، تضمین می‌کنید که گردش‌کار شما حتی با تحول امور مالی (و خود ابزارها) قابل‌اعتماد باقی می‌ماند. شما اسکریپت‌هایی می‌خواهید که بتوانید سال به سال با حداقل تغییرات از آن‌ها استفاده مجدد کنید.

نتیجه‌گیری​

Beancount و Fava بستری قدرتمند و انعطاف‌پذیر برای کاربران فنی‌محور فراهم می‌کنند تا رهگیری مالی شخصی خود را کاملاً سفارشی کنند. با نوشتن اسکریپت‌های پایتون، می‌توانید کارهای خسته‌کننده مانند تطبیق صورت‌حساب‌ها را خودکار کنید، گزارش‌های غنی متناسب با نیازهای خود تولید کنید، و با هشدارهای به‌موقع بر امور مالی خود مسلط بمانید. ما طیفی از مثال‌ها از پایه تا پیشرفته را پوشش دادیم – از پرس‌وجوهای ساده و واردسازی CSV شروع کردیم و به پلاگین‌های کامل Fava و یکپارچه‌سازی‌های API خارجی رسیدیم. هنگام پیاده‌سازی این‌ها، ساده شروع کنید و به تدریج بسازید. حتی چند اسکریپت خودکارسازی کوچک می‌تواند ساعت‌ها کار را ذخیره کند و دقت را به شدت بهبود دهد. و به یاد داشته باشید، چون همه چیز متن‌ساده و پایتون است، شما کنترل کامل دارید – سیستم مالی شما با شما رشد می‌کند، و به نیازهای خاص شما خم می‌شود. اسکریپت‌نویسی لذت‌بخش!

منابع: تکنیک‌های بالا از مستندات Beancount و تجربیات جامعه گرفته شده‌اند. برای مطالعه بیشتر، مستندات رسمی Beancount، راهنماها و وبلاگ‌های جامعه، و مخزن Awesome Beancount برای پیوند به پلاگین‌ها و ابزارهای مفید را ببینید.

منبع: https://beancount.io/fa/docs/Solutions/scriptable-workflows