# مرجع تمام روت‌های API

این فایل از تعریف واقعی روت‌ها تولید شده است. بدنه و پارامترهای دقیق هر روت در بخش Schema آمده‌اند. در مرورگر /docs را باز کنید و با توکن ورود Authorize کنید.

اعداد مالی باید رشته باشند؛ خطاها با error.message و پاسخ موفق با data برمی‌گردند. زمان‌ها UTC هستند؛ from شامل و to غیرشامل است. وضعیت‌های خطا: 400 اعتبارسنجی، 401 ورود، 403 دسترسی، 404 یافت نشد، 409 تعارض، 429 محدودیت درخواست، 500 خطای داخلی و 503 دیتابیس.

| روش | مسیر | دسترسی | کارکرد |
|---|---|---|---|
| GET | /health | Public | Liveness (no database dependency) |
| GET | /ready | Public | Database readiness |
| POST | /api/auth/login | Public | Login |
| GET | /api/auth/me | ADMIN, CASHIER, VIEWER | Current user |
| POST | /api/auth/logout | ADMIN, CASHIER, VIEWER | Revoke current session |
| POST | /api/auth/logout-all | ADMIN, CASHIER, VIEWER | Revoke all my sessions |
| POST | /api/auth/change-password | ADMIN, CASHIER, VIEWER | Change password and revoke all sessions |
| GET | /api/users | ADMIN | List users |
| POST | /api/users | ADMIN | Create user |
| PATCH | /api/users/:id | ADMIN | Edit user; self role/deactivation forbidden |
| POST | /api/users/:id/reset-password | ADMIN | Admin password reset; revoke sessions |
| GET | /api/customers | ADMIN, CASHIER, VIEWER | Search customers |
| POST | /api/customers | ADMIN, CASHIER | Create customer |
| GET | /api/customers/:id | ADMIN, CASHIER, VIEWER | Customer detail |
| PATCH | /api/customers/:id | ADMIN, CASHIER | Edit or deactivate customer |
| GET | /api/customers/:id/balances | ADMIN, CASHIER, VIEWER | Current customer debt/credit by currency |
| GET | /api/customers/:id/statement | ADMIN, CASHIER, VIEWER | Customer ledger with opening and running balances |
| GET | /api/customers/:id/profit | ADMIN, CASHIER, VIEWER | Profit attributable to customer |
| POST | /api/customers/:id/opening-balances | ADMIN | Set optional initial customer debt/credit once per currency |
| GET | /api/currencies | ADMIN, CASHIER, VIEWER | List currencies and reporting rates |
| POST | /api/currencies | ADMIN | Create currency |
| PATCH | /api/currencies/:code | ADMIN | Edit currency name/active flag |
| PUT | /api/currencies/:code/rate | ADMIN, CASHIER | Set reporting rate: EUR per 1 unit of currency |
| GET | /api/settings | ADMIN, CASHIER, VIEWER | Read settings |
| PATCH | /api/settings | ADMIN | Update fee default and optional cash reporting |
| GET | /api/trades | ADMIN, CASHIER, VIEWER | List trades |
| GET | /api/trades/:id | ADMIN, CASHIER, VIEWER | Get trade with ledger entries |
| POST | /api/trades | ADMIN, CASHIER | Create trade |
| POST | /api/trades/:id/void | ADMIN | Void trade with reversal entries |
| GET | /api/payments | ADMIN, CASHIER, VIEWER | List payments |
| GET | /api/payments/:id | ADMIN, CASHIER, VIEWER | Get payment with ledger entries |
| POST | /api/payments | ADMIN, CASHIER | Create payment |
| POST | /api/payments/:id/void | ADMIN | Void payment with reversal entries |
| GET | /api/reports/profit | ADMIN, CASHIER, VIEWER | Aggregate accrued profit report |
| GET | /api/reports/customer-profit | ADMIN, CASHIER, VIEWER | Paged customer profit breakdown |
| GET | /api/reports/balances | ADMIN, CASHIER, VIEWER | Paged current customer balances |
| GET | /api/reports/dashboard | ADMIN, CASHIER, VIEWER | Dashboard counts, profit and optional cash |
| GET | /api/cash | ADMIN, CASHIER, VIEWER | Optional cash balances and completeness |
| POST | /api/cash/openings | ADMIN | Record cash balance at system start (optional) |
| GET | /api/audit-logs | ADMIN | Read immutable audit history |
| GET | /api/reports/catalog | ADMIN, CASHIER, VIEWER | فهرست گزارش‌های پویا |
| GET | /api/reports/trades/options | ADMIN, CASHIER, VIEWER | فیلدها و فیلترهای مجاز: معاملات |
| POST | /api/reports/trades | ADMIN, CASHIER, VIEWER | معاملات با فیلتر و صفحه‌بندی |
| GET | /api/reports/payments/options | ADMIN, CASHIER, VIEWER | فیلدها و فیلترهای مجاز: دریافت و پرداخت |
| POST | /api/reports/payments | ADMIN, CASHIER, VIEWER | دریافت و پرداخت با فیلتر و صفحه‌بندی |
| GET | /api/reports/statement/options | ADMIN, CASHIER, VIEWER | فیلدها و فیلترهای مجاز: گردش حساب |
| POST | /api/reports/statement | ADMIN, CASHIER, VIEWER | گردش حساب با فیلتر و صفحه‌بندی |
| GET | /api/reports/balances/options | ADMIN, CASHIER, VIEWER | فیلدها و فیلترهای مجاز: مانده بدهی و طلب |
| POST | /api/reports/balances | ADMIN, CASHIER, VIEWER | مانده بدهی و طلب با فیلتر و صفحه‌بندی |
| GET | /api/reports/customer-profit/options | ADMIN, CASHIER, VIEWER | فیلدها و فیلترهای مجاز: سود مشتریان |
| POST | /api/reports/customer-profit | ADMIN, CASHIER, VIEWER | سود مشتریان با فیلتر و صفحه‌بندی |
| POST | /api/trades/preview | ADMIN, CASHIER | پیش‌نمایش اختیاری معامله |
| GET | /api/trades/:id/settlement | ADMIN, CASHIER, VIEWER | وضعیت تسویه دو سمت معامله |
| GET | /api/payments/:id/allocations | ADMIN, CASHIER, VIEWER | تخصیص‌ها و مانده تخصیص‌نیافته پرداخت |
| PUT | /api/payments/:id/allocations | ADMIN, CASHIER | اصلاح تخصیص بدون تغییر گردش مالی |
| GET | /api/trades/:id/receipt | ADMIN, CASHIER, VIEWER | رسید trade |
| GET | /api/payments/:id/receipt | ADMIN, CASHIER, VIEWER | رسید payment |
| POST | /api/reports/trades/export | ADMIN, CASHIER, VIEWER | خروجی CSV trades |
| POST | /api/reports/payments/export | ADMIN, CASHIER, VIEWER | خروجی CSV payments |
| POST | /api/reports/statement/export | ADMIN, CASHIER, VIEWER | خروجی CSV statement |
| POST | /api/reports/balances/export | ADMIN, CASHIER, VIEWER | خروجی CSV balances |
| POST | /api/reports/customer-profit/export | ADMIN, CASHIER, VIEWER | خروجی CSV customer-profit |
| GET | /api/report-presets | ADMIN, CASHIER, VIEWER | تنظیمات گزارش شخصی |
| POST | /api/report-presets | ADMIN, CASHIER, VIEWER | ذخیره تنظیم گزارش |
| GET | /api/report-presets/:id | ADMIN, CASHIER, VIEWER | جزئیات تنظیم شخصی |
| PATCH | /api/report-presets/:id | ADMIN, CASHIER, VIEWER | ویرایش تنظیم شخصی با کنترل نسخه |
| DELETE | /api/report-presets/:id | ADMIN, CASHIER, VIEWER | حذف تنظیم شخصی |
| POST | /api/report-presets/:id/run | ADMIN, CASHIER, VIEWER | اجرای تنظیم ذخیره‌شده |
| GET | /api/trade-text/template | ADMIN, CASHIER, VIEWER | قالب ثبت متنی معامله |
| POST | /api/trade-text/preview | ADMIN, CASHIER | بررسی و پیش‌نمایش متن معامله |
| POST | /api/trade-text | ADMIN, CASHIER | ثبت معامله از متن قالب‌دار |
| GET | /api/text-imports/templates | ADMIN, CASHIER, VIEWER | قالب‌های کامل عملیات متنی |
| POST | /api/text-imports/parse | ADMIN, CASHIER | تجزیه متن بدون مراجعه به دیتابیس کسب‌وکار |
| POST | /api/text-imports/validate | ADMIN, CASHIER | اعتبارسنجی همه رکوردها و محاسبه اثر مالی |
| POST | /api/text-imports/preview | ADMIN, CASHIER | پیش‌نمایش قابل تأیید با اعتبار پنج دقیقه |
| POST | /api/text-imports | ADMIN, CASHIER | ثبت اتمیک متن تکی یا گروهی |
| GET | /api/text-imports | ADMIN, CASHIER | تاریخچه ثبت‌های متنی خود کاربر |
| GET | /api/text-imports/:id | ADMIN, CASHIER | متن اصلی و نتیجه ثبت همراه وضعیت جاری اسناد |
| POST | /api/text-drafts | ADMIN, CASHIER | ذخیره پیش‌نویس متنی بدون ثبت مالی |
| GET | /api/text-drafts | ADMIN, CASHIER | فهرست پیش‌نویس‌های شخصی |
| GET | /api/text-drafts/:id | ADMIN, CASHIER | خواندن متن و نسخه پیش‌نویس |
| PATCH | /api/text-drafts/:id | ADMIN, CASHIER | ویرایش پیش‌نویس با کنترل نسخه |
| DELETE | /api/text-drafts/:id | ADMIN, CASHIER | حذف پیش‌نویس ثبت‌نشده |
| POST | /api/text-drafts/:id/preview | ADMIN, CASHIER | بررسی پیش‌نویس و ساخت پیش‌نمایش |
| POST | /api/text-drafts/:id/commit | ADMIN, CASHIER | تأیید نهایی پیش‌نویس |

