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

حقوق و خزانه

برای دفترهای تیمی و سازمانی، جایی که سود بین آدم‌ها تقسیم می‌شود. دفتر شخصی یا کسب‌وکار این را ارائه نمی‌دهد — تقسیم سود بین یک نفر امکان نیست.

ایده

درآمد
  − هزینه‌های مستقیم
  ─────────────
  = سود خالص
  − خزانه                  آنچه تیم اول کنار می‌گذارد
  ─────────────
  = قابل‌توزیع             بر اساس سهم هر عضو تقسیم می‌شود
  ± تعدیل‌ها               پاداش و کسر
  ─────────────
  = خالص پرداختی           برای هر نفر، در یک یا چند انتقال

هر خط از این روی صفحهٔ دوره نشان داده می‌شود. کسی که قرار است سهمی از عددی بگیرد باید بتواند ببیند آن عدد چطور به دست آمده.

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

هزینه از سهم چه کسی کم می‌شود

زنجیرهٔ بالا یکی از دو حالت است و هر دفتر با cost_policy بین آن دو انتخاب می‌کند:

قاعده قابل‌توزیع آنچه خزانه نگه می‌دارد
before_split (پیش‌فرض) درآمد − هزینه − خزانه سهم خودش
from_treasury درآمد − خزانه سهم خودش − هزینه

before_split خوانش متعارف است: هزینه اول کم می‌شود و یک ماه بد را همه حس می‌کنند. from_treasury کاری است که شراکتی می‌کند که سهم افرادش را از سر درآمد می‌دهد — اعضا سهمشان را از کل درآمد می‌گیرند و هزینه‌ها همه از سهم خزانه برداشته می‌شود.

با from_treasury ممکن است خزانه دوره را منفی تمام کند، وقتی هزینه از سهمش بیشتر شود. این خوانش صادقانهٔ آن ماه است، پس گزارش می‌شود نه اینکه صفر شود.

تغییر قاعده فقط محاسبهٔ بعدی را هدایت می‌کند. دوره‌ای که محاسبه شده اعدادش روی فیش‌ها منجمد مانده، و سهم خزانهٔ هر دوره هنگام محاسبهٔ دوباره جایگزین می‌شود، نه اینکه روی هم جمع شود.

با PUT /books/{id}/cost-policy یا در ربات از صفحهٔ خزانه تنظیم می‌شود. هر دو به دسترسی MANAGE_TREASURY نیاز دارند، چون جواب این سؤال پول را بین خزانه و آدم‌ها جابه‌جا می‌کند.

تا وقتی دوره باز است می‌شود یک پرداخت را پس گرفت؛ فیش و کیف پول به حالت قبل برمی‌گردند. وگرنه عددی که اشتباه تایپ شده — یا به نام اشتباه ثبت شده — برای همیشه می‌ماند.

می‌شود به عضو با هر ارزی که دفتر نگه می‌دارد پرداخت کرد. پرداخت نرخ لحظهٔ خودش را همراه دارد و فیش آن را به ارز خودش حساب می‌کند — چهل تتر یعنی ارزش چهل تتر، نه چهل تومان.

دوره‌ها

نه صدور فیش دوره را می‌بندد، نه پرداختش. درآمدی که یک روز دیر می‌رسد و قبضی که بعد از پرداخت به اعضا می‌آید مال همان ماه‌اند، و رد کردنشان فقط حقیقت را به ماه اشتباه هل می‌داد. فقط وضعیت دوره آن را می‌بندد — چرخهٔ پایین را ببین.

فیش اسنپ‌شات است، پس ورودیِ بعد از آن کهنه‌اش می‌کند. صفحهٔ دوره این را می‌گوید و محاسبهٔ دوباره را پیشنهاد می‌دهد. محاسبهٔ دوباره هر فیش را سر جایش به‌روز می‌کند: هر پرداختی که رویش ثبت شده می‌ماند و فقط مانده عوض می‌شود. این به هر دو طرف ممکن است برود:

  • درآمدی که بعد از پرداخت اضافه شود، تفاوت را طلب عضو می‌گذارد؛
  • هزینه در حالت before_split، یا درآمدی که از دوره بیرون برود، ممکن است فیش را کمتر از پرداخت‌شده کند. این به‌صورت overpaid (بیش از سهم) نشان داده می‌شود و هرگز «تسویه» حساب نمی‌شود؛ راه معمول پس‌گرفتنش کسر در دورهٔ بعد است.

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

