ثبت دوطرفه¶
هر تراکنش دو چیز مینویسد: یک سطر Transaction که آدم میبیندش، و یک
JournalEntry متوازن که دفترها را قابل اثبات میکند.
چرا زحمتش را بکشیم¶
دفتر یکطرفه یک فهرست است. میشود از آن پرسید «برای اجاره چقدر دادم؟» و جواب میدهد، اما نمیشود پرسید «اینها جور در میآید؟» — چیزی نیست که در برابرش بشود بررسی کرد.
ثبت دوطرفه همان سؤال را به تو میدهد. اگر بدهکار و بستانکار جور نباشند چیزی خراب است، و پیش از مشتری میفهمی.
شکل کار¶
Transaction JournalEntry
flow: income ├── JournalLine بدهکار 1000 (نقد) ۲۵۰٬۰۰۰
category: فروش └── JournalLine بستانکار 4000 (درآمد) ۲۵۰٬۰۰۰
amount: 250,000
هزینه آینهاش است:
Transaction JournalEntry
flow: expense ├── JournalLine بدهکار 5000 (هزینه) ۲٬۰۰۰٬۰۰۰
category: اجاره └── JournalLine بستانکار 1000 (نقد) ۲٬۰۰۰٬۰۰۰
فهرست حسابها¶
هنگام نیاز ساخته میشود، اولین باری که دفتری چیزی ثبت میکند:
| کد | نام | نوع |
|---|---|---|
| 1000 | نقد و بانک | دارایی |
| 1900 | خزانه | دارایی |
| 2000 | بدهی | بدهی |
| 3000 | سرمایه | سرمایه |
| 4000 | درآمد | درآمد |
| 5000 | هزینه | هزینه |
شش حساب کمینهای است که یک دفتر برای ثبت هر چیزی لازم دارد. عمداً قابلپیکربندی نیست: مغازهدار نمیخواهد فهرست حساب طراحی کند، و سیستمی که اجازهاش را بدهد سیستمی است که اجازه میدهد فهرست خرابی طراحی کند.
آن ثابت، و جایی که بررسی میشود¶
آن ادعا تزئین نیست. پس از ثبت، پس از حذف تراکنش، پس از تسویهٔ بدهی، و پس از پرداخت قسط وام اجرا میشود — چون اینها همان چهار جایی هستند که نوشتن ناقص دفترها را اثباتناپذیر میکند.
اگر سطرهایی که جور در نمیآیند به post_entry داده شوند BalanceError پرتاب
میکند. در محیط عملیاتی هرگز شلیک نکرده، و نکته هم همین است: هست تا روزی که
واقعاً چیزی خراب شود، سرِ نوشتن با صدای بلند شکست بخورد بهجای اینکه سرِ گزارش
بیصدا.
پول هرگز به اعشاری ممیز شناور دست نمیزند¶
shared/money.py نوع Money را تعریف میکند، یک TypeDecorator:
| پستگرس | NUMERIC(28, 4) — دهدهی دقیق بومی |
| SQLite | String(40) — متن همان Decimal، که موقع خواندن برمیگردد |
چهار رقم اعشار، چون نرخ تبدیل لازمشان دارد حتی وقتی ریال ندارد. بیستوهشت رقم، چون تورم هست.
روی مرز HTTP پول رشته است، نه عدد JSON. تنها نوع عددی JSON اعشاری ممیز
شناور است؛ 12500000.15 میشود 12500000.149999999 و همان میماند.
چندارزی¶
تراکنشی به ارز خارجی سه چیز ذخیره میکند:
original_amount عددی که شخص تایپ کرد
original_currency به چه ارزی تایپش کرد
conversion_rate نرخ همان لحظهای که تایپ کرد
converted_amount اصلی × نرخ، به ارز پایهٔ دفتر
نرخ یک بار ثبت میشود و هرگز دوباره اعمال نمیشود. بازخوانی تراکنش سال
گذشته نشان میدهد آن موقع چقدر میارزید، نه آنچه نرخ امروز از آن میسازد.
گزارشها converted_amount را جمع میزنند، پس دفتری با پنج ارز هم جور در
میآید.
حذف یک تراکنش¶
سند روزنامه را با خودش حذف میکند، در همان واحد کار. تراز آزمایشی بعدش در تستها ادعا میشود، چون «سطر را حذف کن و سند را فراموش کن» دقیقاً همان ایرادی است که دفتری را اثباتناپذیر میکند — و تا وقتی کسی مغایرتگیری نکند خودش را نشان نمیدهد.
جایی که سند نوشته نمیشود¶
تخصیصهای خزانه و فیشهای حقوقی سند تصمیم حقوقیاند، نه تراکنش. بهجای ثبت در دفتر، روی دوره عکسبرداری میشوند — فیش حقوقی بیانیهٔ چیزی است که به کسی بدهکاریم، و جابهجا شدن پول واقعیت جداگانه و بعدی است.
پولی که واقعاً جابهجا میشود — قسط وام پرداختشده، شلیک قاعدهٔ تکرارشونده، و
بدهی تسویهشده وقتی کاربر بخواهد پرداختش هم ثبت شود — مثل هر چیز دیگری از
LedgerService.record() رد میشود و مثل هر چیز دیگری سند روزنامهاش را میگیرد.
تسویهٔ بدهی بهطور پیشفرض چیزی ثبت نمیکند: اگر فروش نسیه یا خرید قبلاً ثبت شده،
پرداخت همان پول است و ثبت دوبارهاش آن را دوبار میشمارد.