## GET /health

Liveness (no database dependency)



پاسخ داخل data (رسید HTML استثنا است): { status: ok }


## GET /ready

Database readiness



پاسخ داخل data (رسید HTML استثنا است): { status: ready }


## POST /api/auth/login

Login



پاسخ داخل data (رسید HTML استثنا است): { token, expiresAt, user: { id, name, email, role, active, createdAt } }

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "maxLength": 190
    },
    "password": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    }
  },
  "required": [
    "email",
    "password"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "email": "admin@example.com",
  "password": "YourConfiguredPassword"
}
```

## GET /api/auth/me

Current user



پاسخ داخل data (رسید HTML استثنا است): User without passwordHash


## POST /api/auth/logout

Revoke current session



پاسخ داخل data (رسید HTML استثنا است): { loggedOut: true }


## POST /api/auth/logout-all

Revoke all my sessions



پاسخ داخل data (رسید HTML استثنا است): { loggedOut: true }


## POST /api/auth/change-password

Change password and revoke all sessions



پاسخ داخل data (رسید HTML استثنا است): { loginRequired: true }

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "currentPassword": {
      "type": "string",
      "maxLength": 128
    },
    "newPassword": {
      "type": "string",
      "minLength": 12,
      "maxLength": 128
    }
  },
  "required": [
    "currentPassword",
    "newPassword"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "currentPassword": "{{adminPassword}}",
  "newPassword": "{{adminPassword}}"
}
```

## GET /api/users

List users



پاسخ داخل data (رسید HTML استثنا است): Array of safe user objects

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  }
]
```

## POST /api/users

Create user



پاسخ داخل data (رسید HTML استثنا است): User without passwordHash

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 120
    },
    "email": {
      "type": "string",
      "format": "email",
      "maxLength": 190
    },
    "password": {
      "type": "string",
      "minLength": 12,
      "maxLength": 128
    },
    "role": {
      "type": "string",
      "enum": [
        "ADMIN",
        "CASHIER",
        "VIEWER"
      ]
    }
  },
  "required": [
    "name",
    "email",
    "password",
    "role"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "name": "Example Name",
  "email": "{{adminEmail}}",
  "password": "{{adminPassword}}",
  "role": "CASHIER"
}
```

## PATCH /api/users/:id

Edit user; self role/deactivation forbidden



پاسخ داخل data (رسید HTML استثنا است): User without passwordHash

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 120
    },
    "role": {
      "type": "string",
      "enum": [
        "ADMIN",
        "CASHIER",
        "VIEWER"
      ]
    },
    "active": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "name": "Example Name",
  "role": "CASHIER",
  "active": true
}
```

## POST /api/users/:id/reset-password

Admin password reset; revoke sessions



پاسخ داخل data (رسید HTML استثنا است): { updated: true }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "newPassword": {
      "type": "string",
      "minLength": 12,
      "maxLength": 128
    }
  },
  "required": [
    "newPassword"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "newPassword": "{{adminPassword}}"
}
```

## GET /api/customers

Search customers



پاسخ داخل data (رسید HTML استثنا است): { items: Customer[], total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "q",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "maxLength": 150
    }
  }
]
```

## POST /api/customers

Create customer



پاسخ داخل data (رسید HTML استثنا است): Customer

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40
    },
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 150
    },
    "phone": {
      "type": "string",
      "maxLength": 40,
      "nullable": true
    },
    "notes": {
      "type": "string",
      "maxLength": 2000,
      "nullable": true
    },
    "active": {
      "type": "boolean"
    }
  },
  "required": [
    "code",
    "name"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "code": "C001",
  "name": "Ahmad",
  "phone": "+93700000000"
}
```

## GET /api/customers/:id

Customer detail



پاسخ داخل data (رسید HTML استثنا است): Customer

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```

## PATCH /api/customers/:id

Edit or deactivate customer



پاسخ داخل data (رسید HTML استثنا است): Customer

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40
    },
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 150
    },
    "phone": {
      "type": "string",
      "maxLength": 40,
      "nullable": true
    },
    "notes": {
      "type": "string",
      "maxLength": 2000,
      "nullable": true
    },
    "active": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "code": "C001",
  "name": "Example Name",
  "phone": "example",
  "notes": "example",
  "active": true
}
```

## GET /api/customers/:id/balances

Current customer debt/credit by currency

Positive amount: customer owes exchange. Negative: exchange owes customer. EUR equivalent uses current saved rate; null if missing.

پاسخ داخل data (رسید HTML استثنا است): [{ customerId, currencyCode, amount, side, eurEquivalent, eurRate, rateUpdatedAt }]

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```

## GET /api/customers/:id/statement

Customer ledger with opening and running balances

Ordered ascending by ledger ID; from inclusive, to exclusive UTC. runningBalances include entries before page and from. Reversals stay visible.

پاسخ داخل data (رسید HTML استثنا است): { items: (LedgerEntry & { runningBalance })[], page, limit, openingBeforePeriod: grouped balances, total }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  },
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  }
]
```

## GET /api/customers/:id/profit

Profit attributable to customer



پاسخ داخل data (رسید HTML استثنا است): ProfitSummary

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  }
]
```

## POST /api/customers/:id/opening-balances

Set optional initial customer debt/credit once per currency

Positive amount means customer owes exchange. Allowed only before any ledger entries for that customer/currency. Not cash inventory.

پاسخ داخل data (رسید HTML استثنا است): OpeningBalance

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "currencyCode": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "amount": {
      "type": "string",
      "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "eurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    }
  },
  "required": [
    "currencyCode",
    "amount"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "currencyCode": "USD",
  "amount": "100",
  "eurRate": "0.92"
}
```

## GET /api/currencies

List currencies and reporting rates



پاسخ داخل data (رسید HTML استثنا است): Currency[]


## POST /api/currencies

Create currency



پاسخ داخل data (رسید HTML استثنا است): Currency

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 80
    },
    "decimals": {
      "type": "integer",
      "minimum": 0,
      "maximum": 8,
      "default": 2
    },
    "eurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    }
  },
  "required": [
    "code",
    "name"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "code": "USD",
  "name": "Example Name",
  "decimals": 2,
  "eurRate": "0.92"
}
```

## PATCH /api/currencies/:code

Edit currency name/active flag



پاسخ داخل data (رسید HTML استثنا است): Currency

پارامترها:

```json
[
  {
    "name": "code",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 80
    },
    "active": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "name": "Example Name",
  "active": true
}
```

## PUT /api/currencies/:code/rate

Set reporting rate: EUR per 1 unit of currency

Existing document snapshots do not change. Current balance valuations do change.

پاسخ داخل data (رسید HTML استثنا است): Currency

پارامترها:

```json
[
  {
    "name": "code",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "eurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    }
  },
  "required": [
    "eurRate"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "eurRate": "0.92"
}
```

## GET /api/settings

Read settings



پاسخ داخل data (رسید HTML استثنا است): { id, defaultFeePercent, cashEnabled, updatedAt, baseCurrency }


## PATCH /api/settings

Update fee default and optional cash reporting



پاسخ داخل data (رسید HTML استثنا است): Setting

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "defaultFeePercent": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,2})(\\.\\d{1,4})?$"
    },
    "cashEnabled": {
      "type": "boolean"
    },
    "staleRateHours": {
      "type": "integer",
      "minimum": 1,
      "maximum": 720
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "defaultFeePercent": "0.5",
  "cashEnabled": true,
  "staleRateHours": "0.92"
}
```

## GET /api/trades

List trades



پاسخ داخل data (رسید HTML استثنا است): { items, total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "customerId",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  },
  {
    "name": "status",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "enum": [
        "POSTED",
        "VOID"
      ]
    }
  }
]
```

## GET /api/trades/:id

Get trade with ledger entries



پاسخ داخل data (رسید HTML استثنا است): trade with entries[]

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## POST /api/trades

Create trade

Creates obligations, NOT cash movements. Fee is added in received currency. paidAmount = receivedAmount × agreedRate rounded to paid currency precision. costBasisEur is optional total historical cost of the paid asset.

پاسخ داخل data (رسید HTML استثنا است): trade

پارامترها:

```json
[
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 8,
      "maxLength": 100
    },
    "example": "example-key-0001"
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "customerId": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    },
    "receivedCurrency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "paidCurrency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "receivedAmount": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "agreedRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "receivedEurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "paidEurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "feePercent": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,2})(\\.\\d{1,4})?$"
    },
    "costBasisEur": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "notes": {
      "type": "string",
      "maxLength": 2000
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "customerId",
    "receivedCurrency",
    "paidCurrency",
    "receivedAmount",
    "agreedRate"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "customerId": 1,
  "receivedCurrency": "USD",
  "paidCurrency": "EUR",
  "receivedAmount": "1000",
  "agreedRate": "0.91",
  "receivedEurRate": "0.92",
  "feePercent": "0.5"
}
```

## POST /api/trades/:id/void

Void trade with reversal entries

No hard delete. Repeat void does not duplicate reversals. Void referenced payments before a trade.

پاسخ داخل data (رسید HTML استثنا است): trade

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string",
      "minLength": 3,
      "maxLength": 500
    }
  },
  "required": [
    "reason"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "reason": "Correction of incorrect entry"
}
```

## GET /api/payments

List payments



