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

گردش‌کارهای اسکریپت‌پذیر با Beancount و Fava

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

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

scriptable-workflows

قبل از پرداختن به وظایف خاص، مطمئن شوید که Beancount را نصب کرده‌اید (به عنوان مثال از طریق pip install beancount). از آنجا که Beancount به زبان پایتون نوشته شده است، می‌توانید از آن به عنوان یک کتابخانه در اسکریپت‌های خود استفاده کنید. رویکرد کلی به این صورت است:

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

    from beancount import loader
    entries, errors, options_map = loader.load_file("myledger.beancount")
    if errors:
    print("Errors:", errors)

    این یک لیست از entries (تراکنش‌ها، موجودی‌ها و غیره) و یک options_map با فراداده‌ها به شما می‌دهد. اکنون همه حساب‌ها، تراکنش‌ها و موجودی‌های شما در کد قابل دسترسی هستند.

  • بهره‌گیری از زبان پرس و جو Beancount ‏(BQL): به جای تکرار دستی، می‌توانید پرس و جوهای شبیه SQL را روی داده‌ها اجرا کنید. برای مثال، برای دریافت کل هزینه‌ها بر اساس ماه، می‌توانید از API پرس و جو استفاده کنید:

    from beancount.query import query
    q = query.Query(entries, options_map)
    result = q.query("SELECT month, sum(position) WHERE account ~ 'Expenses' GROUP BY month")
    print(result)

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

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

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

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

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

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

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

برای مبتدیان، رویکرد توصیه شده استفاده از افزونه‌های واردکننده Beancount است. شما یک کلاس پایتون کوچک را مطابق با پروتکل واردکننده Beancount می‌نویسید تا یک قالب معین (CSV، OFX، PDF و غیره) را تجزیه کرده و تراکنش‌ها را تولید کند. سپس از دستور bean-extract یا یک اسکریپت برای اعمال این واردکننده‌ها استفاده کنید:

  • یک واردکننده بنویسید (یک کلاس پایتون با متدهایی مانند identify()، extract()) برای فرمت CSV بانک خود. مستندات Beancount یک راهنما و مثال‌هایی را ارائه می‌دهد.
  • از bean-extract در یک اسکریپت یا Makefile (مانند مثال justfile) برای تجزیه صورت‌حساب‌های جدید استفاده کنید. برای مثال، یک گردش کار bean-extract را روی همه فایل‌های موجود در ~/Downloads اجرا می‌کند و تراکنش‌ها را در یک فایل موقت خروجی می‌دهد.
  • تراکنش‌ها را به صورت دستی بررسی و از فایل موقت در دفتر کل اصلی خود کپی کنید، سپس bean-check را اجرا کنید تا مطمئن شوید که موجودی‌ها تطبیق می‌شوند.

در حالی که این فرآیند هنوز شامل یک مرحله بررسی است، بسیاری از کارهای سنگین تجزیه و قالب‌بندی ورودی‌ها خودکار می‌شوند. اسکریپت‌های واردکننده همچنین می‌توانند به طور خودکار دسته‌ها را اختصاص دهند و حتی ادعاهای موجودی (اظهارات موجودی‌های مورد انتظار) را برای تشخیص مغایرت‌ها تنظیم کنند. برای مثال، پس از وارد کردن، ممکن است خطی مانند 2025-04-30 balance Assets:Bank:Checking 1234.56 USD داشته باشید که موجودی پایانی را ادعا می‌کند. هنگامی که bean-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(amount)
    WHERE account LIKE 'Income:%' OR account LIKE 'Expenses:%'
    GROUP BY year, month

    این تمام ثبت‌های درآمد و هزینه را بر اساس ماه خالص می‌کند. می‌توانید این را از طریق CLI ‏bean-query یا از طریق API پایتون (query.Query همانطور که قبلاً نشان داده شد) اجرا کنید و سپس نتیجه را قالب‌بندی کنید.

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

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

    این یک جدول از هزینه‌ها بر اساس دسته‌بندی را به دست می‌دهد. می‌توانید چندین پرس و جو را در یک اسکریپت اجرا کنید و نتایج را به صورت متن، 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, options_map)
    acct = realization.get_or_create(tree, "Assets:Bank:Checking")
    balance = acct.balance # an Inventory of commodities

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

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

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

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

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

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

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

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

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

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

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

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

