Beancount (ابزار حسابداری دوطرفه متنساده) و Fava (رابط وب آن) بسیار قابل توسعه و اسکریپتپذیر هستند. طراحی آنها به شما امکان میدهد تا وظایف مالی را خودکار کنید، گزارشهای سفارشی تولید کنید و با نوشتن اسکریپتهای پایتون، هشدار تنظیم کنید. به گفته یکی از کاربران، «من واقعاً دوست دارم دادههایم در چنین فرمت مناسبی باشد و دوست دارم که بتوانم همه چیز را تا حد دلخواه خودکار کنم. هیچ APIای مانند یک فایل روی دیسک شما وجود ندارد؛ به راحتی قابل یکپارچهسازی است.» این راهنما شما را در ایجاد گردشکارهای اسکریپتپذیر—از خودکارسازی سطح مبتدی تا پلاگینهای پیشرفته Fava—راهنمایی خواهد کرد.
یک نمونه دفتر کل زنده را کاوش کنید:
با خط فرمان 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 و دستور extract آن را حذف کرده است. شما یک کلاس کوچک پایتون مینویسید که زیرکلاس beangulp.Importer است تا یک فرمت خاص (CSV، OFX، PDF و غیره) را تجزیه کرده و تراکنشها تولید کند. آن را در یک اسکریپت ingest کوتاه ثبت کنید و سپس از طریق bea ingest در موتور مدیریتشده اجرا کنید:
- یک واردکننده بنویسید (یک کلاس پایتون با متدهای
identify()،account()وextract()) برای فرمت CSV بانک شما. - یک اسکریپت ingest اضافه کنید که واردکنندههای شما را ثبت میکند.
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 (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.beancountidentify گزارش میدهد که checking_importer.CheckingImporter برای فایل است. extract تراکنشها را در قالب Beancount مینویسد:
2024-01-08 * "Grocery Store"
Expenses:Food:Groceries 120.00 USD
Assets:Bank:Checking -120.00 USDnew.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) را با ورودیهای دفتر کل خود مقایسه کنید:
- خواندن دادههای خارجی: فایل CSV بانک را با استفاده از ماژول
csvپایتون (یا Pandas) تجزیه کنید. دادهها را به یک لیست از تراکنشها نرمال کنید، به عنوان مثال هر کدام با تاریخ، مبلغ و توضیحات. - بارگذاری تراکنشهای دفتر کل: از
loader.load_fileهمانطور که قبلاً نشان داده شد استفاده کنید تا تمام ورودیهای دفتر کل را دریافت کنید. این لیست را به حساب مورد نظر (به عنوان مثال حساب چکی شما) و شاید بازه زمانی صورتحساب فیلتر کنید. - مقایسه و یافتن مغایرتها:
- برای هر تراکنش خارجی، بررسی کنید که آیا ورودی یکسانی در دفتر کل وجود دارد (تطبیق بر اساس تاریخ و مبلغ، شاید توضیحات). اگر یافت نشد، آن را به عنوان «جدید» علامتگذاری کنید و احتمالاً آن را به عنوان تراکنش با فرمت Beancount برای بررسی شما خروجی دهید.
- برعکس، هر ورودی دفتر کل در آن حساب را که در منبع خارجی ظاهر نمیشود شناسایی کنید – این موارد میتوانند خطاهای ورود داده یا تراکنشهایی باشند که هنوز در بانک تسویه نشدهاند.
- خروجی نتایج: یک گزارش چاپ کنیید یا یک قطعه جدیید
.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 از طریق زبان پرسوجو و سپس قرار دادن آن در یک Pandas 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 برای یک محیط گزارشدهی تعاملی یکپارچه شوید، اگرچه این بیشتر برای اکتشاف است تا خودکارسازی.
راهاندازی هشدارها از دفتر کل شما
یکی دیگر از کاربردهای قدرتمند گردشکارهای اسکریپتپذیر، راهاندازی هشدارها بر اساس شرایط موجود در دادههای مالی شماست. از آنجا که دفتر کل شما به طور منظم بهروزرسانی میشود (و میتواند شامل مواردی با تاریخ آینده مانند قبضهای آینده یا بودجهها باشد)، میتوانید آن را با یک اسکریپت اسکن کنید و از رویدادهای مهم مطلع شوید.
هشدارهای موجودی کم حساب
برای جلوگیری از اضافهبرداشت یا حفظ حداقل موجودی، ممکن است بخواهید اگر هر حسابی (مثلاً چکی یا پسانداز) به زیر یک آستانه رسید، هشدار دریافت کنید. در اینجا نحوه پیادهسازی این کار آمده است:
-
تعیین موجودیهای فعلی: پس از بارگذاری
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 را در USD برمیگرداند). مانند یک مجموع پرسوجو، موجودی هر ارز را جداگانه نگه میدارد. با این حال، برای بیشتر موارد استفاده از پرسوجو سادهتر است. -
بررسی آستانه: موجودی را با حد از پیش تعریفشده خود مقایسه کنید. اگر کمتر بود، یک هشدار فعال کنید.
-
فعالسازی اعلان: این میتواند به سادگی چاپ یک هشدار در کنسول باشد، اما برای هشدارهای واقعی ممکن است ایمیل یا اعلان فشاری ارسال کنید. میتوانید با ایمیل (از طریق
smtplib) یا سرویسی مانند IFTTT یا API webhook اسلک یکپارچه شوید تا هشدار را ارسال کنید. به عنوان مثال: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 یا یک مدیریت وظایف یکپارچه شوید.
تشخیص ناهنجاری
فراتر از آستانهها یا تاریخهای شناختهشده، میتوانید هشدارهای سفارشی براای الگوهای غیر معمول اسکریپت کنید. به عنوا مثال، اگر یک هزینه ماهانه معمول رخ داده نشده باشد (شاید فراموش کردهاید قبضی را پرداخت کنید)، یا اگر هزینه یک دسته در این ماه به طور غیرعادی بالا است، اسکریپت شما میتواند آن را پرچم کند. این معمولاً شامل پرسوجو از دادههای اخیر و مقایسه با تاریخچه است (که ممکن است یک موضوع پیشرفته باشد – احتمالاً استفاده از آمار یا یادگیری ماشین).
در عمل، بسیاری از کاربران براای شناسایی ناهنجاریها (تراکنشهای غیرمنتظره) بر تطبیق تکیه میکنند. اگر اعلانهای بانکی (مانند ایمیل برای هر تراکنش) دریافت میکنید، میتوانید آنها را با یک اسکریپت تجزیه کرده و به طور خودکار به 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 یا استفاده از جاوااسکریپت برای نمایش اعلان). - گزارشها/صفحات سفارشی: اگر کلاس افزونه شما یک ویژگی
report_titleتنظیم کند، Fava یک صفحه جدید در نوار کناری برای آن اضافه میکند. سپس شما یک قالب (HTML/Jinja2) برای محتوای آن صفحه ارائه میدهید. این روشی است که نمای کاملاً جدیدی ایجاد میکنید، مانند یک داشبورد یا خلاصه که Fava به طور پیشفرض ندارد. افزونه میتواند هر دادهای که نیاز دارد جمع آوری کند (میتوانید بهself.ledgerدسترسی داشته باشید که تمام ورودیها، موجودیها و غیره را دارد) و سپس قالب را رندر کند.
به عنوان مثال، افزونه داخلی portfolio_list در Fava صفحهای اضافه میکند که موقعیتهای پرتفوی شما را فهرست میکند. افزونههای جامعه فراتر میروند:
- داشبوردها: پلاگین fava-dashboards امکان تعریف نمودارها و پنلهای سفارشی (با استفاده از کتابخانههایی مانند Apache ECharts) را فراهم میکند. این پلاگین یک پیکربندی YAML از پرسوجوها را میخواند، آنها را از طریق Beancount اجرا میکند و یک صفحه داشبورد پویا در Fava تولید میکند. در اصل، دادههای Beancount و یک کتابخانه نمودار جاوااسکریپت را به هم پیوند میدهد تا تجسمهای تعاملی تولید کند.
- تحلیل پرتفوی: افزونه 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 یک صفحه جدید "سلام دنیا" نمایش میدهد (همچنین به یک فایل قالب 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ابزارهایی مانند
bea priceوجود دارند که توسط Beanprice در موتور مدیریتشده پشتیبانی میشوند و نقلقولهای روزانه را دریافت کرده و آنها را در قالب Beancount خروجی میدهند. میتوانید آن را یکبار باbea engine enable beanpriceفعال کنید، سپسbea price main.beancountرا زمانبندی کنید تا هر شب یک فایل includeprices.beancountرا بهروزرسانی کند. یا از پایتون استفاده کنید: به عنوان مثال، با کتابخانهrequestsبرای فراخوانی یک API. مستندات Beancount پیشنهاد میکند که برای داراییهای معاملهشده در بورس، میتوانید «کدی را فراخوانی کنید که قیمتها را دانلود کرده و دستورات را برای شما بنویسد.» به عبارت دیگر، اجازه دهید یک اسکریپت جستجو و درج خطوطpriceرا انجام دهد، نه اینکه شما آن را به صورت دستی انجام دهید. -
دادههای پرتفوی سهام: مشابه نرخ ارز، میتوانید با APIها برای دریافت دادههای دقیق سهام یا سود سهام یکپارچه شوید. به عنوان مثال، API Yahoo Finance (یا کتابخانههای جامعه مانند
yfinance) میتواند دادههای تاریخی یک نماد را بازیابی کند. یک اسکریپت ممکن است دفتر کل شما را با تاریخچه قیمت ماهانه برای هر سهامی که دارید بهروزرسانی کند و گزارشهای تاریخی دقیقی از ارزش بازار را ممکن سازد. برخی افزونههای سفارشی (مانند fava_investor) حتی دادههای قیمت را در لحظه براای نمایش کشیده و خروجی میدهند، اما سادهترین کار این است که به طور منظم قیمتها را به دفتر کل وارد کنید. -
APIهای بانکی (Open Banking/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 را برای پیوند به پلاگینها و ابزارهای مفید ببینید.