پاسخ داخل data (رسید HTML استثنا است): { items, total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "customerId",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  },
  {
    "name": "status",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "enum": [
        "POSTED",
        "VOID"
      ]
    }
  }
]
```

## GET /api/payments/:id

Get payment with ledger entries



پاسخ داخل data (رسید HTML استثنا است): payment with entries[]

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## POST /api/payments

Create payment

Records actual money movement once. AUTO allocation (default) settles matching open obligations; tradeId restricts AUTO to that trade. NONE keeps it unallocated. Currency/direction must match the reference. Overpayment stays on the customer account. Creation snapshots are historical.

پاسخ داخل data (رسید HTML استثنا است): payment

پارامترها:

```json
[
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 8,
      "maxLength": 100
    },
    "example": "example-key-0001"
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "customerId": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    },
    "tradeId": {
      "type": "string",
      "format": "uuid"
    },
    "currencyCode": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "direction": {
      "type": "string",
      "enum": [
        "RECEIVE",
        "PAY"
      ]
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "eurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "notes": {
      "type": "string",
      "maxLength": 2000
    },
    "allocationMode": {
      "type": "string",
      "enum": [
        "AUTO",
        "NONE"
      ],
      "default": "AUTO"
    }
  },
  "required": [
    "customerId",
    "currencyCode",
    "direction",
    "amount"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "customerId": 1,
  "currencyCode": "USD",
  "direction": "RECEIVE",
  "amount": "1005",
  "eurRate": "0.92"
}
```

## POST /api/payments/:id/void

Void payment with reversal entries

No hard delete. Repeat void does not duplicate reversals. Void referenced payments before a trade.

پاسخ داخل data (رسید HTML استثنا است): payment

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string",
      "minLength": 3,
      "maxLength": 500
    }
  },
  "required": [
    "reason"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "reason": "Correction of incorrect entry"
}
```

## GET /api/reports/profit

Aggregate accrued profit report

feeRevenueEur includes all posted trade fees; knownExchangeProfitEur includes only known cost basis. totalProfitEur is null if any trade profit is unknown. No operating expenses included.

پاسخ داخل data (رسید HTML استثنا است): ProfitSummary

پارامترها:

```json
[
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "customerId",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```

## GET /api/reports/customer-profit

Paged customer profit breakdown

Customer ID order, not a profit ranking.

پاسخ داخل data (رسید HTML استثنا است): [{ customer, profit: ProfitSummary }]

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  }
]
```

## GET /api/reports/balances

Paged current customer balances

Pagination applies to customers. Do not net different currencies automatically.

پاسخ داخل data (رسید HTML استثنا است): Array of per-customer per-currency balances

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  }
]
```

## GET /api/reports/dashboard

Dashboard counts, profit and optional cash



پاسخ داخل data (رسید HTML استثنا است): { customers, trades, profit: ProfitSummary, cash }


## GET /api/cash

Optional cash balances and completeness

No inventory enforcement. Balance remains null without opening, but net movement is available. Includes only posted payments.

پاسخ داخل data (رسید HTML استثنا است): { enabled, balances: [{ currencyCode, opening, netMovement, balance, complete }] }


## POST /api/cash/openings

Record cash balance at system start (optional)

One immutable opening per currency. May be entered later but must describe balance BEFORE all recorded payments. No automatic valuation/cost averaging.

پاسخ داخل data (رسید HTML استثنا است): CashOpening

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "currencyCode": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "amount": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    }
  },
  "required": [
    "currencyCode",
    "amount"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "currencyCode": "USD",
  "amount": "100"
}
```

## GET /api/audit-logs

Read immutable audit history



پاسخ داخل data (رسید HTML استثنا است): AuditLog[]

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "from",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  },
  {
    "name": "to",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "date-time"
    }
  }
]
```

## GET /api/reports/catalog

فهرست گزارش‌های پویا



پاسخ داخل data (رسید HTML استثنا است): Report names, labels and options URLs


## GET /api/reports/trades/options

فیلدها و فیلترهای مجاز: معاملات



پاسخ داخل data (رسید HTML استثنا است): ReportOptions


## POST /api/reports/trades

معاملات با فیلتر و صفحه‌بندی

Read-only POST. No idempotency key needed. Decimal strings, UTC dates, from inclusive and to/asOf exclusive. Options endpoint describes accepted fields. All staff roles may view financial data. Unknown filters/fields are rejected. Rows, total count and summaries share a database snapshot.

پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "nullable": true
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "report",
        "page",
        "limit",
        "totalItems",
        "totalPages",
        "hasNextPage",
        "hasPreviousPage",
        "baseCurrency",
        "timezone"
      ],
      "properties": {
        "report": {
          "type": "string"
        },
        "page": {
          "type": "integer"
        },
        "limit": {
          "type": "integer"
        },
        "totalItems": {
          "type": "integer"
        },
        "totalPages": {
          "type": "integer"
        },
        "hasNextPage": {
          "type": "boolean"
        },
        "hasPreviousPage": {
          "type": "boolean"
        },
        "outOfRange": {
          "type": "boolean"
        },
        "baseCurrency": {
          "type": "string",
          "enum": [
            "EUR"
          ]
        },
        "timezone": {
          "type": "string",
          "enum": [
            "UTC"
          ]
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "filters": {
          "type": "object"
        },
        "search": {
          "type": "string"
        },
        "sort": {
          "type": "object"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "valuation": {
          "type": "string"
        }
      }
    },
    "summary": {
      "type": "object",
      "description": "All filtered results, never just this page. Financial totals exclude VOID. Monetary values are strings; unknown profit is null.",
      "additionalProperties": true
    },
    "columns": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "sortable": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "currencyField": {
            "type": "string"
          }
        }
      }
    }
  }
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "status": {
          "type": "string",
          "enum": [
            "POSTED",
            "VOID"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "documentNumber",
          "profitStatus",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "receivedCurrency",
          "receivedAmount",
          "paidCurrency",
          "paidAmount",
          "agreedRate",
          "receivedEurRate",
          "paidEurRate",
          "feePercent",
          "feeAmount",
          "feeEur",
          "costBasisEur",
          "exchangeProfitEur",
          "status",
          "createdAt",
          "notes"
        ]
      },
      "minItems": 1,
      "maxItems": 22,
      "default": [
        "id",
        "documentNumber",
        "customerName",
        "receivedAmount",
        "receivedCurrency",
        "paidAmount",
        "paidCurrency",
        "feeEur",
        "exchangeProfitEur",
        "profitStatus",
        "status",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "documentNumber",
            "profitStatus",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "receivedCurrency",
            "receivedAmount",
            "paidCurrency",
            "paidAmount",
            "agreedRate",
            "receivedEurRate",
            "paidEurRate",
            "feePercent",
            "feeAmount",
            "feeEur",
            "costBasisEur",
            "exchangeProfitEur",
            "status",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "desc"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 1
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "search": "",
  "filters": {
    "from": "2026-09-01T00:00:00Z",
    "to": "2026-10-01T00:00:00Z"
  },
  "fields": [
    "id",
    "documentNumber",
    "customerName",
    "receivedAmount",
    "receivedCurrency",
    "paidAmount",
    "paidCurrency",
    "feeEur",
    "exchangeProfitEur",
    "profitStatus",
    "status",
    "createdAt"
  ],
  "sort": {
    "field": "createdAt",
    "direction": "desc"
  },
  "page": 1,
  "limit": 25
}
```

## GET /api/reports/payments/options

فیلدها و فیلترهای مجاز: دریافت و پرداخت



پاسخ داخل data (رسید HTML استثنا است): ReportOptions


## POST /api/reports/payments

دریافت و پرداخت با فیلتر و صفحه‌بندی

Read-only POST. No idempotency key needed. Decimal strings, UTC dates, from inclusive and to/asOf exclusive. Options endpoint describes accepted fields. All staff roles may view financial data. Unknown filters/fields are rejected. Rows, total count and summaries share a database snapshot.

پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "nullable": true
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "report",
        "page",
        "limit",
        "totalItems",
        "totalPages",
        "hasNextPage",
        "hasPreviousPage",
        "baseCurrency",
        "timezone"
      ],
      "properties": {
        "report": {
          "type": "string"
        },
        "page": {
          "type": "integer"
        },
        "limit": {
          "type": "integer"
        },
        "totalItems": {
          "type": "integer"
        },
        "totalPages": {
          "type": "integer"
        },
        "hasNextPage": {
          "type": "boolean"
        },
        "hasPreviousPage": {
          "type": "boolean"
        },
        "outOfRange": {
          "type": "boolean"
        },
        "baseCurrency": {
          "type": "string",
          "enum": [
            "EUR"
          ]
        },
        "timezone": {
          "type": "string",
          "enum": [
            "UTC"
          ]
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "filters": {
          "type": "object"
        },
        "search": {
          "type": "string"
        },
        "sort": {
          "type": "object"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "valuation": {
          "type": "string"
        }
      }
    },
    "summary": {
      "type": "object",
      "description": "All filtered results, never just this page. Financial totals exclude VOID. Monetary values are strings; unknown profit is null.",
      "additionalProperties": true
    },
    "columns": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "sortable": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "currencyField": {
            "type": "string"
          }
        }
      }
    }
  }
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "status": {
          "type": "string",
          "enum": [
            "POSTED",
            "VOID"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "RECEIVE",
            "PAY"
          ]
        },
        "tradeId": {
          "type": "string",
          "format": "uuid"
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "documentNumber",
          "unallocatedAmount",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "tradeId",
          "currencyCode",
          "direction",
          "amount",
          "eurRate",
          "eurAmount",
          "status",
          "createdAt",
          "notes"
        ]
      },
      "minItems": 1,
      "maxItems": 16,
      "default": [
        "id",
        "documentNumber",
        "customerName",
        "currencyCode",
        "direction",
        "amount",
        "unallocatedAmount",
        "eurRate",
        "status",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "documentNumber",
            "unallocatedAmount",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "tradeId",
            "currencyCode",
            "direction",
            "amount",
            "eurRate",
            "eurAmount",
            "status",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "desc"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 1
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "search": "",
  "filters": {
    "from": "2026-09-01T00:00:00Z",
    "to": "2026-10-01T00:00:00Z"
  },
  "fields": [
    "id",
    "documentNumber",
    "customerName",
    "currencyCode",
    "direction",
    "amount",
    "unallocatedAmount",
    "eurRate",
    "status",
    "createdAt"
  ],
  "sort": {
    "field": "createdAt",
    "direction": "desc"
  },
  "page": 1,
  "limit": 25
}
```

