Improve FunZone sales/treasury sync and keep IBAN out of treasury reference.

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shayan Azadi
2026-07-11 16:53:03 +03:30
parent 367dc83ecb
commit bcd7a71c4f
35 changed files with 2912 additions and 965 deletions

View File

@@ -0,0 +1,511 @@
# مستند کامل سامانه حسابداری FunZone
این سند توضیح می‌دهد **اعداد و شماره‌ها چطور ساخته می‌شوند**، و **هر بخش منو چطور به بقیه وصل است** — از داشبورد تا تنظیمات.
---
## ۱. نمای کلی معماری
| لایه | تکنولوژی | آدرس تقریبی |
|------|----------|-------------|
| فرانت‌اند حسابداری | React + TypeScript + Vite | اپ `funzone-accounting` |
| API حسابداری | Django REST | معمولاً `localhost:8010/api` |
| API اصلی FunZone | Django (مشتری، پرداخت، مالک، رویداد) | معمولاً `localhost:8000/api` |
**منبع حقیقت داده‌ها:** `AppStore` همه موجودیت‌ها را از API حسابداری بارگذاری می‌کند و CRUD را از همان‌جا انجام می‌دهد. همگام‌سازی با FunZone (بلیت، کیف پول، برداشت) از API اصلی می‌خواند و در دفتر حسابداری ثبت می‌کند.
```
┌─────────────────┐ همگام‌سازی ┌──────────────────────┐
│ FunZone API │ ──────────────────► │ Accounting Frontend │
│ پرداخت/کیف‌پول │ │ AppStore + صفحات │
└─────────────────┘ └──────────┬───────────┘
│ REST
┌──────────────────────┐
│ Accounting Backend │
│ جداول دفتر / شماره سند│
└──────────────────────┘
```
---
## ۲. ساختار منو و مسیرها
| گروه | برچسب منو | مسیر | صفحه اصلی |
|------|-----------|------|-----------|
| اصلی | داشبورد | `/` | `Dashboard.tsx` |
| حسابداری | حساب‌ها (کدینگ) | `/accounts` | `Accounts.tsx` |
| حسابداری | اسناد حسابداری | `/vouchers` | `Vouchers.tsx` |
| عملیات | مشتریان و فروش | `/sales/*` | `Sales.tsx` + زیرصفحات |
| عملیات | تأمین‌کنندگان و خرید | `/purchases/*` | `Purchases.tsx` |
| عملیات | انبارداری | `/inventory` | `Inventory.tsx` |
| عملیات | دریافت و پرداخت | `/treasury` | `Treasury.tsx` |
| عملیات | حقوق و دستمزد | `/payroll` | `Payroll.tsx` |
| تحلیل | گزارش‌ها | `/reports` | `Reports.tsx` |
| سیستم | تنظیمات | `/settings` | `Settings.tsx` |
### زیرمنوی فروش
| مسیر | نقش |
|------|-----|
| `/sales` | فرایند فروش |
| `/sales/proforma` | پیش‌فاکتور |
| `/sales/invoice` | فاکتور فروش |
| `/sales/return` | برداشت‌ها و لغو شده |
| `/sales/adjustments` | اعلامیه بدهکار/بستانکار |
| `/sales/sales-types` | نوع فروش |
| `/sales/customers` | مشتریان |
| `/sales/review` | مرور فروش |
تعریف منو: `src/components/layout/navigation.ts`
---
## ۳. تولید اعداد و شماره‌ها (قلب سیستم)
### ۳.۱ الگوریتم مشترک فرانت‌اند
فایل: `src/utils/id.ts`
```ts
// شناسه موقت کلاینت (قبل از UUID سرور)
createId(prefix) مثلاً "v-" + زمان + تصادفی
// شماره ترتیبی سند/فاکتور/…
nextNumber(items) max(item.number) + 1
```
**نکات مهم:**
- هیچ جدول Sequence / سری شماره‌گذاری جدا در دیتابیس نیست.
- **سال مالی (`fiscalYear`) فقط نمایشی است** و شماره‌ها را ریست یا جدا نمی‌کند.
- برای بیشتر اسناد، شماره را **کلاینت** با `nextNumber` روی لیست فیلترشده می‌سازد و سرور همان عدد را ذخیره می‌کند.
- **استثنا: شماره سند حسابداری (`Voucher.number`)** — سرور شماره کلاینت را نادیده می‌گیرد و خودش اختصاص می‌دهد.
---
### ۳.۲ ماتریس شماره‌گذاری
| نوع سند | فیلد | محدوده سری | کی می‌سازد؟ | فایل / منطق |
|---------|------|------------|-------------|-------------|
| **سند حسابداری** | `number` | همه اسناد | **سرور** (`_next_number` + قفل ردیف) | `serializers.py` |
| **پیش‌فاکتور فروش** | `number` | فقط `documentType=proforma` و `kind=sale` | کلاینت | `SalesDocumentSection` |
| **فاکتور فروش** | `number` | فقط `documentType=invoice` و `kind=sale` | کلاینت | `SalesDocumentSection` + سینک FunZone |
| **فاکتور خرید** | `number` | همه `kind=purchase` | کلاینت | `TradeModule` |
| **دریافت** | `number` | فقط `kind=receipt` | کلاینت | `Treasury.tsx` / سینک خزانه |
| **پرداخت** | `number` | فقط `kind=payment` | کلاینت | `Treasury.tsx` / سینک |
| **برداشت کیف پول** | `number` | **کل** لیست `treasury` (نه فقط payment) | کلاینت | `withdrawalTreasury.ts` |
| **اعلامیه بدهکار/بستانکار** | `number` | همه اعلامیه‌ها | کلاینت | `PartyAdjustmentsPage` |
| **کد حساب** | `code` | دستی / یکتا در DB | کاربر | `Accounts.tsx` + seed |
| **کد کالا / نوع فروش / پرسنل** | `code` | دستی | کاربر | بدون auto |
| **فیش حقوقی** | — | ندارد | — | با `id` و `period` |
---
### ۳.۳ شماره سند حسابداری (تنها شماره قطعی سمت سرور)
در `funzone-accounting-backend/accounting/serializers.py`:
1. فیلد `number` در `VoucherSerializer` **read-only** است.
2. هنگام `create`، سرور با `select_for_update` آخرین شماره را می‌گیرد و `+1` می‌کند.
3. بعد از ایجاد، شماره تغییر نمی‌کند (`update` فیلد `number` را حذف می‌کند).
4. اگر سندی با `number <= 0` باشد، در update می‌تواند شماره بگیرد.
فرانت قبل از POST ممکن است شماره موقت با `nextNumber(data.vouchers)` نشان دهد؛ بعد از ذخیره، شماره واقعی همانی است که سرور برمی‌گرداند.
---
### ۳.۴ شناسه‌های داخلی (`createId`)
| پیشوند | موجودیت |
|--------|---------|
| `v-` | سند |
| `vl-` | سطر سند |
| `inv-` | فاکتور |
| `il-` | سطر فاکتور |
| `tr-` | دریافت/پرداخت |
| `acc-` | حساب |
| `prod-` | کالا |
| `party-` | طرف‌حساب |
| `st-` | نوع فروش |
| `adj-` | اعلامیه |
| `emp-` | کارمند |
| `ps-` | فیش حقوقی |
این‌ها شناسه موقت UI هستند؛ بعد از ایجاد در بک‌اند معمولاً با UUID سرور جایگزین می‌شوند.
---
### ۳.۵ تگ‌های پایدار `source` (برای جلوگیری از دوباره‌ثبتی)
| الگو | معنی |
|------|------|
| `invoice:{uuid}` | فاکتور دستی |
| `funzone:invoice:{paymentId}` | سند بلیت درگاه |
| `funzone:invoice:wallet:{paymentId}` | سند بلیت از کیف پول |
| `adjustment:{id}` | اعلامیه |
| `treasury:{id}` | خزانه دستی |
| `funzone:cust-pay:{id}` | پرداخت مشتری (قدیمی/خزانه) |
| `funzone:wallet-pay:{id}` | تراکنش کیف پول |
| `funzone:owner-txn:{id}` | تراکنش مالک |
| `withdrawal:{id}` | برداشت IBAN |
فایل کلیدی لینک بلیت↔خزانه: `src/pages/sales/funzoneVoucherLink.ts`
منبع حقیقت تقسیم کارمزد بلیت = **سند فاکتور فروش**؛ اگر سند خزانه تکراری برای همان پرداخت بماند، حذف می‌شود.
---
## ۴. کدینگ حساب‌ها و نقش هر کد
حساب‌های پیش‌فرض از `seeding.py` (نمونه مهم‌ها):
| کد | نام | نقش در سیستم |
|----|-----|--------------|
| 1001 | صندوق | نقدینگی داشبورد |
| 1002 | بانک ملت | بدهکار/بستانکار نقد در اسناد خودکار |
| 1003 | بانک سامان | نقدینگی |
| 1004 | دریافتنی مشتریان | فروش عادی، اعلامیه، تسویه خزانه |
| 1005 | موجودی کالا | فاکتور خرید |
| 2001 | پرداختنی تأمین‌کنندگان | خرید |
| 2002 | مالیات پرداختنی | ۱۰٪ بلیت FunZone |
| 2003 / 2004 | حقوق / بیمه | در کدینگ هست؛ حقوق هنوز سند نمی‌زند |
| 2005 | بدهی به مالکان (کیف پول) | ۷۶٪ سهم مالک |
| 2006 | بدهی به مشتریان (کیف پول) | شارژ/برداشت/بلیت از کیف |
| 4001 | درآمد فروش | فروش عادی + ۱۴٪ سود پلتفرم |
| 5002 | هزینه حقوق | رزرو شده؛ Payroll وصل نیست |
| 5007 | سهم مالکان | در تنظیمات مپ شده؛ مسیر فعلی بلیت بیشتر از 2005/4001/2002 استفاده می‌کند |
**تنظیمات نگاشت حساب‌ها** (`Settings``voucherAccounts`): بانک، بدهی مالک، بدهی مشتری، سهم مالک، درآمد فروش.
بدون کدینگ درست، `buildSalesVoucher` / `buildPurchaseVoucher` / `buildTreasuryVoucher` سند نمی‌سازند (`null`).
---
## ۵. فلوچارت اتصال ماژول‌ها
### ۵.۱ نمای کلی ارتباطات
```mermaid
flowchart TB
subgraph سیستم["سیستم"]
SET[تنظیمات<br/>نگاشت حساب‌ها / سال مالی نمایشی]
end
subgraph حسابداری["حسابداری"]
ACC[حساب‌ها / کدینگ]
VOC[اسناد حسابداری]
end
subgraph عملیات["عملیات"]
SAL[مشتریان و فروش]
PUR[تأمین‌کنندگان و خرید]
INV[انبارداری]
TRE[دریافت و پرداخت]
PAY[حقوق و دستمزد]
end
subgraph تحلیل["تحلیل"]
REP[گزارش‌ها]
DAS[داشبورد]
end
subgraph خارجی["خارج از اپ"]
FZ[FunZone API]
end
SET --> ACC
SET --> SAL
SET --> TRE
ACC --> VOC
ACC --> SAL
ACC --> PUR
ACC --> TRE
FZ -->|سینک طرف‌حساب| SAL
FZ -->|سینک بلیت / لغو| SAL
FZ -->|شارژ کیف / پرداخت مالک| TRE
FZ -->|رویداد / رزرو| INV
SAL -->|buildSalesVoucher + موجودی| VOC
SAL -->|اعلامیه| VOC
PUR -->|buildPurchaseVoucher + موجودی| VOC
PUR -->|افزایش stock| INV
SAL -->|کاهش stock| INV
TRE -->|buildTreasuryVoucher + تسویه فاکتور| VOC
TRE -->|reconcile paid/confirmed| SAL
PAY -.->|فعلاً بدون سند| VOC
VOC --> REP
VOC --> DAS
SAL --> DAS
PUR --> DAS
INV --> DAS
TRE --> DAS
```
### ۵.۲ جریان تولید سند از عملیات
```mermaid
flowchart LR
A[عملیات کاربر یا سینک FunZone] --> B{نوع؟}
B -->|فاکتور فروش/خرید| C[ذخیره Invoice]
B -->|دریافت/پرداخت| D[ذخیره TreasuryTxn]
B -->|اعلامیه| E[ذخیره PartyAdjustment]
B -->|سند دستی| F[Vouchers UI]
C --> G[invoiceSideEffects]
G --> H[تغییر موجودی کالا]
G --> I[buildInvoiceVoucher]
D --> J[buildTreasuryVoucher]
D --> K[reconcileInvoicePaymentStatus]
E --> L[buildAdjustmentVoucher]
F --> M[createDraft + nextNumber کلاینت]
I --> N[POST سند]
J --> N
L --> N
M --> N
N --> O[سرور: شماره قطعی Voucher]
O --> P[دفتر کل / تراز / داشبورد / گزارش]
```
### ۵.۳ جریان بلیت FunZone (تقسیم مبالغ)
برای مبلغ ناخالص `G`:
| جزء | نرخ | حساب بستانکار معمول |
|-----|-----|---------------------|
| مالیات | ۱۰٪ | 2002 |
| سود پلتفرم | ۱۴٪ | 4001 |
| سهم مالک | ۷۶٪ (باقیمانده) | 2005 |
**بدهکار:**
- درگاه: 1002 بانک
- کیف پول مشتری: 2006
تابع: `splitEventPayment` در `src/utils/funzoneFees.ts`
سند: `buildSalesVoucher` وقتی note شامل `funzone:pay:` است.
```mermaid
flowchart TB
P[پرداخت بلیت در FunZone] --> S[funzoneSalesSync]
S --> I[فاکتور فروش status=paid]
I --> V[سند با source=funzone:invoice:…]
V --> L1[بدهکار بانک یا کیف 2006]
V --> L2[بستانکار مالک 2005]
V --> L3[بستانکار درآمد 4001]
V --> L4[بستانکار مالیات 2002]
T[سند خزانه تکراری همان پرداخت] -.->|حذف توسط funzoneVoucherLink| X[حذف]
```
### ۵.۴ ترتیب همگام‌سازی یکپارچه
`unifiedVoucherSync.ts`:
1. سینک خزانه از FunZone (شارژ کیف، تراکنش مالک، …)
2. سینک فاکتورهای فروش بلیت + ثبت سند فاکتور
3. حذف اسناد تکراری / منسوخ (`supersededVoucherIds` + لینک بلیت)
---
## ۶. جزئیات هر بخش منو
### ۶.۱ داشبورد
**می‌خواند از:** اسناد posted، فاکتورها، موجودی کالا، حساب‌های نقدی.
**نمایش تقریبی:**
- نقدینگی (کدهای 10011003)
- جمع فروش / خرید از فاکتورها
- سود و زیان و تراز از `trialBalance` روی اسناد
- موجودی کم / فاکتورهای تسویه‌نشده
- نمودار چندماهه
**تولید عدد:** خودش شماره سند نمی‌سازد؛ فقط تجمیع می‌کند.
---
### ۶.۲ حساب‌ها (کدینگ)
**نقش:** درخت حساب‌ها (`parentId`)، مانده افتتاحیه، نوع (دارایی/بدهی/درآمد/هزینه).
**اتصال:** پایه همه سطرهای سند (`VoucherLine.accountId`). بدون حساب‌های اجباری، اسناد خودکار ساخته نمی‌شوند.
**شماره:** کد حساب دستی و یکتا — بدون `nextNumber`.
---
### ۶.۳ اسناد حسابداری
**ورودی‌ها:**
| منبع | چگونه |
|------|--------|
| دستی در همین صفحه | پیش‌نویس + ثبت |
| فروش | `buildSalesVoucher` |
| خرید | `buildPurchaseVoucher` |
| خزانه | `buildTreasuryVoucher` |
| اعلامیه | `buildAdjustmentVoucher` |
| دکمه همگام FunZone | `unifiedVoucherSync` |
**شماره:** سرور قطعی می‌کند.
**وضعیت:** `draft` | `posted` — گزارش‌ها معمولاً روی posted (منهای اسناد حذف‌شده/منسوخ).
---
### ۶.۴ مشتریان و فروش
**موجودیت‌ها:** Party (مشتری)، Invoice فروش، نوع فروش، اعلامیه، سینک FunZone.
| عمل | اثر حسابداری | اثر انبار |
|-----|--------------|-----------|
| پیش‌فاکتور | بدون سند | معمولاً بدون تغییر قطعی موجودی مسیر اصلی سند |
| فاکتور فروش عادی confirmed | بدهکار 1004 / بستانکار درآمد | کاهش stock |
| بلیت FunZone | تقسیم ۱۰/۱۴/۷۶ | — |
| لغو بلیت درگاه | بانک بدهکار / 2006 بستانکار | — |
| لغو بلیت کیف پول | حذف/بازگردانی سند قبلی | — |
| اعلامیه بدهکار | ↑ مطالبات / ↑ درآمد | — |
| اعلامیه بستانکار | عکس بالا | — |
**شماره فاکتور:** سری جدا برای پیش‌فاکتور و فاکتور (`nextNumber` روی همان `documentType`).
**تسویه:** خزانه با `reconcileInvoicePaymentStatus` به صورت FIFO وضعیت `confirmed``paid` را عوض می‌کند (بلیت‌های FunZone prepaid می‌مانند).
---
### ۶.۵ تأمین‌کنندگان و خرید
**موجودیت‌ها:** Party (supplier)، Invoice خرید.
**سند خودکار (confirmed):**
- بدهکار 1005 موجودی
- بستانکار 2001 پرداختنی
**شماره:** یک سری برای همه فاکتورهای خرید.
**اتصال به خزانه:** پرداخت به تأمین‌کننده از ماژول دریافت/پرداخت؛ تسویه فاکتور خرید از همان منطق reconcile.
---
### ۶.۶ انبارداری
**دو لایه:**
1. **موجودی کالا در حسابداری** — فیلد `Product.stock`؛ با تأیید فاکتور خرید/فروش عوض می‌شود (`applyStockChanges` / `stockDeltas`).
2. **نمای عملیاتی FunZone** — رویداد/رزرو زنده (`funzoneInventory.ts`**سند حسابداری نمی‌زند**.
یعنی انبار «دفتر انبار جدا» ندارد؛ موجودی از فاکتورها مشتق می‌شود + نمای FunZone برای عملیات.
---
### ۶.۷ دریافت و پرداخت (خزانه)
| نوع تراکنش | سند تقریبی |
|------------|------------|
| شارژ کیف پول مشتری | بدهکار بانک / بستانکار 2006 |
| برداشت کیف پول | بدهکار 2006 / بستانکار بانک |
| دریافت عادی از مشتری | بدهکار بانک / بستانکار 1004 |
| پرداخت به تأمین‌کننده/مالک | بدهکار 2005 یا 2001 / بستانکار بانک |
| مسیر قدیمی split روی خزانه | همان ۱۰/۱۴/۷۶ (امروز ترجیح با سند فاکتور بلیت است) |
**شماره:** دریافت و پرداخت سری جدا در UI؛ مسیر برداشت ممکن است از `nextNumber` روی **کل** خزانه استفاده کند.
---
### ۶.۸ حقوق و دستمزد
CRUD کارمند و فیش حقوقی.
**اتصال به اسناد: فعلاً وجود ندارد.** حساب‌های 2003/2004/5002 در کدینگ آماده هستند ولی Payroll سند ثبت نمی‌کند.
---
### ۶.۹ گزارش‌ها
از توابع `src/utils/accounting.ts`:
- تراز آزمایشی (`trialBalance`)
- دفتر حساب (`accountLedger`)
- سود و زیان (`incomeStatement`)
- ترازنامه (`balanceSheet`)
ورودی: حساب‌ها (مانده افتتاحیه) + اسناد posted (فیلتر اسناد منسوخ).
---
### ۶.۱۰ تنظیمات
- اطلاعات شرکت و **سال مالی نمایشی**
- نگاشت حساب‌های پیش‌فرض اسناد خودکار
- ریست/seed داده‌ها (در صورت فعال بودن در UI)
بدون نگاشت درست، زنجیره فروش/خزانه/بلیت می‌شکند.
---
## ۷. موجودیت‌ها و روابط
```
Account (درخت)
└── VoucherLine.accountId → Voucher
Party (customer | supplier ، گاهی externalId از FunZone)
├── Invoice.partyId
├── PartyAdjustment.partyId
└── TreasuryTxn.partyId
Invoice (sale | purchase)
└── Voucher با source پایدار
TreasuryTxn
└── Voucher با source خزانه / FunZone / withdrawal
Employee → Payslip (بدون Voucher)
CompanySettings (singleton) → FKs به Account
```
**وضعیت فاکتور:** `draft` | `confirmed` | `paid` | `cancelled`
**وضعیت سند:** `draft` | `posted`
---
## ۸. فایل‌های کلیدی (مرجع سریع)
| فایل | نقش |
|------|-----|
| `src/utils/id.ts` | `createId` / `nextNumber` |
| `src/pages/sales/salesUtils.ts` | همه سازنده‌های سند + تسویه + source |
| `src/pages/sales/invoiceEffects.ts` | اثر جانبی ذخیره فاکتور |
| `src/utils/funzoneFees.ts` | تقسیم ۱۰٪ / ۱۴٪ / ۷۶٪ |
| `src/pages/sales/funzoneVoucherLink.ts` | لینک بلیت و حذف سند تکراری خزانه |
| `src/accounting/unifiedVoucherSync.ts` | ارکستراسیون سینک |
| `src/accounting/voucherDedupe.ts` | حذف/مخفی اسناد تکراری |
| `src/pages/sales/funzoneSalesSync.ts` | پرداخت FunZone → فاکتور |
| `src/pages/treasury/funzoneTreasurySync.ts` | کیف پول / مالک → خزانه |
| `src/utils/accounting.ts` | محاسبات گزارش و تراز |
| `src/store/AppStore.tsx` | بارگذاری و CRUD |
| `backend/.../serializers.py` | شماره قطعی سند |
| `backend/.../seeding.py` | کدینگ اولیه |
> ⚠️ `voucherSync.ts` مسیر قدیمی است؛ برای جلوگیری از دوباره‌ثبتی از مسیر یکپارچه (`unifiedVoucherSync`) استفاده شود.
---
## ۹. جمع‌بندی قوانین شماره‌گذاری (برای تیم)
1. **فقط شماره سند حسابداری روی سرور قطعی است**؛ بقیه `max+1` سمت کلاینت روی لیست فیلترشده.
2. **سال مالی شماره‌ها را جدا یا ریست نمی‌کند.**
3. **پیش‌فاکتور و فاکتور فروش سری جدا** دارند؛ **خرید یک سری** دارد.
4. **دریافت و پرداخت در UI سری جدا** دارند؛ مسیر برداشت ممکن است با سری دیگر هم‌پوشانی عددی داشته باشد.
5. **بلیت FunZone:** فاکتور + سند با تگ `funzone:invoice:…`؛ تقسیم کارمزد روی همان سند؛ خزانه برای همان بلیت نباید دوبار ثبت شود.
6. **حقوق هنوز به دفتر کل وصل نیست.**
---
*آخرین به‌روزرسانی بر اساس کدبیس `funzone-accounting` و `funzone-accounting-backend`.*