با استفاده از اینها، می‌توانید یک اسکریپت "tickler" ایجاد کنید که هنگام اجرا، لیستی از وظایف یا قبوضی که به زودی سررسید می‌شوند را خروجی دهد. با یک 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 می‌تواند روی موجودی حساب‌ها تکرار شود و شاید هشدارهایی را ذخیره کند (اگرچه نمایش آنها در UI ممکن است به کمی کار بیشتر نیاز داشته باشد، مانند افزایش یک FavaAPIError یا استفاده از جاوا اسکریپت برای نشان دادن یک اعلان).
  • گزارش‌ها/صفحات سفارشی: اگر کلاس افزونه شما یک ویژگی report_title را تنظیم کند، Fava یک صفحه جدید در نوار کناری برای آن اضافه می‌کند. سپس شما یک الگو (HTML/Jinja2) برای محتوای آن صفحه ارائه می‌دهید. اینگونه است که نماهای کاملاً جدیدی ایجاد می‌کنید، مانند یک داشبورد یا خلاصه‌ای که Fava به طور پیش فرض ندارد. افزونه می‌تواند هر داده‌ای را که نیاز دارد جمع‌آوری کند (می‌توانید به self.ledger دسترسی داشته باشید که دارای تمام ورودی‌ها، موجودی‌ها و غیره است) و سپس الگو را رندر کند.

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

  • داشبوردها: افزونه fava-dashboards به شما امکان می‌دهد نمودارها و پانل‌های سفارشی را تعریف کنید (با استفاده از کتابخانه‌هایی مانند Apache ECharts). یک پیکربندی YAML از پرس و جوها را برای اجرا می‌خواند، آنها را از طریق Beancount اجرا می‌کند و یک صفحه داشبورد پویا را در Fava تولید می‌کند. در اصل، داده‌های Beancount و یک کتابخانه نمودارسازی جاوا اسکریپت را به هم متصل می‌کند تا تجسم‌های تعاملی تولید کند.
  • تجزیه و تحلیل پورتفولیو: افزونه PortfolioSummary (ارائه‌شده توسط کاربر) خلاصه‌های سرمایه‌گذاری را محاسبه می‌کند (گروه‌بندی حساب‌ها، محاسبه IRR و غیره) و آنها را در UI 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ها و داده‌های شخص ثالث

یکی از مزایای گردش‌کارهای اسکریپت‌پذیر، توانایی وارد کردن داده‌های بیرونی است. در اینجا ادغام‌های رایج وجود دارد:

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

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

    ابزارهایی مانند bean-price وجود دارند (اکنون یک ابزار خارجی تحت چتر beancount) که قیمت‌های روزانه را واکشی می‌کنند و آنها را در قالب Beancount خروجی می‌دهند. می‌توانید bean-price را زمان‌بندی کنید تا هر شب اجرا شود تا یک فایل شامل prices.beancount را به روز کند. یا از پایتون استفاده کنید: به عنوان مثال، با کتابخانه requests برای فراخوانی یک API. مستندات Beancount پیشنهاد می‌کند که برای دارایی‌های معامله شده عمومی، می‌توانید "کدی را فراخوانی کنید که قیمت‌ها را دانلود کرده و دستورالعمل‌ها را برای شما بنویسد." به عبارت دیگر، اجازه دهید یک اسکریپت جستجو را انجام دهد و خطوط price را وارد کند، نه اینکه شما این کار را به صورت دستی انجام دهید.

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

  • APIهای بانکی (Open Banking/Plaid): به جای دانلود CSVها، می‌توانید از APIها برای واکشی خودکار تراکنش‌ها استفاده کنید. سرویس‌هایی مانند Plaid حساب‌های بانکی را جمع‌آوری می‌کنند و دسترسی برنامه‌نویسی به تراکنش‌ها را امکان پذیر می‌کنند. در یک تنظیم پیشرفته، می‌توانید یک اسکریپت پایتون داشته باشید که از API Plaid برای واکشی تراکنش‌های جدید روزانه و ذخیره آنها در یک فایل استفاده می‌کند (یا مستقیماً به دفتر کل وارد می‌کند). یک کاربر قدرتمند سیستمی را ساخته است که در آن Plaid وارد خط لوله واردات آنها می‌شود و کتاب‌های آنها را تقریباً خودکار می‌کند. آنها خاطرنشان می‌کنند که "هیچ چیزی مانع شما نمی‌شود که در Plaid API ثبت نام کنید و همان کار را به صورت محلی انجام دهید" - به این معنی که می‌توانید یک اسکریپت محلی برای دریافت داده‌های بانکی بنویسید، سپس از منطق واردکننده Beancount خود برای تجزیه آن به ورودی‌های دفتر کل استفاده کنید. برخی از مناطق APIهای بانکداری باز ارائه شده توسط بانک‌ها دارند. از آن APIها نیز می‌توان به طور مشابه استفاده کرد.

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

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

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

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

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

  • پیکربندی: از کدگذاری سخت مقادیر خودداری کنید. از یک فایل پیکربندی یا متغیرهای بالای اسکریپت برای مواردی مانند نام حساب‌ها