## GET /api/reports/statement/options

فیلدها و فیلترهای مجاز: گردش حساب



پاسخ داخل data (رسید HTML استثنا است): ReportOptions


## POST /api/reports/statement

گردش حساب با فیلتر و صفحه‌بندی

Read-only POST. No idempotency key needed. Decimal strings, UTC dates, from inclusive and to/asOf exclusive. Options endpoint describes accepted fields. All staff roles may view financial data. Unknown filters/fields are rejected. Rows, total count and summaries share a database snapshot.

پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "nullable": true
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "report",
        "page",
        "limit",
        "totalItems",
        "totalPages",
        "hasNextPage",
        "hasPreviousPage",
        "baseCurrency",
        "timezone"
      ],
      "properties": {
        "report": {
          "type": "string"
        },
        "page": {
          "type": "integer"
        },
        "limit": {
          "type": "integer"
        },
        "totalItems": {
          "type": "integer"
        },
        "totalPages": {
          "type": "integer"
        },
        "hasNextPage": {
          "type": "boolean"
        },
        "hasPreviousPage": {
          "type": "boolean"
        },
        "outOfRange": {
          "type": "boolean"
        },
        "baseCurrency": {
          "type": "string",
          "enum": [
            "EUR"
          ]
        },
        "timezone": {
          "type": "string",
          "enum": [
            "UTC"
          ]
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "filters": {
          "type": "object"
        },
        "search": {
          "type": "string"
        },
        "sort": {
          "type": "object"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "valuation": {
          "type": "string"
        }
      }
    },
    "summary": {
      "type": "object",
      "description": "All filtered results, never just this page. Financial totals exclude VOID. Monetary values are strings; unknown profit is null.",
      "additionalProperties": true
    },
    "columns": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "sortable": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "currencyField": {
            "type": "string"
          }
        }
      }
    }
  }
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "source": {
          "type": "string",
          "enum": [
            "TRADE",
            "PAYMENT",
            "OPENING",
            "REVERSAL"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "required": [
        "customerId"
      ],
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "currencyCode",
          "amount",
          "runningBalance",
          "eurRate",
          "eurAmount",
          "source",
          "tradeId",
          "paymentId",
          "openingId",
          "reversalOf",
          "createdAt"
        ]
      },
      "minItems": 1,
      "maxItems": 16,
      "default": [
        "id",
        "customerName",
        "currencyCode",
        "amount",
        "runningBalance",
        "source",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "currencyCode",
            "amount",
            "runningBalance",
            "eurRate",
            "eurAmount",
            "source",
            "tradeId",
            "paymentId",
            "openingId",
            "reversalOf",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "asc"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 1
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    }
  },
  "required": [
    "filters"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "search": "",
  "filters": {
    "customerId": 1,
    "from": "2026-09-01T00:00:00Z",
    "to": "2026-10-01T00:00:00Z"
  },
  "fields": [
    "id",
    "customerName",
    "currencyCode",
    "amount",
    "runningBalance",
    "source",
    "createdAt"
  ],
  "sort": {
    "field": "createdAt",
    "direction": "asc"
  },
  "page": 1,
  "limit": 25
}
```

## GET /api/reports/balances/options

فیلدها و فیلترهای مجاز: مانده بدهی و طلب



پاسخ داخل data (رسید HTML استثنا است): ReportOptions


## POST /api/reports/balances

مانده بدهی و طلب با فیلتر و صفحه‌بندی

Read-only POST. No idempotency key needed. Decimal strings, UTC dates, from inclusive and to/asOf exclusive. Options endpoint describes accepted fields. All staff roles may view financial data. Unknown filters/fields are rejected. Rows, total count and summaries share a database snapshot.

پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "nullable": true
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "report",
        "page",
        "limit",
        "totalItems",
        "totalPages",
        "hasNextPage",
        "hasPreviousPage",
        "baseCurrency",
        "timezone"
      ],
      "properties": {
        "report": {
          "type": "string"
        },
        "page": {
          "type": "integer"
        },
        "limit": {
          "type": "integer"
        },
        "totalItems": {
          "type": "integer"
        },
        "totalPages": {
          "type": "integer"
        },
        "hasNextPage": {
          "type": "boolean"
        },
        "hasPreviousPage": {
          "type": "boolean"
        },
        "outOfRange": {
          "type": "boolean"
        },
        "baseCurrency": {
          "type": "string",
          "enum": [
            "EUR"
          ]
        },
        "timezone": {
          "type": "string",
          "enum": [
            "UTC"
          ]
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "filters": {
          "type": "object"
        },
        "search": {
          "type": "string"
        },
        "sort": {
          "type": "object"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "valuation": {
          "type": "string"
        }
      }
    },
    "summary": {
      "type": "object",
      "description": "All filtered results, never just this page. Financial totals exclude VOID. Monetary values are strings; unknown profit is null.",
      "additionalProperties": true
    },
    "columns": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "sortable": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "currencyField": {
            "type": "string"
          }
        }
      }
    }
  }
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "customerActive": {
          "type": "boolean"
        },
        "asOf": {
          "type": "string",
          "format": "date-time"
        },
        "side": {
          "type": "string",
          "enum": [
            "CUSTOMER_OWES",
            "CUSTOMER_IS_OWED",
            "SETTLED"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "currencyCode",
          "amount",
          "side",
          "eurEquivalent",
          "eurRate",
          "rateStatus",
          "rateUpdatedAt"
        ]
      },
      "minItems": 1,
      "maxItems": 11,
      "default": [
        "customerId",
        "customerName",
        "currencyCode",
        "amount",
        "side",
        "eurEquivalent",
        "rateStatus",
        "rateUpdatedAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "currencyCode",
            "amount",
            "side",
            "eurEquivalent",
            "eurRate",
            "rateStatus",
            "rateUpdatedAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "customerId",
        "direction": "asc"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 1
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "search": "",
  "filters": {},
  "fields": [
    "customerId",
    "customerName",
    "currencyCode",
    "amount",
    "side",
    "eurEquivalent",
    "rateStatus",
    "rateUpdatedAt"
  ],
  "sort": {
    "field": "customerId",
    "direction": "asc"
  },
  "page": 1,
  "limit": 25
}
```

## GET /api/reports/customer-profit/options

فیلدها و فیلترهای مجاز: سود مشتریان



پاسخ داخل data (رسید HTML استثنا است): ReportOptions


## POST /api/reports/customer-profit

سود مشتریان با فیلتر و صفحه‌بندی

Read-only POST. No idempotency key needed. Decimal strings, UTC dates, from inclusive and to/asOf exclusive. Options endpoint describes accepted fields. All staff roles may view financial data. Unknown filters/fields are rejected. Rows, total count and summaries share a database snapshot.

پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            }
          ],
          "nullable": true
        }
      }
    },
    "meta": {
      "type": "object",
      "required": [
        "report",
        "page",
        "limit",
        "totalItems",
        "totalPages",
        "hasNextPage",
        "hasPreviousPage",
        "baseCurrency",
        "timezone"
      ],
      "properties": {
        "report": {
          "type": "string"
        },
        "page": {
          "type": "integer"
        },
        "limit": {
          "type": "integer"
        },
        "totalItems": {
          "type": "integer"
        },
        "totalPages": {
          "type": "integer"
        },
        "hasNextPage": {
          "type": "boolean"
        },
        "hasPreviousPage": {
          "type": "boolean"
        },
        "outOfRange": {
          "type": "boolean"
        },
        "baseCurrency": {
          "type": "string",
          "enum": [
            "EUR"
          ]
        },
        "timezone": {
          "type": "string",
          "enum": [
            "UTC"
          ]
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "filters": {
          "type": "object"
        },
        "search": {
          "type": "string"
        },
        "sort": {
          "type": "object"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "valuation": {
          "type": "string"
        }
      }
    },
    "summary": {
      "type": "object",
      "description": "All filtered results, never just this page. Financial totals exclude VOID. Monetary values are strings; unknown profit is null.",
      "additionalProperties": true
    },
    "columns": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "sortable": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "currencyField": {
            "type": "string"
          }
        }
      }
    }
  }
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "tradeCount",
          "feeRevenueEur",
          "knownExchangeProfitEur",
          "knownSubtotalEur",
          "unknownProfitTrades",
          "totalProfitEur"
        ]
      },
      "minItems": 1,
      "maxItems": 10,
      "default": [
        "customerId",
        "customerName",
        "tradeCount",
        "feeRevenueEur",
        "knownExchangeProfitEur",
        "unknownProfitTrades",
        "totalProfitEur"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "tradeCount",
            "feeRevenueEur",
            "knownExchangeProfitEur",
            "knownSubtotalEur",
            "unknownProfitTrades",
            "totalProfitEur"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "feeRevenueEur",
        "direction": "desc"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 1
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 25
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "search": "",
  "filters": {
    "from": "2026-09-01T00:00:00Z",
    "to": "2026-10-01T00:00:00Z"
  },
  "fields": [
    "customerId",
    "customerName",
    "tradeCount",
    "feeRevenueEur",
    "knownExchangeProfitEur",
    "unknownProfitTrades",
    "totalProfitEur"
  ],
  "sort": {
    "field": "feeRevenueEur",
    "direction": "desc"
  },
  "page": 1,
  "limit": 25
}
```

## POST /api/trades/preview

پیش‌نمایش اختیاری معامله

No financial document. Preview expires in 5 minutes. Pass previewId with unchanged input to create; stale/consumed previews return 409. Direct creation is still allowed.

پاسخ داخل data (رسید HTML استثنا است): Pricing, obligations, warnings, previewId and expiresAt

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "customerId": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    },
    "receivedCurrency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "paidCurrency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "receivedAmount": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "agreedRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "receivedEurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "paidEurRate": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,11})(\\.\\d{1,12})?$"
    },
    "feePercent": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,2})(\\.\\d{1,4})?$"
    },
    "costBasisEur": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
    },
    "notes": {
      "type": "string",
      "maxLength": 2000
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "customerId",
    "receivedCurrency",
    "paidCurrency",
    "receivedAmount",
    "agreedRate"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "customerId": 1,
  "receivedCurrency": "USD",
  "paidCurrency": "EUR",
  "receivedAmount": "1000",
  "agreedRate": "0.91",
  "receivedEurRate": "0.92",
  "feePercent": "0.5"
}
```

