# مستند دیتابیس MySQL

مرجع اجرایی: prisma/schema.prisma و sql/schema.sql. charset برابر utf8mb4 است. زمان‌ها UTC و مبالغ DECIMAL هستند. هیچ موجودی اولیه‌ای شرط ایجاد Trade نیست.

## User

کاربران و هش رمز؛ passwordHash هرگز در API نمایش داده نمی‌شود.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| name | String | بله |  |
| email | String | بله | یکتا |
| passwordHash | String | بله |  |
| role | Role | بله | دارای مقدار پیش‌فرض |
| active | Boolean | بله | دارای مقدار پیش‌فرض |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| sessions | Session[] | بله | رابطه SessionToUser |
| previews | TradePreview[] | بله | رابطه TradePreviewToUser |
| presets | ReportPreset[] | بله | رابطه ReportPresetToUser |
| textImports | TextImport[] | بله | رابطه TextImportToUser |
| textDrafts | TextDraft[] | بله | رابطه TextDraftToUser |
## Session

هش نشست قابل ابطال، زمان انقضا و کاربر.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی |
| userId | Int | بله |  |
| user | User | بله | رابطه SessionToUser |
| expiresAt | DateTime | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## Customer

مشخصات مشتری؛ با active غیرفعال می‌شود و حذف مالی ندارد.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| code | String | بله | یکتا |
| name | String | بله |  |
| phone | String | خیر |  |
| notes | String | خیر |  |
| active | Boolean | بله | دارای مقدار پیش‌فرض |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| updatedAt | DateTime | بله |  |
| trades | Trade[] | بله | رابطه CustomerToTrade |
| payments | Payment[] | بله | رابطه CustomerToPayment |
| entries | LedgerEntry[] | بله | رابطه CustomerToLedgerEntry |
| openings | OpeningBalance[] | بله | رابطه CustomerToOpeningBalance |
## Currency

کد سه‌حرفی ارز، اعشار و آخرین نرخ گزارش یورویی.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| code | String | بله | کلید اصلی |
| name | String | بله |  |
| decimals | Int | بله | دارای مقدار پیش‌فرض |
| active | Boolean | بله | دارای مقدار پیش‌فرض |
| eurRate | Decimal | خیر |  |
| rateUpdatedAt | DateTime | خیر |  |
| entries | LedgerEntry[] | بله | رابطه CurrencyToLedgerEntry |
| payments | Payment[] | بله | رابطه CurrencyToPayment |
| openings | OpeningBalance[] | بله | رابطه CurrencyToOpeningBalance |
| cashOpening | CashOpening | خیر | رابطه CashOpeningToCurrency |
| receivedTrades | Trade[] | بله | رابطه received |
| paidTrades | Trade[] | بله | رابطه paid |
## Setting

تنظیمات تک‌رکوردی با id=1.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| defaultFeePercent | Decimal | بله | دارای مقدار پیش‌فرض |
| cashEnabled | Boolean | بله | دارای مقدار پیش‌فرض |
| staleRateHours | Int | بله | دارای مقدار پیش‌فرض |
| updatedAt | DateTime | بله |  |
## Trade

تعهد معامله، snapshot نرخ و کارمزد و هزینه اختیاری.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| documentNumber | String | خیر | یکتا |
| resultSnapshot | Json | خیر |  |
| idempotencyKey | String | بله | یکتا |
| requestHash | String | بله |  |
| customerId | Int | بله |  |
| customer | Customer | بله | رابطه CustomerToTrade |
| receivedCurrency | String | بله |  |
| received | Currency | بله | رابطه received |
| paidCurrency | String | بله |  |
| paid | Currency | بله | رابطه paid |
| receivedAmount | Decimal | بله |  |
| paidAmount | Decimal | بله |  |
| agreedRate | Decimal | بله |  |
| receivedEurRate | Decimal | بله |  |
| paidEurRate | Decimal | بله |  |
| feePercent | Decimal | بله |  |
| feeAmount | Decimal | بله |  |
| feeEur | Decimal | بله |  |
| costBasisEur | Decimal | خیر |  |
| exchangeProfitEur | Decimal | خیر |  |
| status | Status | بله | دارای مقدار پیش‌فرض |
| notes | String | خیر |  |
| createdBy | Int | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| voidedAt | DateTime | خیر |  |
| voidReason | String | خیر |  |
| entries | LedgerEntry[] | بله | رابطه LedgerEntryToTrade |
| payments | Payment[] | بله | رابطه PaymentToTrade |
| allocations | PaymentAllocation[] | بله | رابطه PaymentAllocationToTrade |
## Payment

جابه‌جایی واقعی پول از/به مشتری؛ از معامله مستقل است.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| documentNumber | String | خیر | یکتا |
| resultSnapshot | Json | خیر |  |
| allocationVersion | Int | بله | دارای مقدار پیش‌فرض |
| idempotencyKey | String | بله | یکتا |
| requestHash | String | بله |  |
| customerId | Int | بله |  |
| customer | Customer | بله | رابطه CustomerToPayment |
| tradeId | String | خیر |  |
| trade | Trade | خیر | رابطه PaymentToTrade |
| currencyCode | String | بله |  |
| currency | Currency | بله | رابطه CurrencyToPayment |
| direction | Direction | بله |  |
| amount | Decimal | بله |  |
| eurRate | Decimal | بله |  |
| notes | String | خیر |  |
| status | Status | بله | دارای مقدار پیش‌فرض |
| createdBy | Int | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| voidedAt | DateTime | خیر |  |
| voidReason | String | خیر |  |
| entries | LedgerEntry[] | بله | رابطه LedgerEntryToPayment |
| allocations | PaymentAllocation[] | بله | رابطه PaymentToPaymentAllocation |
## OpeningBalance