تا وقتی دوره به کسی پول نداده، می‌شود محاسبه را باطل کرد؛ این کار هم فیش‌ها و هم تخصیص خزانه را آزاد می‌کند. بعد از اولین پرداخت رد می‌شود — پرداخت‌ها با فیششان پاک می‌شوند — و چیزی که چنین دوره‌ای لازم دارد محاسبهٔ دوباره است.

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

دوره همان بازه‌ای است که همه‌چیز روی آن سنجیده می‌شود — معمولاً یک ماه جلالی.

باز → در حال محاسبه → در انتظار تأیید → تأییدشده → پرداخت‌شده → قفل

فقط همین حرکت‌ها مجازند (PERIOD_TRANSITIONS). هر چیز دیگری ایراد است، نه تصمیم.

دورهٔ قفل‌شده ویرایش و پرداخت را رد می‌کند. اصلاح‌ها در دورهٔ بعدی می‌روند، که حسابداری همین‌طور کار می‌کند — وقتی به آدم‌ها بابت ماه گذشته پول داده شده، برنمی‌گردی و عوضش نمی‌کنی. دوره تا وقتی کسی در آن طلب دارد قفل نمی‌شود و پیام ردّ، نام آن‌ها را می‌آورد.

بستن (close_period، «بستن دوره» در ربات، POST …/periods/{p}/close) این حالت‌ها را یک‌جا تا قفل طی می‌کند. اگر فیشی نیست، فیشی از عددهای دوره کهنه‌تر است یا کسی هنوز طلب دارد، رد می‌کند و هیچ‌چیز را جابه‌جا نمی‌کند.

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

خزانه

پولی که پیش از پرداخت به هر کسی کنار گذاشته می‌شود: ذخیرهٔ اضطراری، کنارگذاشتِ مالیات، صندوق توسعه.

صندوق یک کوزهٔ نام‌دار است. قاعده تغذیه‌اش می‌کند:

مبنا معنی
gross_percent درصدی از درآمد، پیش از هزینه‌ها
net_percent درصدی از سود، پس از هزینه‌ها
fixed مبلغی ثابت در هر دوره

قواعد بازهٔ تاریخ اثر دارند، پس «از مرداد شروع کردیم ۱۰٪ برداریم» قابل بیان است و دوره‌های سال گذشته دست‌نخورده می‌مانند.

دو قاعده دربارهٔ حذف

صندوقی که پول برداشته حذف نمی‌شود. دورهٔ پرداخت‌شده به جای خالی اشاره می‌کرد. غیرفعال‌کردنش جلوی برداشت بیشتر را می‌گیرد، و «حذف» وقتی پول جابه‌جا شده واقعاً یعنی همین.

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

همین عدم‌تقارن، کل طراحی در مقیاس کوچک است: تصمیم تغییرپذیر است، سندِ آنچه تصمیم گرفته شد نه.

سهم‌ها

یک ShareRule برای هر عضو، در هر دفتر، با تاریخ اثر:

مبنا قابل‌توزیع تقسیم می‌شود بر اساس
percent درصدی برای هرکس
fixed مبلغی ثابت برای هرکس
hours · days · points کارکرد ثبت‌شدهٔ آن دوره، ضرب در وزن قاعده
project مشارکت در پروژه

جایی که دو قاعده هم‌پوشانی دارند، آن که دیرتر شروع شده برنده است.

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

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

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

تا کسی سهم نداشته باشد پولی نمی‌گیرد. محاسبه‌ای بدون قاعدهٔ سهم هیچ فیشی تولید نمی‌کند — پس ربات از اجرایش سر باز می‌زند و به‌جایش صفحهٔ سهم‌ها را نشان می‌دهد، به‌جای برگرداندن نتیجهٔ خالی‌ای که شبیه موفقیت است.

کارکرد

سه مبنای سنجشی چیزی برای سنجیدن لازم دارند. ⏱ کارکرد روی صفحهٔ دوره ساعت، روز یا امتیاز هر عضو را ثبت می‌کند، و فقط کسانی را فهرست می‌کند که سهمشان واقعاً با سنجه پرداخت می‌شود — پرسیدن ساعت کارکرد از کسی که درصد ثابت دارد سؤالی است که جوابش به کار نمی‌آید.

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