## GET /api/trades/:id/settlement

وضعیت تسویه دو سمت معامله



پاسخ داخل data (رسید HTML استثنا است): { tradeId, documentNumber, state, legs }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## GET /api/payments/:id/allocations

تخصیص‌ها و مانده تخصیص‌نیافته پرداخت



پاسخ داخل data (رسید HTML استثنا است): Payment allocation summary; version required for edits

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## PUT /api/payments/:id/allocations

اصلاح تخصیص بدون تغییر گردش مالی

Replaces all allocations atomically. AUTO targets oldest open matching obligations (up to 100; tradeId if present restricts it); NONE clears allocations; MANUAL uses supplied rows. It never creates money movements.

پاسخ داخل data (رسید HTML استثنا است): Payment allocation summary

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "expectedVersion": {
      "type": "integer",
      "minimum": 0
    },
    "mode": {
      "type": "string",
      "enum": [
        "AUTO",
        "MANUAL",
        "NONE"
      ],
      "default": "MANUAL"
    },
    "allocations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "tradeId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "string",
            "pattern": "^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
          }
        },
        "required": [
          "tradeId",
          "amount"
        ],
        "additionalProperties": false
      },
      "maxItems": 100,
      "default": []
    }
  },
  "required": [
    "expectedVersion"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "expectedVersion": 1,
  "mode": "AUTO",
  "allocations": []
}
```

## GET /api/trades/:id/receipt

رسید trade

format=json by default; html is escaped and printable. Current settlement plus historical balance-at-posting; legacy documents use UUID as reference.

پاسخ داخل data (رسید HTML استثنا است): Receipt JSON or printable HTML

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "format",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "enum": [
        "json",
        "html"
      ],
      "default": "json"
    }
  }
]
```

## GET /api/payments/:id/receipt

رسید payment

format=json by default; html is escaped and printable. Current settlement plus historical balance-at-posting; legacy documents use UUID as reference.

پاسخ داخل data (رسید HTML استثنا است): Receipt JSON or printable HTML

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "format",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "enum": [
        "json",
        "html"
      ],
      "default": "json"
    }
  }
]
```

## POST /api/reports/trades/export

خروجی CSV trades

Read-only; exact filters/fields/sort of report, no page/limit. Default cap 5000; maximum 10000. String formula prefixes escaped. No partial files.

فایل CSV مستقیم: UTF-8 CSV with BOM, bounded to maxRows; 422 if too large

Response Schema (data unless raw):

```json
{
  "type": "string",
  "format": "binary"
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "status": {
          "type": "string",
          "enum": [
            "POSTED",
            "VOID"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "documentNumber",
          "profitStatus",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "receivedCurrency",
          "receivedAmount",
          "paidCurrency",
          "paidAmount",
          "agreedRate",
          "receivedEurRate",
          "paidEurRate",
          "feePercent",
          "feeAmount",
          "feeEur",
          "costBasisEur",
          "exchangeProfitEur",
          "status",
          "createdAt",
          "notes"
        ]
      },
      "minItems": 1,
      "maxItems": 22,
      "default": [
        "id",
        "documentNumber",
        "customerName",
        "receivedAmount",
        "receivedCurrency",
        "paidAmount",
        "paidCurrency",
        "feeEur",
        "exchangeProfitEur",
        "profitStatus",
        "status",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "documentNumber",
            "profitStatus",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "receivedCurrency",
            "receivedAmount",
            "paidCurrency",
            "paidAmount",
            "agreedRate",
            "receivedEurRate",
            "paidEurRate",
            "feePercent",
            "feeAmount",
            "feeEur",
            "costBasisEur",
            "exchangeProfitEur",
            "status",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "desc"
      }
    },
    "maxRows": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 5000
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "filters": {},
  "maxRows": 5000
}
```

## POST /api/reports/payments/export

خروجی CSV payments

Read-only; exact filters/fields/sort of report, no page/limit. Default cap 5000; maximum 10000. String formula prefixes escaped. No partial files.

فایل CSV مستقیم: UTF-8 CSV with BOM, bounded to maxRows; 422 if too large

Response Schema (data unless raw):

```json
{
  "type": "string",
  "format": "binary"
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "status": {
          "type": "string",
          "enum": [
            "POSTED",
            "VOID"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "RECEIVE",
            "PAY"
          ]
        },
        "tradeId": {
          "type": "string",
          "format": "uuid"
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "documentNumber",
          "unallocatedAmount",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "tradeId",
          "currencyCode",
          "direction",
          "amount",
          "eurRate",
          "eurAmount",
          "status",
          "createdAt",
          "notes"
        ]
      },
      "minItems": 1,
      "maxItems": 16,
      "default": [
        "id",
        "documentNumber",
        "customerName",
        "currencyCode",
        "direction",
        "amount",
        "unallocatedAmount",
        "eurRate",
        "status",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "documentNumber",
            "unallocatedAmount",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "tradeId",
            "currencyCode",
            "direction",
            "amount",
            "eurRate",
            "eurAmount",
            "status",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "desc"
      }
    },
    "maxRows": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 5000
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "filters": {},
  "maxRows": 5000
}
```

## POST /api/reports/statement/export

خروجی CSV statement

Read-only; exact filters/fields/sort of report, no page/limit. Default cap 5000; maximum 10000. String formula prefixes escaped. No partial files.

فایل CSV مستقیم: UTF-8 CSV with BOM, bounded to maxRows; 422 if too large

Response Schema (data unless raw):

```json
{
  "type": "string",
  "format": "binary"
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        },
        "source": {
          "type": "string",
          "enum": [
            "TRADE",
            "PAYMENT",
            "OPENING",
            "REVERSAL"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "required": [
        "customerId"
      ],
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "id",
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "currencyCode",
          "amount",
          "runningBalance",
          "eurRate",
          "eurAmount",
          "source",
          "tradeId",
          "paymentId",
          "openingId",
          "reversalOf",
          "createdAt"
        ]
      },
      "minItems": 1,
      "maxItems": 16,
      "default": [
        "id",
        "customerName",
        "currencyCode",
        "amount",
        "runningBalance",
        "source",
        "createdAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "id",
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "currencyCode",
            "amount",
            "runningBalance",
            "eurRate",
            "eurAmount",
            "source",
            "tradeId",
            "paymentId",
            "openingId",
            "reversalOf",
            "createdAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "createdAt",
        "direction": "asc"
      }
    },
    "maxRows": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 5000
    }
  },
  "required": [
    "filters"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "filters": {
    "customerId": 1
  },
  "maxRows": 5000
}
```

## POST /api/reports/balances/export

خروجی CSV balances

Read-only; exact filters/fields/sort of report, no page/limit. Default cap 5000; maximum 10000. String formula prefixes escaped. No partial files.

فایل CSV مستقیم: UTF-8 CSV with BOM, bounded to maxRows; 422 if too large

Response Schema (data unless raw):

```json
{
  "type": "string",
  "format": "binary"
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "customerActive": {
          "type": "boolean"
        },
        "asOf": {
          "type": "string",
          "format": "date-time"
        },
        "side": {
          "type": "string",
          "enum": [
            "CUSTOMER_OWES",
            "CUSTOMER_IS_OWED",
            "SETTLED"
          ]
        },
        "minAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        },
        "maxAmount": {
          "type": "string",
          "pattern": "^-?(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "currencyCode",
          "amount",
          "side",
          "eurEquivalent",
          "eurRate",
          "rateStatus",
          "rateUpdatedAt"
        ]
      },
      "minItems": 1,
      "maxItems": 11,
      "default": [
        "customerId",
        "customerName",
        "currencyCode",
        "amount",
        "side",
        "eurEquivalent",
        "rateStatus",
        "rateUpdatedAt"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "currencyCode",
            "amount",
            "side",
            "eurEquivalent",
            "eurRate",
            "rateStatus",
            "rateUpdatedAt"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "customerId",
        "direction": "asc"
      }
    },
    "maxRows": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 5000
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "filters": {},
  "maxRows": 5000
}
```

## POST /api/reports/customer-profit/export

خروجی CSV customer-profit

Read-only; exact filters/fields/sort of report, no page/limit. Default cap 5000; maximum 10000. String formula prefixes escaped. No partial files.

فایل CSV مستقیم: UTF-8 CSV with BOM, bounded to maxRows; 422 if too large

Response Schema (data unless raw):

```json
{
  "type": "string",
  "format": "binary"
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "maxLength": 150,
      "default": ""
    },
    "filters": {
      "type": "object",
      "properties": {
        "customerId": {
          "type": "integer",
          "exclusiveMinimum": true,
          "minimum": 0,
          "maximum": 2147483647
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "customerActive": {
          "type": "boolean"
        }
      },
      "additionalProperties": false,
      "default": {}
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "customerId",
          "customerName",
          "customerCode",
          "customerPhone",
          "tradeCount",
          "feeRevenueEur",
          "knownExchangeProfitEur",
          "knownSubtotalEur",
          "unknownProfitTrades",
          "totalProfitEur"
        ]
      },
      "minItems": 1,
      "maxItems": 10,
      "default": [
        "customerId",
        "customerName",
        "tradeCount",
        "feeRevenueEur",
        "knownExchangeProfitEur",
        "unknownProfitTrades",
        "totalProfitEur"
      ]
    },
    "sort": {
      "type": "object",
      "properties": {
        "field": {
          "type": "string",
          "enum": [
            "customerId",
            "customerName",
            "customerCode",
            "customerPhone",
            "tradeCount",
            "feeRevenueEur",
            "knownExchangeProfitEur",
            "knownSubtotalEur",
            "unknownProfitTrades",
            "totalProfitEur"
          ]
        },
        "direction": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ]
        }
      },
      "required": [
        "field",
        "direction"
      ],
      "additionalProperties": false,
      "default": {
        "field": "feeRevenueEur",
        "direction": "desc"
      }
    },
    "maxRows": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 5000
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "filters": {},
  "maxRows": 5000
}
```

## GET /api/report-presets

تنظیمات گزارش شخصی



پاسخ داخل data (رسید HTML استثنا است): { items, total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "search",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "maxLength": 80
    }
  }
]
```