مانده اولیه اختیاری هر مشتری/ارز.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| customerId | Int | بله |  |
| customer | Customer | بله | رابطه CustomerToOpeningBalance |
| currencyCode | String | بله |  |
| currency | Currency | بله | رابطه CurrencyToOpeningBalance |
| amount | Decimal | بله |  |
| eurRate | Decimal | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| createdBy | Int | بله |  |
| entries | LedgerEntry[] | بله | رابطه LedgerEntryToOpeningBalance |
## LedgerEntry

گردش امضادار حساب مشتری؛ مثبت بدهی مشتری، منفی طلب او.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| customerId | Int | بله |  |
| customer | Customer | بله | رابطه CustomerToLedgerEntry |
| currencyCode | String | بله |  |
| currency | Currency | بله | رابطه CurrencyToLedgerEntry |
| amount | Decimal | بله |  |
| eurRate | Decimal | بله |  |
| tradeId | String | خیر |  |
| trade | Trade | خیر | رابطه LedgerEntryToTrade |
| paymentId | String | خیر |  |
| payment | Payment | خیر | رابطه LedgerEntryToPayment |
| openingId | Int | خیر |  |
| opening | OpeningBalance | خیر | رابطه LedgerEntryToOpeningBalance |
| reversalOf | Int | خیر | یکتا |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## CashOpening

موجودی اختیاری قبل از تمام پرداخت‌های سیستم.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| currencyCode | String | بله | کلید اصلی |
| currency | Currency | بله | رابطه CashOpeningToCurrency |
| amount | Decimal | بله |  |
| createdBy | Int | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## AuditLog

تاریخچه عملیات مهم بدون رمز یا توکن.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | Int | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| actorId | Int | بله |  |
| action | String | بله |  |
| entityId | String | بله |  |
| detail | Json | خیر |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## DocumentCounter

شمارنده اتمیک شماره سند؛ هرگز ریست نشود.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| key | String | بله | کلید اصلی |
| value | Int | بله | دارای مقدار پیش‌فرض |
## TradePreview

پیش‌نمایش پنج‌دقیقه‌ای متعلق به کاربر؛ بدون گردش مالی.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| userId | Int | بله |  |
| user | User | بله | رابطه TradePreviewToUser |
| inputHash | String | بله |  |
| pricingHash | String | بله |  |
| expiresAt | DateTime | بله |  |
| consumedAt | DateTime | خیر |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## PaymentAllocation

تخصیص پرداخت به تعهد معامله؛ بدون گردش مالی اضافه.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| paymentId | String | بله |  |
| payment | Payment | بله | رابطه PaymentToPaymentAllocation |
| tradeId | String | بله |  |
| trade | Trade | بله | رابطه PaymentAllocationToTrade |
| amount | Decimal | بله |  |
| createdBy | Int | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## ReportPreset

تنظیم گزارش خصوصی هر کاربر همراه کنترل نسخه.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| userId | Int | بله |  |
| user | User | بله | رابطه ReportPresetToUser |
| name | String | بله |  |
| report | String | بله |  |
| config | Json | بله |  |
| version | Int | بله | دارای مقدار پیش‌فرض |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| updatedAt | DateTime | بله |  |
## TextImport

ثبت اتمیک گروه متن: مالک، کلید تکرار، متن اصلی و نتیجه تاریخی. وضعیت زنده اسناد جدا خوانده می‌شود.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| userId | Int | بله |  |
| user | User | بله | رابطه TextImportToUser |
| idempotencyKey | String | بله | یکتا |
| requestHash | String | بله |  |
| sourceText | String | بله |  |
| recordCount | Int | بله |  |
| result | Json | بله |  |
| references | TextImportReference[] | بله | رابطه TextImportToTextImportReference |
| drafts | TextDraft[] | بله | رابطه TextDraftToTextImport |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## TextImportReference

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

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| reference | String | بله | کلید اصلی |
| importId | String | بله |  |
| import | TextImport | بله | رابطه TextImportToTextImportReference |
| recordIndex | Int | بله |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
## TextDraft

پیش‌نویس خصوصی قابل ویرایش تا ثبت؛ کنترل نسخه و ارجاع به نتیجه ثبت. وضعیت از وجود importId مشخص می‌شود.

| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| id | String | بله | کلید اصلی؛ دارای مقدار پیش‌فرض |
| userId | Int | بله |  |
| user | User | بله | رابطه TextDraftToUser |
| title | String | بله |  |
| text | String | بله |  |
| version | Int | بله | دارای مقدار پیش‌فرض |
| importId | String | خیر |  |
| import | TextImport | خیر | رابطه TextDraftToTextImport |
| commitKey | String | خیر |  |
| committedVersion | Int | خیر |  |
| createdAt | DateTime | بله | دارای مقدار پیش‌فرض |
| updatedAt | DateTime | بله |  |

## محدودیت‌های لایه برنامه

رابطه createdBy و actorId به‌صورت شناسه ثبت می‌شود؛ API حذف کاربر ندارد. برای reversalOf قید unique وجود دارد و ایجاد برگشت فقط در سرویس ابطال انجام می‌شود. قاعده علامت مبلغ و یک‌منبعی بودن LedgerEntry در سرویس اعمال می‌شود، نه CHECK مستقل SQL. مستقیم در جدول‌ها داده مالی ننویسید.

اسناد مالی در تراکنش Serializable با ثبت هم‌زمان گردش و Audit ساخته می‌شوند. کلید Idempotency-Key در سطح هر جدول یکتاست.