سهم‌های وزنی آنچه را که پس از تسویهٔ ادعاهای ثابت و درصدی مانده تقسیم می‌کنند. دو نفر روی hours با وزن ۱، با ۱۲۰ و ۴۰ ساعت، سه‌چهارم و یک‌چهارم باقی‌مانده را می‌برند.

تعدیل‌ها

پاداش یا کسر روی پرداخت یک نفر، پیش از محاسبه.

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

در ربات برای پاداش ۲م تایپ می‌کنی و برای کسر -۵۰۰ک، و نوعش (bonus / penalty) از علامت درمی‌آید نه از سؤال دومی که همان را می‌پرسد.

تعدیل‌ها جدا از ثبت‌شدن تأیید می‌شوند، پس «مدیر پیشنهاد داد، مالک تأیید کرد» تمایز واقعی است.

محاسبه

calculate() برای هر عضو یک Payslip تولید می‌کند و هر ورودی را رویش منجمد می‌کند:

distributable_snapshot   چقدر برای تقسیم بود
share_basis_snapshot     چطور تقسیم شد
share_value_snapshot     سهم این شخص، همان‌طور که آن موقع بود
base_share               مبلغ حاصل
adjustments_total        چه اضافه یا کم شد
net_pay                  چقدر بدهکاریم

خواندن فیش سال گذشته نشان می‌دهد آن موقع چه تصمیمی گرفته شده، نه آنچه قواعد امروز تولید می‌کنند.

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

پرداخت

مرحله‌ای، چون قاعده همین است: سهم اغلب در چند انتقال پرداخت می‌شود.

GET  payslip → خالص ۴۰٬۰۰۰٬۰۰۰، پرداخت‌شده ۱۵٬۰۰۰٬۰۰۰، مانده ۲۵٬۰۰۰٬۰۰۰، بیش‌پرداخت ۰

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

ربات «پرداخت باقی‌مانده» را به‌صورت یک فشار پیشنهاد می‌دهد — با فاصله پرتکرارترین حالت، و تایپ دوبارهٔ عدد فرصتی برای غلط تایپ‌کردنش است — به‌علاوهٔ گزینهٔ پرداخت جزئی.

هر Payment مبلغ، تاریخ، ارز، نرخ و یک ارجاع اختیاری را ثبت می‌کند. ربات و API هر دو از همان pay() پرداخت می‌کنند که صاحب قاعده‌هاست: پرداخت به ارز خود فیش با نرخ ۱ است، هر نرخی هم که فرستاده شود؛ توکن نرخ می‌خواهد — که پیش از سنجیده‌شدن به همان چهار رقم اعشاری که ذخیره می‌شود گرد می‌شود، پس نرخی که برای ذخیره‌شدن بیش از حد کوچک است رد می‌شود نه اینکه صفر ذخیره شود — و دفتر باید توکنی را که می‌دهد داشته باشد.

چه کسی چه می‌بیند

دسترسی
MANAGE_PAYROLL باز کردن دوره، محاسبه، ثبت پرداخت، مدیریت صندوق
VIEW_OTHERS_PAY دیدن فیش همه
LOCK_PERIOD بستن دوره؛ فقط مالک

بدون VIEW_OTHERS_PAY، تابع payslips() فقط مال خودت را برمی‌گرداند. این در سرویس اعمال می‌شود، پس برای ربات و API یکسان برقرار است.

از داخل ربات

منوی دفتر → 👥 حقوق و سهم
              → ➕ دورهٔ <ماه>            باز کردن دوره
              → همان محاسبه، کامل
              → 🧮 محاسبهٔ فیش‌ها          محاسبه
              → 💵 فیش‌ها                  فیش‌ها
                  → 💵 پرداخت کامل        یا مبلغی جزئی
              → ➕ کسر/اضافه              تعدیل‌ها
              → 🏦 خزانه                  صندوق‌ها و قواعد

یک جزئیات تجزیه که ارزش دانستن دارد

درصد خزانه به‌عنوان عدد ساده تجزیه می‌شود، نه از راه تجزیه‌کنندهٔ مبلغ. تجزیه‌کنندهٔ مبلغ ۱۰م را ده میلیون می‌خواند — که قاعده‌ای برای ده میلیون درصد می‌ساخت. درصدها فقط رقم می‌پذیرند.