## POST /api/report-presets

ذخیره تنظیم گزارش

Private to current user. Config is validated with the report schema; page is reset to 1.

پاسخ داخل data (رسید HTML استثنا است): ReportPreset

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 80
    },
    "report": {
      "type": "string",
      "enum": [
        "trades",
        "payments",
        "statement",
        "balances",
        "customer-profit"
      ]
    },
    "config": {
      "type": "object",
      "additionalProperties": {}
    }
  },
  "required": [
    "name",
    "report",
    "config"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "name": "معاملات دلار",
  "report": "trades",
  "config": {
    "filters": {
      "currency": "USD"
    },
    "limit": 25
  }
}
```

## GET /api/report-presets/:id

جزئیات تنظیم شخصی



پاسخ داخل data (رسید HTML استثنا است): ReportPreset

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## PATCH /api/report-presets/:id

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



پاسخ داخل data (رسید HTML استثنا است): ReportPreset

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "expectedVersion": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    },
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 80
    },
    "config": {
      "type": "object",
      "additionalProperties": {}
    }
  },
  "required": [
    "expectedVersion"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "expectedVersion": 1,
  "name": "گزارش معاملات روزانه"
}
```

## DELETE /api/report-presets/:id

حذف تنظیم شخصی



پاسخ داخل data (رسید HTML استثنا است): { deleted: true }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "expectedVersion",
    "in": "query",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```

## POST /api/report-presets/:id/run

اجرای تنظیم ذخیره‌شده



پاسخ مستقیم با data/meta/summary/columns: ReportEnvelope

Response Schema (data unless raw):

```json
{
  "type": "object",
  "required": [
    "data",
    "meta",
    "summary",
    "columns"
  ],
  "additionalProperties": true
}
```
پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "page": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{}
```

## GET /api/trade-text/template

قالب ثبت متنی معامله



پاسخ داخل data (رسید HTML استثنا است): { version, template, required, optional }


## POST /api/trade-text/preview

بررسی و پیش‌نمایش متن معامله

No ledger posting. Customer code is required. Amounts are obligations, not cash movements. Preview is informational; creation recalculates current defaults.

پاسخ داخل data (رسید HTML استثنا است): Pricing and warnings

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 5000
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92\nهزینه یورویی: 910\nتوضیحات: معامله نمونه"
}
```

## POST /api/trade-text

ثبت معامله از متن قالب‌دار

Same atomic accounting service as POST /api/trades. Required customer code; received principal excludes fee. Paid amount must match supported rate and currency precision. Does not create Payment. Reuse the same Idempotency-Key on retry.

پاسخ داخل data (رسید HTML استثنا است): trade

پارامترها:

```json
[
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 8,
      "maxLength": 100
    },
    "example": "example-key-0001"
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 5000
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92\nهزینه یورویی: 910\nتوضیحات: معامله نمونه"
}
```

## GET /api/text-imports/templates

قالب‌های کامل عملیات متنی



پاسخ داخل data (رسید HTML استثنا است): Templates, limits, field descriptions and operation rules


## POST /api/text-imports/parse

تجزیه متن بدون مراجعه به دیتابیس کسب‌وکار

Authentication still queries the session. Syntax only; does not resolve customers, prices or post anything. Invalid syntax returns 200 valid=false.

پاسخ داخل data (رسید HTML استثنا است): { valid, records, errors }

بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 15000
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92"
}
```

## POST /api/text-imports/validate

اعتبارسنجی همه رکوردها و محاسبه اثر مالی

Read-only; checks customers, currencies, rates, references and precision. Does not save a preview or financial data. Invalid records return 200 valid=false.

پاسخ داخل data (رسید HTML استثنا است): { valid, records, errors, summary? }

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "formatVersion": {
      "type": "integer",
      "enum": [
        2
      ]
    },
    "valid": {
      "type": "boolean"
    },
    "records": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "TRADE",
              "RECEIVE",
              "PAY"
            ]
          },
          "customer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "code",
              "name"
            ]
          },
          "input": {
            "type": "object",
            "additionalProperties": true
          },
          "pricing": {
            "type": "object",
            "description": "Trade prices; exact decimal strings",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            }
          },
          "settlement": {
            "type": "string",
            "enum": [
              "NONE",
              "FULL",
              "PARTIAL"
            ]
          },
          "payments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "customerId": {
                  "type": "integer"
                },
                "tradeId": {
                  "type": "string",
                  "format": "uuid"
                },
                "currencyCode": {
                  "type": "string"
                },
                "direction": {
                  "type": "string",
                  "enum": [
                    "RECEIVE",
                    "PAY"
                  ]
                },
                "amount": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "eurRate": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "allocationMode": {
                  "type": "string",
                  "enum": [
                    "AUTO",
                    "NONE"
                  ]
                },
                "notes": {
                  "type": "string"
                }
              },
              "required": [
                "customerId",
                "currencyCode",
                "direction",
                "amount",
                "allocationMode"
              ]
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            }
          },
          "recordAlias": {
            "type": "string"
          },
          "externalReference": {
            "type": "string"
          },
          "localTradeIndex": {
            "type": "integer"
          }
        },
        "required": [
          "index",
          "kind",
          "customer",
          "input",
          "payments",
          "warnings"
        ]
      }
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "record": {
            "type": "integer"
          },
          "line": {
            "type": "integer"
          },
          "field": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ]
      }
    },
    "summary": {
      "type": "object",
      "properties": {
        "recordCount": {
          "type": "integer"
        },
        "tradeCount": {
          "type": "integer"
        },
        "paymentCount": {
          "type": "integer"
        },
        "effects": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "accountChange",
              "cashChange"
            ]
          }
        },
        "balances": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "before": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "after": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "before",
              "after",
              "accountChange"
            ]
          }
        }
      },
      "required": [
        "recordCount",
        "tradeCount",
        "paymentCount",
        "effects"
      ]
    }
  },
  "required": [
    "formatVersion",
    "valid",
    "records",
    "errors"
  ]
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 15000
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92\n---\nنوع: دریافت\nمشتری: C001\nمبلغ: 100 USD\nنرخ یورویی: 0.92\nتخصیص: AUTO"
}
```

## POST /api/text-imports/preview

پیش‌نمایش قابل تأیید با اعتبار پنج دقیقه

Stores a user-bound preview but no ledger entry. Pass previewId and the same text to commit. All validation errors are returned in error.details.errors.

پاسخ داخل data (رسید HTML استثنا است): { valid, records, summary, previewId, expiresAt }

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "formatVersion": {
      "type": "integer",
      "enum": [
        2
      ]
    },
    "valid": {
      "type": "boolean"
    },
    "records": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "TRADE",
              "RECEIVE",
              "PAY"
            ]
          },
          "customer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "code",
              "name"
            ]
          },
          "input": {
            "type": "object",
            "additionalProperties": true
          },
          "pricing": {
            "type": "object",
            "description": "Trade prices; exact decimal strings",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            }
          },
          "settlement": {
            "type": "string",
            "enum": [
              "NONE",
              "FULL",
              "PARTIAL"
            ]
          },
          "payments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "customerId": {
                  "type": "integer"
                },
                "tradeId": {
                  "type": "string",
                  "format": "uuid"
                },
                "currencyCode": {
                  "type": "string"
                },
                "direction": {
                  "type": "string",
                  "enum": [
                    "RECEIVE",
                    "PAY"
                  ]
                },
                "amount": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "eurRate": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "allocationMode": {
                  "type": "string",
                  "enum": [
                    "AUTO",
                    "NONE"
                  ]
                },
                "notes": {
                  "type": "string"
                }
              },
              "required": [
                "customerId",
                "currencyCode",
                "direction",
                "amount",
                "allocationMode"
              ]
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            }
          },
          "recordAlias": {
            "type": "string"
          },
          "externalReference": {
            "type": "string"
          },
          "localTradeIndex": {
            "type": "integer"
          }
        },
        "required": [
          "index",
          "kind",
          "customer",
          "input",
          "payments",
          "warnings"
        ]
      }
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "record": {
            "type": "integer"
          },
          "line": {
            "type": "integer"
          },
          "field": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ]
      }
    },
    "summary": {
      "type": "object",
      "properties": {
        "recordCount": {
          "type": "integer"
        },
        "tradeCount": {
          "type": "integer"
        },
        "paymentCount": {
          "type": "integer"
        },
        "effects": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "accountChange",
              "cashChange"
            ]
          }
        },
        "balances": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "before": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "after": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "before",
              "after",
              "accountChange"
            ]
          }
        }
      },
      "required": [
        "recordCount",
        "tradeCount",
        "paymentCount",
        "effects"
      ]
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "formatVersion",
    "valid",
    "records",
    "errors",
    "previewId",
    "expiresAt",
    "summary"
  ]
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 15000
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "نوع: معامله\nمشتری: C001\nدریافتی: ۱٬۰۰۰ USD\nپرداختی: ۹۱۰ EUR\nکارمزد: ۰٫۵\nنرخ یورویی دریافتی: ۰٫۹۲\nتسویه: کامل"
}
```

## POST /api/text-imports

ثبت اتمیک متن تکی یا گروهی

Up to 20 records. All-or-nothing. Preview is optional; supplied preview binds input and calculated prices. FULL settlement explicitly creates real RECEIVE and PAY. Idempotency-Key belongs to the whole batch. Never retry an uncertain request with a new key.

پاسخ داخل data (رسید HTML استثنا است): { importId, formatVersion, replayed, summary, documents }

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "importId": {
      "type": "string",
      "format": "uuid"
    },
    "formatVersion": {
      "type": "integer",
      "enum": [
        2
      ]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "replayed": {
      "type": "boolean"
    },
    "summary": {
      "type": "object",
      "properties": {
        "recordCount": {
          "type": "integer"
        },
        "tradeCount": {
          "type": "integer"
        },
        "paymentCount": {
          "type": "integer"
        },
        "effects": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "accountChange",
              "cashChange"
            ]
          }
        },
        "balances": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "before": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "after": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "before",
              "after",
              "accountChange"
            ]
          }
        }
      },
      "required": [
        "recordCount",
        "tradeCount",
        "paymentCount",
        "effects"
      ]
    },
    "documents": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "record": {
            "type": "integer"
          },
          "kind": {
            "type": "string"
          },
          "customer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "code",
              "name"
            ]
          },
          "trade": {
            "$ref": "#/components/schemas/Trade"
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            }
          },
          "recordAlias": {
            "type": "string"
          },
          "externalReference": {
            "type": "string"
          }
        },
        "required": [
          "record",
          "kind",
          "customer",
          "payments",
          "warnings"
        ]
      }
    }
  },
  "required": [
    "importId",
    "formatVersion",
    "createdAt",
    "replayed",
    "summary",
    "documents"
  ]
}
```
پارامترها:

```json
[
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 8,
      "maxLength": 100
    },
    "example": "example-key-0001"
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 15000
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92"
}
```

## GET /api/text-imports

تاریخچه ثبت‌های متنی خود کاربر

Optional reference filter matches an exact external reference, normalized to uppercase; owner only.

پاسخ داخل data (رسید HTML استثنا است): { items, total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "reference",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    }
  }
]
```

## GET /api/text-imports/:id

متن اصلی و نتیجه ثبت همراه وضعیت جاری اسناد

Owner only, including admins. Stored result is historical; currentDocuments contains current POSTED/VOID states. Reverse individual documents through existing void APIs.

پاسخ داخل data (رسید HTML استثنا است): { id, sourceText, result, currentDocuments, createdAt }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## POST /api/text-drafts

ذخیره پیش‌نویس متنی بدون ثبت مالی

Text may be incomplete or empty. Owner only. No financial posting; validate on preview.

پاسخ داخل data (رسید HTML استثنا است): TextDraftView

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "title": {
      "type": "string"
    },
    "text": {
      "type": "string"
    },
    "version": {
      "type": "integer"
    },
    "status": {
      "type": "string",
      "enum": [
        "DRAFT",
        "COMMITTED"
      ]
    },
    "importId": {
      "type": "string",
      "nullable": true,
      "format": "uuid"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "title",
    "version",
    "status"
  ]
}
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "default": "پیش‌نویس جدید"
    },
    "text": {
      "type": "string",
      "maxLength": 15000,
      "default": ""
    }
  },
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "title": "معاملات امروز",
  "text": "مشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nکارمزد: 0.5\nنرخ یورویی دریافتی: 0.92"
}
```

## GET /api/text-drafts

فهرست پیش‌نویس‌های شخصی

Search title; filter status; text excluded from list. GET details to edit.

پاسخ داخل data (رسید HTML استثنا است): { items, total, page, limit }

پارامترها:

```json
[
  {
    "name": "page",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 1000,
      "default": 1
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 100,
      "default": 25
    }
  },
  {
    "name": "status",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "enum": [
        "DRAFT",
        "COMMITTED"
      ]
    }
  },
  {
    "name": "search",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "maxLength": 120
    }
  }
]
```

## GET /api/text-drafts/:id

خواندن متن و نسخه پیش‌نویس



پاسخ داخل data (رسید HTML استثنا است): TextDraftView

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "title": {
      "type": "string"
    },
    "text": {
      "type": "string"
    },
    "version": {
      "type": "integer"
    },
    "status": {
      "type": "string",
      "enum": [
        "DRAFT",
        "COMMITTED"
      ]
    },
    "importId": {
      "type": "string",
      "nullable": true,
      "format": "uuid"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "title",
    "version",
    "status"
  ]
}
```
پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```

## PATCH /api/text-drafts/:id

ویرایش پیش‌نویس با کنترل نسخه

No overwriting concurrent edits. Returns VERSION_CONFLICT for stale version; committed drafts are immutable.

پاسخ داخل data (رسید HTML استثنا است): TextDraftView

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "title": {
      "type": "string"
    },
    "text": {
      "type": "string"
    },
    "version": {
      "type": "integer"
    },
    "status": {
      "type": "string",
      "enum": [
        "DRAFT",
        "COMMITTED"
      ]
    },
    "importId": {
      "type": "string",
      "nullable": true,
      "format": "uuid"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "title",
    "version",
    "status"
  ]
}
```
پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "expectedVersion": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2147483647
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "text": {
      "type": "string",
      "maxLength": 15000
    }
  },
  "required": [
    "expectedVersion"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "expectedVersion": 1,
  "text": "نوع: معامله\nمشتری: C001\nدریافتی: 1000 USD\nپرداختی: 910 EUR\nنرخ یورویی دریافتی: 0.92\nتسویه: جزئی\nدریافت واقعی: 500 USD\nپرداخت واقعی: 200 EUR"
}
```

## DELETE /api/text-drafts/:id

حذف پیش‌نویس ثبت‌نشده

Committed drafts cannot be deleted. No financial document is removed.

پاسخ داخل data (رسید HTML استثنا است): { deleted: true }

پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "expectedVersion",
    "in": "query",
    "required": true,
    "schema": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2147483647
    }
  }
]
```

## POST /api/text-drafts/:id/preview

بررسی پیش‌نویس و ساخت پیش‌نمایش

No financial posting. Input/rates/balances bind the returned preview; version must still match on commit.

پاسخ داخل data (رسید HTML استثنا است): Preview with draft.id and draft.version

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "formatVersion": {
      "type": "integer",
      "enum": [
        2
      ]
    },
    "valid": {
      "type": "boolean"
    },
    "records": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "TRADE",
              "RECEIVE",
              "PAY"
            ]
          },
          "customer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "code",
              "name"
            ]
          },
          "input": {
            "type": "object",
            "additionalProperties": true
          },
          "pricing": {
            "type": "object",
            "description": "Trade prices; exact decimal strings",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            }
          },
          "settlement": {
            "type": "string",
            "enum": [
              "NONE",
              "FULL",
              "PARTIAL"
            ]
          },
          "payments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "customerId": {
                  "type": "integer"
                },
                "tradeId": {
                  "type": "string",
                  "format": "uuid"
                },
                "currencyCode": {
                  "type": "string"
                },
                "direction": {
                  "type": "string",
                  "enum": [
                    "RECEIVE",
                    "PAY"
                  ]
                },
                "amount": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "eurRate": {
                  "type": "string",
                  "description": "Exact decimal string; do not convert to JS Number"
                },
                "allocationMode": {
                  "type": "string",
                  "enum": [
                    "AUTO",
                    "NONE"
                  ]
                },
                "notes": {
                  "type": "string"
                }
              },
              "required": [
                "customerId",
                "currencyCode",
                "direction",
                "amount",
                "allocationMode"
              ]
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            }
          },
          "recordAlias": {
            "type": "string"
          },
          "externalReference": {
            "type": "string"
          },
          "localTradeIndex": {
            "type": "integer"
          }
        },
        "required": [
          "index",
          "kind",
          "customer",
          "input",
          "payments",
          "warnings"
        ]
      }
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "record": {
            "type": "integer"
          },
          "line": {
            "type": "integer"
          },
          "field": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ]
      }
    },
    "summary": {
      "type": "object",
      "properties": {
        "recordCount": {
          "type": "integer"
        },
        "tradeCount": {
          "type": "integer"
        },
        "paymentCount": {
          "type": "integer"
        },
        "effects": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "accountChange",
              "cashChange"
            ]
          }
        },
        "balances": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "integer"
              },
              "customerCode": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "before": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "after": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "accountChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              },
              "cashChange": {
                "type": "string",
                "description": "Exact decimal string; do not convert to JS Number"
              }
            },
            "required": [
              "customerId",
              "currency",
              "before",
              "after",
              "accountChange"
            ]
          }
        }
      },
      "required": [
        "recordCount",
        "tradeCount",
        "paymentCount",
        "effects"
      ]
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "draft": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        }
      }
    }
  },
  "required": [
    "formatVersion",
    "valid",
    "records",
    "errors",
    "previewId",
    "expiresAt",
    "summary"
  ]
}
```
پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "expectedVersion": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2147483647
    }
  },
  "required": [
    "expectedVersion"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "expectedVersion": 1
}
```

## POST /api/text-drafts/:id/commit

تأیید نهایی پیش‌نویس

Requires the successful preview and Idempotency-Key. Draft and financial documents commit in one transaction. Retry with the SAME key and original expectedVersion; a committed draft cannot post again with a new key.

پاسخ داخل data (رسید HTML استثنا است): { draft: TextDraftView, result: ImportResult }

Response Schema (data unless raw):

```json
{
  "type": "object",
  "properties": {
    "draft": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "title": {
          "type": "string"
        },
        "text": {
          "type": "string"
        },
        "version": {
          "type": "integer"
        },
        "status": {
          "type": "string",
          "enum": [
            "DRAFT",
            "COMMITTED"
          ]
        },
        "importId": {
          "type": "string",
          "nullable": true,
          "format": "uuid"
        },
        "createdAt": {
          "type": "string",
          "format": "date-time"
        },
        "updatedAt": {
          "type": "string",
          "format": "date-time"
        }
      },
      "required": [
        "id",
        "title",
        "version",
        "status"
      ]
    },
    "result": {
      "type": "object",
      "properties": {
        "importId": {
          "type": "string",
          "format": "uuid"
        },
        "formatVersion": {
          "type": "integer",
          "enum": [
            2
          ]
        },
        "createdAt": {
          "type": "string",
          "format": "date-time"
        },
        "replayed": {
          "type": "boolean"
        },
        "summary": {
          "type": "object",
          "properties": {
            "recordCount": {
              "type": "integer"
            },
            "tradeCount": {
              "type": "integer"
            },
            "paymentCount": {
              "type": "integer"
            },
            "effects": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "customerId": {
                    "type": "integer"
                  },
                  "customerCode": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "accountChange": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  },
                  "cashChange": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  }
                },
                "required": [
                  "customerId",
                  "currency",
                  "accountChange",
                  "cashChange"
                ]
              }
            },
            "balances": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "customerId": {
                    "type": "integer"
                  },
                  "customerCode": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "before": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  },
                  "after": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  },
                  "accountChange": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  },
                  "cashChange": {
                    "type": "string",
                    "description": "Exact decimal string; do not convert to JS Number"
                  }
                },
                "required": [
                  "customerId",
                  "currency",
                  "before",
                  "after",
                  "accountChange"
                ]
              }
            }
          },
          "required": [
            "recordCount",
            "tradeCount",
            "paymentCount",
            "effects"
          ]
        },
        "documents": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "record": {
                "type": "integer"
              },
              "kind": {
                "type": "string"
              },
              "customer": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "code",
                  "name"
                ]
              },
              "trade": {
                "$ref": "#/components/schemas/Trade"
              },
              "payments": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Payment"
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              },
              "recordAlias": {
                "type": "string"
              },
              "externalReference": {
                "type": "string"
              }
            },
            "required": [
              "record",
              "kind",
              "customer",
              "payments",
              "warnings"
            ]
          }
        }
      },
      "required": [
        "importId",
        "formatVersion",
        "createdAt",
        "replayed",
        "summary",
        "documents"
      ]
    }
  }
}
```
پارامترها:

```json
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 8,
      "maxLength": 100
    },
    "example": "example-key-0001"
  }
]
```
بدنه (Schema):

```json
{
  "type": "object",
  "properties": {
    "expectedVersion": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2147483647
    },
    "previewId": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "expectedVersion",
    "previewId"
  ],
  "additionalProperties": false
}
```
نمونه (مقادیر نمونه را متناسب با داده واقعی تغییر دهید):

```json
{
  "expectedVersion": 1,
  "previewId": "00000000-0000-4000-8000-000000000001"
}
```

## مدل‌های پاسخ

مقدارهای Decimal رشته هستند. روابط در جزئیات سند با entries برمی‌گردند؛ مدل‌های پایه زیر فیلدهای ذخیره‌شده را توضیح می‌دهند.

### User

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "name": {
      "type": "string"
    },
    "email": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "ADMIN",
        "CASHIER",
        "VIEWER"
      ]
    },
    "active": {
      "type": "boolean"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### Session

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "userId": {
      "type": "integer"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### Customer

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "code": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "phone": {
      "type": "string",
      "nullable": true
    },
    "notes": {
      "type": "string",
      "nullable": true
    },
    "active": {
      "type": "boolean"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### Currency

```json
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "decimals": {
      "type": "integer"
    },
    "active": {
      "type": "boolean"
    },
    "eurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string",
      "nullable": true
    },
    "rateUpdatedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    }
  }
}
```

### Setting

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "defaultFeePercent": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "cashEnabled": {
      "type": "boolean"
    },
    "staleRateHours": {
      "type": "integer"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### Trade

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "documentNumber": {
      "type": "string",
      "nullable": true
    },
    "resultSnapshot": {
      "type": "object",
      "additionalProperties": true,
      "nullable": true
    },
    "idempotencyKey": {
      "type": "string"
    },
    "requestHash": {
      "type": "string"
    },
    "customerId": {
      "type": "integer"
    },
    "receivedCurrency": {
      "type": "string"
    },
    "paidCurrency": {
      "type": "string"
    },
    "receivedAmount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "paidAmount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "agreedRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "receivedEurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "paidEurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "feePercent": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "feeAmount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "feeEur": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "costBasisEur": {
      "type": "string",
      "description": "Exact decimal, serialized as string",
      "nullable": true
    },
    "exchangeProfitEur": {
      "type": "string",
      "description": "Exact decimal, serialized as string",
      "nullable": true
    },
    "status": {
      "type": "string",
      "enum": [
        "POSTED",
        "VOID"
      ]
    },
    "notes": {
      "type": "string",
      "nullable": true
    },
    "createdBy": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "voidedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "voidReason": {
      "type": "string",
      "nullable": true
    }
  }
}
```

### Payment

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "documentNumber": {
      "type": "string",
      "nullable": true
    },
    "resultSnapshot": {
      "type": "object",
      "additionalProperties": true,
      "nullable": true
    },
    "allocationVersion": {
      "type": "integer"
    },
    "idempotencyKey": {
      "type": "string"
    },
    "requestHash": {
      "type": "string"
    },
    "customerId": {
      "type": "integer"
    },
    "tradeId": {
      "type": "string",
      "nullable": true
    },
    "currencyCode": {
      "type": "string"
    },
    "direction": {
      "type": "string",
      "enum": [
        "RECEIVE",
        "PAY"
      ]
    },
    "amount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "eurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "notes": {
      "type": "string",
      "nullable": true
    },
    "status": {
      "type": "string",
      "enum": [
        "POSTED",
        "VOID"
      ]
    },
    "createdBy": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "voidedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "voidReason": {
      "type": "string",
      "nullable": true
    }
  }
}
```

### OpeningBalance

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "customerId": {
      "type": "integer"
    },
    "currencyCode": {
      "type": "string"
    },
    "amount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "eurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "createdBy": {
      "type": "integer"
    }
  }
}
```

### LedgerEntry

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "customerId": {
      "type": "integer"
    },
    "currencyCode": {
      "type": "string"
    },
    "amount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "eurRate": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "tradeId": {
      "type": "string",
      "nullable": true
    },
    "paymentId": {
      "type": "string",
      "nullable": true
    },
    "openingId": {
      "type": "integer",
      "nullable": true
    },
    "reversalOf": {
      "type": "integer",
      "nullable": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### CashOpening

```json
{
  "type": "object",
  "properties": {
    "currencyCode": {
      "type": "string"
    },
    "amount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "createdBy": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### AuditLog

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer"
    },
    "actorId": {
      "type": "integer"
    },
    "action": {
      "type": "string"
    },
    "entityId": {
      "type": "string"
    },
    "detail": {
      "type": "object",
      "additionalProperties": true,
      "nullable": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### DocumentCounter

```json
{
  "type": "object",
  "properties": {
    "key": {
      "type": "string"
    },
    "value": {
      "type": "integer"
    }
  }
}
```

### TradePreview

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "userId": {
      "type": "integer"
    },
    "inputHash": {
      "type": "string"
    },
    "pricingHash": {
      "type": "string"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "consumedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### PaymentAllocation

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "paymentId": {
      "type": "string"
    },
    "tradeId": {
      "type": "string"
    },
    "amount": {
      "type": "string",
      "description": "Exact decimal, serialized as string"
    },
    "createdBy": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### ReportPreset

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "userId": {
      "type": "integer"
    },
    "name": {
      "type": "string"
    },
    "report": {
      "type": "string"
    },
    "config": {
      "type": "object",
      "additionalProperties": true
    },
    "version": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### TextImport

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "userId": {
      "type": "integer"
    },
    "idempotencyKey": {
      "type": "string"
    },
    "requestHash": {
      "type": "string"
    },
    "sourceText": {
      "type": "string"
    },
    "recordCount": {
      "type": "integer"
    },
    "result": {
      "type": "object",
      "additionalProperties": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### TextImportReference

```json
{
  "type": "object",
  "properties": {
    "reference": {
      "type": "string"
    },
    "importId": {
      "type": "string"
    },
    "recordIndex": {
      "type": "integer"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### TextDraft

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "userId": {
      "type": "integer"
    },
    "title": {
      "type": "string"
    },
    "text": {
      "type": "string"
    },
    "version": {
      "type": "integer"
    },
    "importId": {
      "type": "string",
      "nullable": true
    },
    "commitKey": {
      "type": "string",
      "nullable": true
    },
    "committedVersion": {
      "type": "integer",
      "nullable": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

### ProfitSummary

```json
{
  "type": "object",
  "properties": {
    "baseCurrency": {
      "type": "string",
      "enum": [
        "EUR"
      ]
    },
    "tradeCount": {
      "type": "integer"
    },
    "unknownProfitTrades": {
      "type": "integer"
    },
    "feeRevenueEur": {
      "type": "string"
    },
    "knownExchangeProfitEur": {
      "type": "string"
    },
    "knownSubtotalEur": {
      "type": "string"
    },
    "totalProfitEur": {
      "type": "string",
      "nullable": true
    },
    "basis": {
      "type": "string"
    }
  }
}
```

### Error

```json
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "field": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "requestId": {
          "type": "string"
        },
        "details": {
          "type": "object"
        }
      }
    }
  }
}
```

