# API.php

<span>این مستند نمای کلی مسیرهای API پروژه را ارائه می‌دهد که در معماری بک‌اند Laravel توسعه یافته است. ساختار به‌صورت ماژولار طراحی شده و در قالب چند دامنه اصلی شامل </span>**Panel (v2)**<span>، </span>**B2C (v1)**<span>، </span>**Hub**<span>، </span>**AI**<span> و </span>**Core System**<span> پیاده‌سازی شده است.</span>

- <span>در بخش </span>**Panel (v2)**، مسیرها تحت توکن JWT فعال می‌گردند و مسئولیت مدیریت عملیات مالی، حسابداری Redis، اسناد دستی، ترید، چارتر، پست الکترونیک، آموزش، و ماژول‌های رسمی و داخلی را برعهده دارند. این بخش هسته‌ی مدیریتی سیستم بوده و عملیات بک‌اند تیمی را هدایت می‌کند.
- <span>در بخش </span>**B2C (v1)**، APIها برای کاربران نهایی طراحی شده‌اند و شامل احراز هویت، عملیات کیف‌پول و تراکنش، مدیریت رزروهای آنلاین و محتوای مقالات است. برخی مسیرها عمومی و برخی نیازمند JWT می‌باشند.
- **Hub API**<span> به‌عنوان مرکز پردازش مالی و صورتحساب‌ها عمل کرده و پرداخت‌ها و رزروها را بین سیستم‌های مختلف هماهنگ می‌سازد.</span>
- **AI services**<span> قابلیت‌های هوش مصنوعی برای تحلیل و پردازش خودکار داده‌ها را فراهم می‌کند. در نهایت </span>**Core APIs**<span> داده‌های مادر سیستم مانند دفاتر، پروازها و اقامتگاه‌ها را تأمین می‌کنند.</span>
- تمامی مسیرها با اصول REST طراحی شده، دارای ساختار گروهی (prefix/controller grouping) هستند و برای هماهنگی کامل بین فرانت‌اند و بک‌اند بهینه شده‌اند.

# POST /api/auth/connect/branch

### Route Info

<div id="bkmrk-%D9%85%D9%88%D8%B1%D8%AF-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD%D8%A7%D8%AA-method-" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"><table style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center; height: 252.512px;"><thead style="background: #efefef;"><tr style="height: 33.6px;"><th class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">مورد</th><th class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">توضیحات</th></tr></thead><tbody><tr style="height: 33.6px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">Method</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">POST</td></tr><tr style="height: 34.1125px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 34.1125px; width: 50.0502%;">Endpoint</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 34.1125px; width: 50.0502%;">`/api/auth/connect/branch`</td></tr><tr style="height: 33.6px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">Controller</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">UserController</td></tr><tr style="height: 33.6px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">Middleware فعال</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">ندارد</td></tr><tr style="height: 50.4px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 50.4px; width: 50.0502%;">Purpose (هدف کلی)</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 50.4px; width: 50.0502%;">اتصال و معتبرسازی شعبه بر اساس کد ورودی برای بازیابی دامنه مرتبط</td></tr><tr style="height: 33.6px;"><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">دسته‌بندی عملکردی</td><td class="align-center" dir="rtl" style="padding: 8px; border: 1px solid rgb(204, 204, 204); height: 33.6px; width: 50.0502%;">Authentication / Branch Connection</td></tr></tbody></table>

</div>### تحلیل عملکرد (Analysis)

این Endpoint یک نقطه ورود **بدون احراز هویت (Unauthenticated)** برای شناسایی و اعتبارسنجی شعبه است. هدف آن، بازگرداندن دامنه محیطی شعبه معتبر می‌باشد.

#### مکانیزم کدگذاری شعبه

سیستم از فرمول زیر برای استخراج شناسه واقعی شعبه از کد ارسالی استفاده می‌کند:

```
Actual Office ID = Input Branch Code - 1000
```

برای مثال اگر کد ورودی `1050` باشد، شناسه واقعی شعبه `50` خواهد بود.

#### منطق اعتبارسنجی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D9%88%D8%A7%D9%82%D8%B9%DB%8C-" style="font-family: Tahoma, 'Segoe UI', Arial; line-height: 1.7;">- استخراج شناسه واقعی با کسر 1000 از ورودی
- جستجو در جدول `offices` بر اساس `id = Result` و `status = 1`
- برگرداندن فیلد `domain` به عنوان نتیجه در صورت معتبر بودن رکورد
- بازگرداندن پیام خطای فارسی در صورت نبود رکورد معتبر

</div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"><table style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center;"><thead style="background: #efefef;"><tr><th style="padding: 8px; border: 1px solid #ccc;">فیلد</th><th style="padding: 8px; border: 1px solid #ccc;">نوع داده</th><th style="padding: 8px; border: 1px solid #ccc;">توضیح</th><th style="padding: 8px; border: 1px solid #ccc;">الزامی</th></tr></thead><tbody><tr><td style="border: 1px solid #ccc;">branch</td><td style="border: 1px solid #ccc;">Integer</td><td style="border: 1px solid #ccc;">کد شعبه (شناسه واقعی + 1000)</td><td style="border: 1px solid #ccc;">بله</td></tr></tbody></table>

</div>Example Request Body:

```
{
  "branch": 1050
}
```

### خروجی‌ها (Outputs)

پاسخ همیشه `HTTP 200` بازمی‌گردد.

#### خروجی موفق:

<table id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD--1" style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center;"><thead style="background: #efefef;"><tr><th dir="rtl" style="width: 33.3668%;">فیلد</th><th dir="rtl" style="width: 33.3668%;">نوع داده</th><th dir="rtl" style="width: 33.3668%;">توضیح</th></tr></thead><tbody><tr><td dir="rtl" style="height: 30.9125px; width: 33.3668%;">status</td><td dir="rtl" style="height: 30.9125px; width: 33.3668%;">Boolean</td><td dir="rtl" style="height: 30.9125px; width: 33.3668%;">همیشه `true`</td></tr><tr><td dir="rtl" style="height: 29.6px; width: 33.3668%;">time</td><td dir="rtl" style="height: 29.6px; width: 33.3668%;">Integer</td><td dir="rtl" style="height: 29.6px; width: 33.3668%;">زمان یونیکس پاسخ سرور</td></tr><tr><td dir="rtl" style="height: 29.6px; width: 33.3668%;">data.domain</td><td dir="rtl" style="height: 29.6px; width: 33.3668%;">String</td><td dir="rtl" style="height: 29.6px; width: 33.3668%;">دامنهٔ مرتبط با شعبه</td></tr></tbody></table>

<div id="bkmrk-" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"></div>```
{
  "status": true,
  "time": 1731324000,
  "data": {
    "domain": "example.com"
  }
}
```

#### خروجی ناموفق:

```
{
  "status": false,
  "code": 1201,
  "message": "کد دسترسی نا معتبر می باشد."
}
```

### وابستگی‌ها (Dependencies)

<div id="bkmrk-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%DA%AF%DB%8C-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-db" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"><table style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center;"><thead style="background: #efefef;"><tr><th>وابستگی</th><th>نوع</th><th>توضیح</th></tr></thead><tbody><tr><td>DB::table('offices')</td><td>Database Table</td><td>واکشی دامنهٔ شعبه از پایگاه داده</td></tr><tr><td>Illuminate\\Http\\Request</td><td>Framework Class</td><td>دریافت داده JSON ورودی</td></tr><tr><td>response()-&gt;json()</td><td>Laravel Helper</td><td>ساخت خروجی JSON استاندارد</td></tr></tbody></table>

</div>### تست و استفاده (Testing &amp; Usage)

<div id="bkmrk-%D9%85%D9%88%D8%B1%D8%AF-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-method-po" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"><table style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center;"><thead style="background: #efefef;"><tr><th>مورد</th><th>مقدار</th></tr></thead><tbody><tr><td>Method</td><td>POST</td></tr><tr><td>URL</td><td>/api/auth/connect/branch</td></tr><tr><td>Headers</td><td>Content-Type: application/json</td></tr></tbody></table>

</div>Example cURL:

```
curl -X POST https://yourdomain.com/api/auth/connect/branch \
  -H "Content-Type: application/json" \
  -d '{"branch":1050}'
```

### موارد خطا (Typical Error Cases)

<div id="bkmrk-%D8%AE%D8%B7%D8%A7-%DA%A9%D8%AF-http-%D8%B9%D9%84%D8%AA-inva" style="font-family: Tahoma,Segoe UI,Arial; direction: rtl; text-align: justify; line-height: 1.7;"><table style="width: 95%; margin: 15px auto; border-collapse: collapse; text-align: center;"><thead style="background: #efefef;"><tr><th dir="rtl">خطا</th><th dir="rtl">کد HTTP</th><th dir="rtl">علت</th></tr></thead><tbody><tr><td dir="rtl">Invalid Branch Code</td><td dir="rtl">200</td><td dir="rtl">کد شعبه پس از کسر 1000 در جدول `offices` یافت نشود یا وضعیت آن 1 نباشد.</td></tr></tbody></table>

</div>### جزئیات پیاده‌سازی (Conceptual Implementation)

```
$branch = $request->get('branch');
$code = $branch - 1000;

$office = DB::table('offices')
    ->where('id', $code)
    ->where('status', 1)
    ->select('domain')
    ->first();

if ($office) {
    return response()->json([
        'status' => true,
        'time' => now()->timestamp,
        'data' => [ 'domain' => json_decode($office->domain)[0] ],
    ]);
}
return response()->json([
    'status' => false,
    'code'   => 1201,
    'message'=> 'کد دسترسی نا معتبر می باشد.'
]);
```

### نتیجه نهایی (Conclusion)

این Endpoint بدون نیاز به توکن احراز هویت، دامنه شعبه را تعیین می‌کند. طراحی آن ساده و سریع بوده و پایهٔ احراز در سایر سرویس‌های `connectOtp` و `connectSubmit` می‌باشد.

### پیوست: نگهداری و امنیت

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-rate-limiting-" style="font-family: Tahoma, 'Segoe UI', Arial; line-height: 1.7;">- اعمال Rate Limiting برای جلوگیری از سوءاستفاده.
- اعتبارسنجی ورودی جهت جلوگیری از تزریق SQL (توسط Query Builder ایمن).

</div>

# POST /api/v2/colleagues/billing

<div id="bkmrk-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>دسته‌بندی عملکردی</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/connect/branch</td><td style="direction: ltr; text-align: left;">UserController@connectBranch</td><td style="direction: ltr; text-align: left;">ندارد</td><td style="direction: rtl; text-align: right;">اتصال و معتبرسازی شعبه بر اساس کد ورودی برای پایان‌دامنه مرتبط</td><td style="direction: ltr; text-align: left;">Authentication / Branch Connection</td></tr></tbody></table>

</div>### (Analysis) تحلیل عملکرد

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B4%D9%86" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint برای شناسایی و اعتبارسنجی شعبه است. هدف آن بارگزاری دامنه محیط (Unauthenticated) به عنوان نقطه ورود اولیه بدون نیاز به احراز هویت است. این درخواست با دریافت کد شعبه ورودی، هویت دامنه مرتبط با آن دفتر را بازگردانی می‌کند.</div></div>### مکانیسم کدگذاری شعبه

<div id="bkmrk-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A7%D8%B2-%D9%81%D8%B1%D9%85%D9%88%D9%84-%D8%B2%DB%8C%D8%B1-%D8%A8" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">سیستم از فرمول زیر برای استخراج شناسه واقعی شعبه از کد ارسالی استفاده می‌کند:</div></div>```
Actual Office ID = Input Branch Code - 1000
  
```

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%AB%D8%A7%D9%84-%D8%A7%DA%AF%D8%B1-%DA%A9%D8%AF-%D9%88%D8%B1%D9%88" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">برای مثال اگر کد ورودی **1050** باشد، شناسه واقعی شعبه **50** خواهد بود.</div></div>### منطق اعتبارسنجی

<div id="bkmrk-%D9%BE%D8%B3-%D8%A7%D8%B2-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">پس از استخراج شناسه واقعی، سیستم پارامتر زیر را بررسی می‌کند: - در جدول `offices` جستجو انجام می‌شود تا مطمئن شود شعبه فعال است.
- در صورت یافت نشدن یا غیرفعال بودن، پاسخ خطا بازگردانده می‌شود.
- اگر شعبه معتبر باشد، شناسه و اطلاعات دامنه مرتبط (`domain`) بازگردانده می‌شود.

</div></div>### (Inputs) ورودی‌ها

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>Integer</td><td>بله</td><td style="direction: rtl; text-align: justify;">کد شعبه (شناسه واقعی + 1000)</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست (Request Body):</div></div>```
{
  "branch": 1050
}
  
```

<div id="bkmrk--1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### (Outputs) خروجی‌ها

<div id="bkmrk-%D8%AF%D8%B1-%D9%87%D9%85%D9%87-%D8%AD%D8%A7%D9%84%D8%A7%D8%AA-%D9%BE%D8%A7%D8%B3%D8%AE-ht" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">در همه حالات پاسخ HTTP 200 برمی‌گردد.</div></div>#### خروجی موفق:

```
{
  "status": true,
  "office": {
    "id": 50,
    "domain": "example.domain.ir",
    "title": "دفتر مرکزی"
  }
}
  
```

#### خروجی ناموفق:

```
{
  "status": false,
  "message": "کد شعبه معتبر نیست یا یافت نشد."
}
  
```

<div id="bkmrk--2" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### (Dependencies) وابستگی‌ها

<div id="bkmrk-usercontroller%3A%3Aconn" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- `UserController::connectBranch(Request $request)`
- مدل‌ها: `Office` و `Domain`

</div>### (Testing &amp; Usage) تست و استفاده

<div id="bkmrk-%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AA%D8%B3%D8%AA-%D8%A8%D8%A7-postman" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">نمونه تست با Postman:</div></div>```
POST /api/auth/connect/branch
Content-Type: application/json

{
  "branch": 1050
}
  
```

<div id="bkmrk--3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### (Typical Error Cases) موارد خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B9%D8%A8%D9%87-%DA%A9%D9%88%DA%86%DA%A9%D8%AA%D8%B1-%D8%A7%D8%B2-10" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- کد شعبه کوچکتر از 1000 یا غیرعددی
- شعبه غیرفعال در جدول `offices`
- عدم تعریف دامنه برای دفتر مربوطه

</div>### (Implementation Details) جزئیات پیاده‌سازی

```
public function connectBranch(Request $request)
{
    $branchCode = $request->branch;
    $actualId = $branchCode - 1000;
    $office = Office::find($actualId);

    if (!$office) {
        return response()->json(['status' => false, 'message' => 'کد شعبه معتبر نیست یا یافت نشد.']);
    }

    return response()->json([
        'status' => true,
        'office' => [
            'id' => $office->id,
            'domain' => $office->domain,
            'title' => $office->title,
        ]
    ]);
}
  
```

<div id="bkmrk--4" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### (Conclusion) نتیجه‌گیری

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%DB%8C%DA%A9-%D9%86%D9%82%D8%B7%D9%87" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint یک نقطه ورود کاربردی برای لاگین دامنه‌ها بدون نیاز به احراز هویت اولیه است و فرایند تشخیص دامنه بر اساس کد شعبه را ساده می‌کند.</div></div>### (Appendix) پیوست: نگهداری و امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D8%AA" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- احراز ورودی قبل از تبدیل نوع: اطمینان از عددی بودن branch.
- عدم نیاز به تزریق احراز JWT در این مرحله (unauthenticated).
- پیشنهاد به محدود کردن نرخ درخواست‌ها جهت جلوگیری از Spam.

</div>

# POST /api/v2/trade/national

<div id="bkmrk-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>دسته‌بندی عملکردی</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/national</td><td style="direction: ltr; text-align: left;">UserController@nationalPrefix</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">شناسایی شهر بر اساس کد ملی</td><td style="direction: ltr; text-align: left;">Trade / National Identification</td></tr></tbody></table>

</div>### تحلیل عملکرد (Analysis)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D9%88%D8%B8%DB%8C%D9%81%D9%87%E2%80%8C%DB%8C" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint وظیفه‌ی بررسی صحت کد ملی و در صورت معتبر بودن، شناسایی شهر متناظر با سه رقم ابتدایی آن را بر عهده دارد. در صورتی که کد ملی از نظر ساختاری صحیح تشخیص داده شود، شناسهٔ شهر از مدل `City` بر اساس مقدار `national_prefix` بازیابی می‌شود.</div></div>### مکانیزم شناسایی شهر

<div id="bkmrk-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A7%D8%B2-%D8%B3%D9%87-%D8%B1%D9%82%D9%85-%D8%A7%D8%A8%D8%AA%D8%AF" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">سیستم از سه رقم ابتدایی کد ملی برای یافتن پیش‌شمارهٔ ملی (`national_prefix`) در جدول شهرها استفاده می‌کند.</div></div>```
SELECT * FROM cities WHERE national_prefix LIKE '%123%' LIMIT 1;
  
```

<div id="bkmrk-%D8%AF%D8%B1-%D8%A7%DB%8C%D9%86-%D9%85%D8%AB%D8%A7%D9%84%D8%8C-%D8%A7%DA%AF%D8%B1-%DA%A9%D8%A7%D8%B1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">در این مثال، اگر کاربر کد ملی "1234567890" را ارسال کند، مقدار **123** به عنوان پیش‌شماره استخراج خواهد شد.</div></div>### منطق اعتبارسنجی کد ملی

<div id="bkmrk-%D9%81%D8%B1%D8%A7%DB%8C%D9%86%D8%AF-%D9%85%D9%86%D8%B7%D9%82%DB%8C-%D8%AA%D8%A7%D8%A8%D8%B9-na" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">فرایند منطقی تابع `nationalPrefix()` به شرح زیر است: 1. بررسی صحت کد ملی با `Validator::nationalCode()`.
2. در صورت معتبر بودن، سرچ در جدول `City` بر اساس سه رقم ابتدایی.
3. بازگشت نام شهر در کلید `data` در صورت یافتن رکورد.
4. در صورتی که رکوردی یافت نشود، پیام “شهر یافت نشد” بازگردانده می‌شود.
5. در صورت نامعتبر بودن کد ملی، پیام “کد ملی نامعتبر” بازگردانده می‌شود.

</div></div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>code</td><td>String</td><td>بله</td><td class="align-center" style="direction: rtl;">کد ملی (ده‌رقمی) جهت بررسی صحت و تشخیص شهر.</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست:</div></div>```
{
  "code": "0451234567"
}
  
```

<div id="bkmrk--1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### خروجی‌ها (Outputs)

#### خروجی موفق (شهر یافت شد)

```
{
  "status": true,
  "time": 1731926417,
  "data": "تبریز"
}
  
```

#### خروجی معتبر اما بدون شهر

```
{
  "status": true,
  "time": 1731926417,
  "data": "کد ملی صحیح می باشد. اما شهر یافت نشد"
}
  
```

#### خروجی نامعتبر (کد اشتباه)

```
{
  "status": false,
  "time": 1731926417,
  "data": "کد ملی نامعتبر"
}
  
```

<div id="bkmrk--2" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-usercontroller%3A%3Anati" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- `UserController::nationalPrefix(Request $request)`
- `App\Models\City`
- `Validator::nationalCode()`

</div>### تست و استفاده (Testing &amp; Usage)

<div id="bkmrk-%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%A8%D8%A7-pos" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">نمونه درخواست با Postman:</div></div>```
POST /api/v2/trade/national
Content-Type: application/json

{
  "code": "4580021347"
}
  
```

<div id="bkmrk--3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### موارد خطا (Typical Error Cases)

<div id="bkmrk-%DA%A9%D8%AF-%D9%85%D9%84%DB%8C-%DA%A9%D9%88%D8%AA%D8%A7%D9%87%E2%80%8C%D8%AA%D8%B1-%D8%A7%D8%B2-1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- کد ملی کوتاه‌تر از 10 رقم.
- تمامی ارقام تکراری (مثلاً 1111111111).
- عدم وجود رکورد در جدول City با پیش‌شمارهٔ استخراج‌شده.

</div>### جزئیات پیاده‌سازی (Implementation)

```
public function nationalPrefix(Request $request)
{
    if (Validator::nationalCode($request->code)) {
        $Code = City::where('national_prefix', 'LIKE', '%' . substr($request->code, 0, 3) . '%')->first();
        if (!is_null($Code))
            return ['status' => true, 'time' => time(), 'data' => $Code->fa_name];
        else
            return ['status' => true, 'time' => time(), 'data' => 'کد ملی صحیح می باشد. اما شهر یافت نشد'];
    } else
        return ['status' => false, 'time' => time(), 'data' => 'کد ملی نامعتبر'];
}
  
```

<div id="bkmrk--4" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### نتیجه‌گیری (Conclusion)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint%D8%8C-%D8%B1%D9%88%D8%B4%DB%8C-%D8%B3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint، روشی سریع، سبک و بدون نیاز به احراز هویت برای شناسایی شهر بر اساس کد ملی کاربر ارائه می‌دهد. در پروژه‌های تجاری داخلی می‌توان از آن برای ارائهٔ شناسهٔ مکانی خودکار در فرم‌های ثبت‌نام یا رزرو بهره برد.</div></div>### پیوست: نگهداری و امنیت

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%B1-%D9%BE%D8%A7%DB%8C%D9%87%D9%94-loo" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- داده‌ها بر پایهٔ lookup ساده از جدول `City` هستند؛ نیاز به caching برای عملکرد بهتر.
- ورودی کاربر باید قبل از query با regex فیلتر شود تا از Injection جلوگیری گردد.

</div>

# POST /api/auth/sign-in

<div direction:="" id="bkmrk-" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### Route Info

<div direction:="" id="bkmrk-method-endpoint-cont" justify="" line-height:="" rtl="" text-align:="" vazir=""><table border="“1”" cellpadding="“6”" cellspacing="“0”" style="“margin: 15px;"><tbody><tr style="“background: #f9f9f9;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="“direction: ltr;">POST</td><td style="“direction: ltr;">/api/auth/sign-in</td><td style="“direction: ltr;">UserController@signIn</td><td style="“direction: ltr;">domainAccess, ipTrust</td><td style="“direction: rtl;">ورود اپراتور با شناسه پرسنلی و رمز عبور</td><td style="“direction: ltr;">tags={“Auth”}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div class="align-right" direction:="" id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-signin%28%29-%D9%85%D8%B3%D8%A6%D9%88%D9%84-" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">تابع `signIn()` مسئول احراز هویت اپراتور بر اساس شناسهٔ پرسنلی و رمز عبور است. این فرایند شامل مراحل زیر می‌باشد: 1. اعتبارسنجی ورودی `branch` (شعبه) و دادهٔ `data` که شامل `personnelId` و `password` است.
2. جستجوی اپراتور در جدول `operators`، بررسی انطباق شعبه (مقدار `[0]` یا شامل branch در JSON).
3. بررسی وضعیت مسدودی اکانت (حوزه فیلد `blocked_up`).
4. بررسی رمز عبور با `Hash::check()`.
5. در صورت معتبر بودن و فعال بودن حساب (`status == 1`): تولید JWT Token، ثبت لاگ با تأخیر ۱۰ دقیقه، ذخیره Shortcutها و ارسال نوتیفیکیشن تلگرام.
6. در صورت وضعیت غیرفعال یا خطای رمز، بازگرداندن پیام خطا مطابق Swagger.
7. در بخش `catch`، بازگرداندن خطای Exception با پیام و Trace.

</div></div>### ورودی‌ها (Inputs)

<div direction:="" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" justify="" line-height:="" rtl="" text-align:="" vazir=""><table border="1" cellpadding="“6”" cellspacing="“0”" class="align-center" style="border-collapse: collapse;"><tbody><tr style="“background: #f9f9f9;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه‌ای که اپراتور به آن وارد می‌شود.</td></tr><tr><td>data.personnelId</td><td>string</td><td>بله</td><td>شناسه پرسنلی اپراتور برای ورود.</td></tr><tr><td>data.password</td><td>string</td><td>بله</td><td>رمز عبور اپراتور (hash شده در دیتابیس).</td></tr></tbody></table>

<div class="align-right" style="“direction: rtl;" text-align:justify="">نمونه درخواست:</div></div>```
POST /api/auth/sign-in

Content-Type: application/json

{

“branch”: 1,

“data”: {

“personnelId”: “101234”,

“password”: “12345678”

}

}

```

<div direction:="" id="bkmrk--1" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### خروجی‌ها (Outputs)

#### پاسخ موفق (HTTP 200)

```
{

“user”: {

“uuid”: 1,

“from”: “users”,

“role”: “admin”,

“isAirPlusAdmin”: true,

“group”: “IT”,

“data”: {

“displayName”: “علی شاکرچکی”,

“personnelId”: “101234”,

“branch”: “[0]”,

“telegram”: true,

“position”: “مدیر فناوری اطلاعات”,

“access”: […],

“shortcuts”: […]

}

},

“access_token”: “jwt.token.value”

}

```

#### خطا در احراز هویت (HTTP 401)

```
{

“error”: [

{

“type”: “personnelId”,

“message”: “اطلاعات کاربری همخوانی ندارد.”

}

]

}

```

#### حساب کاربری مسدود

```
{

“error”: [

{

“type”: “personnelId”,

“message”: “حساب کاربری شما مسدود شده است.”

}

]

}

```

<div direction:="" id="bkmrk--2" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### منطق تولید JWT Token

<div direction:="" id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%B4%D8%A7%D9%85%D9%84-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7" justify="" line-height:="" rtl="" text-align:="" vazir=""><div class="align-right" style="“direction: rtl;" text-align:justify="">توکن JWT شامل فیلدهای کلیدی زیر است:</div></div>```
{

“typ”: “base”,

“iss”: “{DomainHeader}”,

“aud”: “{DomainHeader}”,

“iat”: 1731927000,

“exp”: 1732531800,

“uuid”: “{OperatorId}”,

“brn”: “{Branch}”,

“uip”: “{ClientIP}”,

“brw”: “{UserAgentClient}”

}

```

<div direction:="" id="bkmrk--3" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### سیستم لاگ و اعلان‌ها

<div class="align-right" direction:="" id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%A8%D8%A7-%D8%AA%D8%A3%D8%AE%DB%8C%D8%B1-%DB%B1%DB%B0-" justify="" line-height:="" rtl="" text-align:="" vazir="">- ثبت لاگ با تأخیر ۱۰ دقیقه‌ای از نوع `SystemLog::dispatch(type=‘Login’)`
- در صورت فعال بودن فیلد `telegram`، ارسال پیام ورود موفق به اکانت تلگرام اپراتور با دکمه «اتمام جلسه»
- توکن موقت برای لینک مسدودی ۱۵ دقیقه‌ای ساخته و در DB ذخیره می‌شود.

</div>### مدیریت اعلان (Push &amp; Telegram)

<div class="align-right" direction:="" id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%D8%AC%D9%88%D8%AF-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%D9%94-" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">در صورت وجود شناسهٔ تلگرام در درجۀ `Operator->telegram`، Dispatcher پیام مارک‌دان را ارسال می‌کند شامل: - نام و آیدی پرسنلی
- شعبه و دامنه
- IP و مرورگر دستگاه
- دکمه مسدودی موقت

</div></div>### موارد خطا (Error Cases)

<div class="align-right" direction:="" id="bkmrk-%D8%B1%D9%85%D8%B2-%D8%B9%D8%A8%D9%88%D8%B1-%D8%A7%D8%B4%D8%AA%D8%A8%D8%A7%D9%87-%DB%8C%D8%A7-%D8%B9" justify="" line-height:="" rtl="" text-align:="" vazir="">- رمز عبور اشتباه یا عدم تطابق شناسه پرسنلی
- حساب کاربری غیرفعال (`status != 1`)
- حساب مسدود تا زمان مشخص‌شده در `blocked_up`
- Exception هنگام تولید JWT یا ثبت در Redis/DB

</div>### وابستگی‌ها (Dependencies)

<div class="align-right" direction:="" id="bkmrk-use-firebase%5Cjwt%5Cjwt" justify="" line-height:="" rtl="" text-align:="" vazir="">- `use Firebase\JWT\JWT;`
- `use Jenssegers\Agent\DeviceDetector;`
- `use Illuminate\Support\Facades\Hash;`
- `use Illuminate\Support\Facades\DB;`
- `use Carbon\Carbon, Morilog\Jalali\Jalalian;`
- `use Redis, Str, SendNotification, SystemLog;`

</div>### نمونه تست واقعی (Postman Example)

```
POST https://console.service01.ir/api/auth/sign-in

Headers:

Domain: service01.ir

Content-Type: application/json

Body:

{

“branch”: 2,

“data”: {

“personnelId”: “104512”,

“password”: “test@1234”

}

}

```

<div direction:="" id="bkmrk--4" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### نتیجه عملکرد (Result Summary)

<div class="align-right" direction:="" id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA%D8%8C-jwt-" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">در صورت موفقیت، JWT توکن برای ۷ روز معتبر ایجاد می‌شود و اپراتور احراز هویت‌شده به همراه تنظیمات رابط کاربری از Redis و دسترسی‌ها برگردانده می‌شود. مسیر در Swagger با Security نوع `bearerAuth` ثبت می‌گردد.</div></div>### پیوست: نکات امنیتی

<div direction:="" id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%A7%DB%8C%D8%AF" justify="" line-height:="" rtl="" text-align:="" vazir="">- تمام درخواست‌ها باید از دامنه‌ی معتبر (Header: `Domain`) ارسال شوند.
- فیلد `password` باید با `Hash::check` مطابقت یابد؛ رمزها هیچ‌گاه در plain text ذخیره نشوند.
- از Env Key `JWT_SECRET_KEY` برای encode استفاده گردد.
- فرآیند Telegram notification در صف `fastJob` ایزوله شود.
- برای لاگ ورود از صف `snailJob` جهت تأخیر بارگذاری استفاده گردد.

</div>

# POST /api/auth/update

<div direction:="" id="bkmrk-" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### Route Info

<div direction:="" id="bkmrk-method-endpoint-cont" justify="" line-height:="" rtl="" text-align:="" vazir=""><table border="“1”" cellpadding="“6”" cellspacing="“0”" style="“margin: 15px;"><tbody><tr style="“background: #f9f9f9;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="“direction: ltr;">POST</td><td style="“direction: ltr;">/api/auth/update</td><td style="“direction: ltr;">UserController@update</td><td style="“direction: ltr;">domainAccess, ipTrust</td><td style="“direction: rtl;">به‌روزرسانی داده‌های رابط کاربری اپراتور (shortcuts) از سمت کلاینت</td><td style="“direction: ltr;">tags={“Auth”}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div direction:="" id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-update%28%29-%D9%85%D8%B3%D8%A6%D9%88%D9%84-" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">تابع `update()` مسئول همگام‌سازی اطلاعات رابط کاربری اپراتور با سرور است. اطلاعات از جمله میان‌برها (shortcuts) از سمت کلاینت ارسال شده و در Redis ذخیره می‌شوند. اگر IP فعلی کاربر با IP ثبت‌شده در توکن JWT مطابقت نداشته باشد، خطایی با نوع **changeIp** بازگردانده می‌شود. عملکرد تابع شامل مراحل زیر است: 1. دریافت ورودی `data` از درخواست.
2. تحلیل و اعتبارسنجی توکن `access_token` با استفاده از **JWT::decode**.
3. مقایسه IP کاربر فعلی با IP ذخیره‌شده در توکن (`uip`).
4. در صورت تطابق، یافتن اپراتور بر اساس `uuid` استخراج‌شده از JWT.
5. اگر اپراتور فعال باشد (`status == 1`)، دادهٔ shortcuts جدید در Redis ذخیره می‌شود.
6. در صورت غیرفعال بودن وضعیت اپراتور، پیام خطا بازگردانده می‌شود.
7. اگر IP تغییر کرده باشد، جزئیات هر دو IP در خروجی خطا قرار می‌گیرد.
8. در صورت بروز هر Exception، محتوا و `trace` بازگردانده می‌شود.

</div></div>### ورودی‌ها (Inputs)

<div direction:="" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" justify="" line-height:="" rtl="" text-align:="" vazir=""><table border="“1”" cellpadding="“6”" cellspacing="“0”" style="“margin: 15px;"><tbody><tr style="“background: #f9f9f9;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td style="“direction: rtl;">شناسه شعبه‌ای که کاربر در آن وارد شده است.</td></tr><tr><td>data.access\_token</td><td>string (JWT)</td><td>بله</td><td style="“direction: rtl;">توکن JWT معتبر که شامل شناسه کاربر (uuid)، IP و زمان صدور است.</td></tr><tr><td>data.user.data.shortcuts</td><td>array</td><td>بله</td><td style="“direction: rtl;">لیست میان‌برهای جدید رابط کاربری اپراتور که باید در Redis ذخیره شوند.</td></tr></tbody></table>

<div style="“direction: rtl;" text-align:justify="">نمونه درخواست:</div></div>```
POST /api/auth/update

Content-Type: application/json

{

“branch”: 1,

“data”: {

“access_token”: “jwt.token.value”,

“user”: {

“data”: {

“shortcuts”: [“dashboard”, “customers”, “tickets”]

}

}

}

}

```

<div direction:="" id="bkmrk--1" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### خروجی‌ها (Outputs)

#### پاسخ موفق (HTTP 200)

```
{}

```

<div direction:="" id="bkmrk-%28%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA%D8%8C-%D8%A8%D8%AF%D9%86" justify="" line-height:="" rtl="" text-align:="" vazir="">(در صورت موفقیت، بدنه پاسخ خالی و بدون خطا بازگردانده می‌شود.)</div>#### وضعیت غیرفعال کاربر

```
{

“error”: {

“type”: “personnelId”,

“message”: “وضعیت شما بصورت غیرفعال می باشد.”

}

}

```

#### تغییر IP کاربر

```
{

“error”: {

“type”: “changeIp”,

“message”: “وضعیت اینترنت شما تغییر پیدا کرده”,

“details”: {

“token_ip”: “192.168.1.10”,

“current_ip”: “5.114.200.88”

}

}

}

```

#### خطا در پردازش

```
{

“error”: {

“type”: “personnelId”,

“message”: “Signature verification failed”,

“trace”: […]

}

}

```

<div direction:="" id="bkmrk--2" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### منطق تشخیص IP مجاز

<div direction:="" id="bkmrk-%D8%AF%D8%B1-%D9%87%D9%86%DA%AF%D8%A7%D9%85-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B5%D8%AD%D8%AA-i" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">در هنگام بررسی صحت IP، سیستم IP توکن را با IP جاری تطبیق می‌دهد. سرویس آدرس‌های داخلی (Local IP Range) را مجاز می‌داند: - 192.168..
- 10...\*
- 172.16..

در این شبکه‌ها حتی در صورت تغییر IP نیازی به invalid کردن توکن نیست.</div></div>### ذخیره اطلاعات در Redis

```
Redis::set(
𝑟𝑒𝑞𝑢𝑒𝑠𝑡−>𝑔𝑒𝑡(′𝑏𝑟𝑎𝑛𝑐ℎ′).′𝑜𝑝𝑒𝑟𝑎𝑡𝑜𝑟:′.
request−>get( ′branch ′). ′operator:′.
Operator->id . ‘:shortcuts’,
json_encode($data[‘user’][‘data’][‘shortcuts’]));
```

<div direction:="" id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87%E2%80%8C%D8%B4%D8%AF%D9%87-%D8%AF%D8%B1" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">اطلاعات ذخیره‌شده در Redis جهت شخصی‌سازی محیط کاربری اپراتور در ورودهای بعدی استفاده می‌شوند.</div></div>### مدیریت استثناها (Exceptions)

<div direction:="" id="bkmrk-token-%D9%86%D8%A7%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%DB%8C%D8%A7-%D9%85%D9%86%D9%82" justify="" line-height:="" rtl="" text-align:="" vazir="">- Token نامعتبر یا منقضی شده: پیام Signature Verification Failed
- خطا در ساختار درخواست: بازگرداندن Exception عمومی با Trace

</div>### وابستگی‌ها (Dependencies)

<div direction:="" id="bkmrk-use-firebase%5Cjwt%5Cjwt" justify="" line-height:="" rtl="" text-align:="" vazir="">- `use Firebase\JWT\JWT;`
- `use Firebase\JWT\Key;`
- `use Illuminate\Support\Facades\Redis;`
- `use App\Models\User;`
- `use Exception;`

</div>### مثال تست (Postman Example)

```
POST https://console.service01.ir/api/auth/update

Content-Type: application/json

{

“branch”: 2,

“data”: {

“access_token”: “jwt.token.value”,

“user”: {

“data”: {

“shortcuts”: [“dashboard”, “customers”, “trade-management”]

}

}

}

}

```

<div direction:="" id="bkmrk--3" justify="" line-height:="" rtl="" text-align:="" vazir=""></div>### تحلیل امنیتی

<div direction:="" id="bkmrk-%D8%A7%D8%B2-ip-%D9%88%D8%A7%D9%82%D8%B9%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%A8%D8%B1" justify="" line-height:="" rtl="" text-align:="" vazir="">- از IP واقعی کاربر برای مطابقت توکن استفاده می‌شود (افزایش امنیت session).
- رمزگشایی JWT تنها با کلید محیطی `JWT_SECRET_KEY` انجام می‌شود.
- هیچ داده‌ای بازگردانده نمی‌شود تا احتمال افشای Token یا داده کاهش یابد.
- درخواست‌ها فقط از دامنه‌های معتبر (domainAccess) و IP مجاز (ipTrust) پذیرفته می‌شوند.

</div>### نتیجه عملکرد (Summary Result)

<div direction:="" id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%A8%D9%87%E2%80%8C%D8%B7%D9%88%D8%B1-" justify="" line-height:="" rtl="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">این Endpoint به‌طور خاص برای همگام‌سازی داده‌های رابط کاربری طراحی شده تا تجربه کاربر در اتوماسیون حفظ شود. در صورت تغییر IP یا وضعیت کاربر، کلاینت باید فرآیند Sign-In را تکرار نماید.</div></div>### پیوست: نگهداری و بهینه‌سازی

<div direction:="" id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-redis-%D9%85%DB%8C%E2%80%8C%D8%AA%D9%88%D8%A7" justify="" line-height:="" rtl="" text-align:="" vazir="">- اطلاعات Redis می‌توانند با TTL (Time To Live) مدیریت شوند تا از انباشت داده جلوگیری شود.
- در صورت تغییر ساختار shortcuts، مطابقت نسخه کلاینت الزامی است.
- در نسخه‌های بعدی، API می‌تواند بازگشت وضعیت موفقیت با پیام استاندارد ارائه دهد.

</div>

# POST /api/auth/blocking

<div direction:="" id="bkmrk-" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""></div>### Route Info

<div direction:="" id="bkmrk-method-endpoint-cont" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><table border="“1”" cellpadding="“6”" cellspacing="“0”" style="“margin: 15px;"><tbody><tr style="“background: #f9f9f9;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="“direction: ltr;">POST</td><td style="“direction: ltr;">/api/auth/blocking</td><td style="“direction: ltr;">UserController@blocking</td><td style="“direction: ltr;">domainAccess, ipTrust</td><td style="“direction: rtl;">مسدودسازی موقت نشست‌های کاربر و خروج اجباری از تمامی نشست‌ها</td><td style="“direction: ltr;">tags={“Auth”}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div direction:="" id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D9%85%D8%B3%D8%A6%D9%88%D9%84-%D8%A8%D8%B3%D8%AA%D9%86-" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">این مسیر مسئول بستن تمامی نشست‌های فعال کاربر است و با دریافت توکن مربوط به تعامل (مثلاً از بات تلگرام)، دسترسی کاربر را به مدت مشخص (پیش‌فرض ۱۵ دقیقه) مسدود می‌کند. عملیات در سه مرحله انجام می‌شود: 1. دریافت توکن از پارامتر `token` در بدنه درخواست یا آدرس مسیر.
2. یافتن رکورد اپراتور با مقدار توکن «telegram:{token}» در ستون `operators.token`.
3. به‌روزرسانی ستون‌ها: 
    - `blocked_up`: تاریخ فعلی به‌علاوه مدت زمان مسدودسازی.
    - `token`: مقدار null برای قطع دسترسی فعلی.

در نهایت پاسخ خروجی شامل پیام موفقیت و زمان فعلی (epoch) است. در صورت بروز استثنا، جزئیات خطا همراه با trace بازگردانده می‌شود.</div></div>### ورودی‌ها (Inputs)

<div direction:="" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><table border="“1”" cellpadding="“6”" cellspacing="“0”" style="“margin: 15px;"><tbody><tr style="“background: #f9f9f9;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>token</td><td>string</td><td>بله</td><td style="“direction: rtl;">شناسه توکن یکتای ارتباط (نظیر توکن تلگرام) برای یافتن حساب اپراتور.</td></tr><tr><td>duration</td><td>integer</td><td>خیر</td><td style="“direction: rtl;">مدت زمان مسدودسازی به دقیقه. مقدار پیش‌فرض ۱۵ دقیقه است.</td></tr></tbody></table>

<div style="“direction: rtl;" text-align:justify="">نمونه درخواست:</div></div>```
POST /api/auth/blocking

Content-Type: application/json

{

“token”: “abc123”,

“duration”: 30

}

```

<div direction:="" id="bkmrk--1" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""></div>### خروجی‌ها (Outputs)

#### پاسخ موفق (HTTP 200)

```
{

“status”: true,

“time”: 1731956440,

“message”: “تمامی نشست ها با موفقیت خاتمه یافتند و کاربری شما بمدت 15 دقیقه مسدود گردید”

}

```

#### پاسخ خطا (Exception)

```
{

“status”: false,

“time”: 1731956440,

“message”: “23000 : SQLSTATE[23000]: Integrity constraint violation”,

“trace”: […]

}

```

<div direction:="" id="bkmrk--2" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""></div>### منطق مسدودسازی نشست کاربر

<div direction:="" id="bkmrk-%D8%A8%D8%A7-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%D8%8C-%D8%AA%D9%85" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">با اجرای درخواست، تمامی نشست‌های فعال متصل به همان توکن تلگرام منقضی می‌شوند. کاربر تا پایان بازه زمانی **blocked\_up** قادر به ورود مجدد نخواهد بود. پس از انقضا، سیستم به صورت خودکار دسترسی را بازمی‌گرداند.</div></div>### تغییرات دیتابیس (Database Update)

```
UPDATE operators

SET

blocked_up = NOW() + INTERVAL {duration or 15} MINUTE,

token = NULL

WHERE token = CONCAT(‘telegram:’, {token});

```

<div direction:="" id="bkmrk-%D8%B3%D8%AA%D9%88%D9%86-blocked_up-%D8%B2%D9%85%D8%A7%D9%86" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">ستون `blocked_up` زمان اتمام مسدودسازی را ذخیره می‌کند. توکن کاربر حذف می‌شود تا از سوی سایر نشست‌ها غیرقابل استفاده شود.</div></div>### ریدایرکت سمت کلاینت

<div direction:="" id="bkmrk-%D9%BE%D8%B3-%D8%A7%D8%B2-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%85%D9%88" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""><div style="“direction: rtl;" text-align:justify="">پس از به‌روزرسانی موفقیت‌آمیز پایگاه‌داده، کاربر به دامنه‌ی `https://airplus.app` هدایت می‌شود. این هدایت فقط از سمت سرور انجام می‌شود (بدون پاسخ HTTP Redirect واقعی).</div></div>### مدیریت استثناها (Exceptions)

<div direction:="" id="bkmrk-%D8%AD%D8%B0%D9%81-%DB%8C%D8%A7-%D8%B9%D8%AF%D9%85-%D9%88%D8%AC%D9%88%D8%AF-%D8%B1%DA%A9%D9%88%D8%B1" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir="">- حذف یا عدم وجود رکورد اپراتور با توکن داده‌شده.
- خطاهای پایگاه داده یا اتصال.
- Exception ناشی از Carbon یا زمان اشتباه در duration.

</div>### وابستگی‌ها (Dependencies)

<div direction:="" id="bkmrk-use-illuminate%5Chttp%5C" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir="">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use Exception;

</div>### تست Endpoint (Postman Example)

```
POST https://console.service01.ir/api/auth/blocking

Content-Type: application/json

{

“token”: “tg-ops-9902”,

“duration”: 20

}

```

<div direction:="" id="bkmrk--3" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir=""></div>### تحلیل امنیتی

<div direction:="" id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D8%B3%D9%88%D8%A1%D8%A7" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir="">- برای جلوگیری از سوءاستفاده، مسیر فقط در دسترس دامنه‌های مجاز (domainAccess) و IPهای مجاز (ipTrust) است.
- داده حساس (token) نباید در URL یا logهای عمومی نمایش داده شود.
- پیشنهاد می‌شود بعد از اجرای موفق، session کاربران در Redis یا cache نیز پاک شود.

</div>### پیوست نگهداری و توسعه آینده

<div direction:="" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D8%A7%D8%AE%D8%AA%DB%8C%D8%A7" justify="" line-height:="" rtl="" tahoma="" text-align:="" vazir="">- افزودن پارامتر اختیاری `reason` برای ثبت علت مسدودسازی مفید است.
- قابل‌ارتقا برای مدیریت انواع توکن‌ (email, telegram, panel).
- ثبت گزارش رویداد در لاگ امنیتی مرکزی پیشنهاد می‌شود.
- در نسخه بعدی، بازگشت Redirect واقعی (HTTP 302) می‌تواند در هماهنگی کلاینت بهبود ایجاد کند.

</div>

# POST /api/auth/forgot/otp

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/forgot/otp</td><td style="direction: ltr; text-align: left;">UserController@forgotOtp</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">ارسال OTP برای بازیابی کلمه عبور اپراتور</td><td style="direction: ltr; text-align: left;">tags={"Auth"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D8%AC%D9%87%D8%AA-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این مسیر جهت ارسال رمز موقت (OTP) به کاربرانی است که قصد دارند کلمه عبور خود را بازیابی کنند. پس از بررسی شماره موبایل و کد پرسنلی کاربر، سیستم رمز یک‌بار مصرف ۶ رقمی تولید کرده و آن را از طریق پیامک به شماره کاربر ارسال می‌کند. همچنین محدودیت ارسال (Throttle) برای جلوگیری از اسپم به کمک لاگ داخلی `Visa::showSystemLogs` کنترل می‌شود.</div>1. یافتن اپراتور فعال با مشخصات دریافتی در جدول `operators`.
2. بررسی آخرین ۵ ساعت لاگ برای نوع `SendOtp` (حداکثر ۳ تلاش در ۵ ساعت).
3. در صورت مجاز بودن، ساخت رمز یکبار مصرف با طول ۶ رقم.
4. ذخیره OTP و زمان صدور (`otp_issuing`) در پایگاه داده.
5. ارسال پیامک حاوی OTP با تابع `StaticController::sendNotification`.
6. در صورت موفقیت، ثبت لاگ در صف **snailJob** با تاخیر ۱۰ دقیقه‌ای.
7. بازگشت خروجی استاندارد شامل وضعیت، زمان، و پیام نتیجه.

</div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">شناسه پرسنلی کاربر دارای وضعیت فعال (status=1)</td></tr><tr><td>mobile</td><td>string (11 digits)</td><td>بله</td><td style="direction: rtl; text-align: right;">شماره موبایل اصلی یا جایگزین اپراتور (mobile یا mobile1)</td></tr><tr><td>branch</td><td>integer</td><td>خیر</td><td style="direction: rtl; text-align: right;">شناسه شعبه جهت ارجاع ارسال پیامک از شاخه مربوطه</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست:</div></div>```
POST /api/auth/forgot/otp
Content-Type: application/json

{
  "personnel_id": "P00123",
  "mobile": "09131234567",
  "branch": 1
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### خروجی‌ها (Outputs)

#### ارسال موفق OTP

```
{
  "status": true,
  "time": 1731958300,
  "message": "OTP با موفقیت ارسال گردید."
}
```

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

```
{
  "status": false,
  "code": 1201,
  "message": "اطلاعات وارد شده با هیچ کاربری همخوانی ندارد"
}
```

#### پیامک ارسال نشد

```
{
  "status": false,
  "code": 1202,
  "message": "پیامک OTP ارسال نشد. مشکلی رخ داده است. لطفا دوباره تلاش کنید",
  "trace": { ... }
}
```

#### تعداد درخواست بیش از حد مجاز

```
{
  "status": false,
  "code": 1203,
  "message": "تعداد درخواست های کاربر بیش از حد مجاز بوده است. لطفا 5 ساعت دیگر اقدام نمائید."
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### منطق محدودسازی درخواست‌ها (Throttle)

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D8%A7%D8%B1%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">برای جلوگیری از ارسال مکرر OTP، سیستم لاگ‌های نوع `SendOtp` را بررسی کرده و فقط تا ۳ بار ارسال در بازه ۵ ساعته مجاز می‌باشد. در صورت تجاوز از این حد، پاسخ خطا با کد 1203 بازگردانده می‌شود.</div></div>### محتوای پیامک ارسالی (SMS Template)

```
code: 123456
این رمز جهت تغییر کلمه عبور صادر شده است.
از در اختیار قراردادن آن به دیگران جدا خودداری فرمائید.
مدت اعتبار: 15 دقیقه

 example.domain

```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### ثبت لاگ سیستمی (SystemLog Dispatch)

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D9%85%D9%88%D9%81%D9%82-%D9%BE" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">در صورت ارسال موفق پیامک، رویداد زیر با تأخیر ۱۰ دقیقه در صف `snailJob` ثبت می‌شود:</div></div>```
SystemLog::dispatch([
  "type" => "SendOtp",
  "goal" => operator_id,
  "by" => operator_id,
  "agent" => $_SERVER['HTTP_USER_AGENT'],
  "ip" => getIP(),
  "datetime" => now()
])->delay(now()->addMinutes(10))->onQueue('snailJob');
```

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\StaticController;
- use App\\Jobs\\SystemLog;
- use App\\Services\\Visa;

</div>### نمونه تست (Postman Example)

```
POST https://console.service01.ir/api/auth/forgot/otp
Content-Type: application/json

{
  "personnel_id": "P00999",
  "mobile": "09138889999",
  "branch": 3
}
```

<div id="bkmrk--5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### تحلیل امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2-%D9%85%D9%88%D9%82%D8%AA-%28otp%29-%D9%81%D9%82%D8%B7-%DB%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- رمز موقت (OTP) فقط ۱۵ دقیقه اعتبار دارد.
- در صورت سه تلاش ناموفق در ۵ ساعت، سیستم ارسال را قفل می‌کند.
- محدودسازی IP و domain در سطح middleware اعمال می‌شود.
- هیچ جزئی از OTP یا توکن در پاسخ لو نمی‌رود.

</div>### نتیجه عملکرد (Summary Result)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D9%81%D8%B1%D8%A2%DB%8C%D9%86%D8%AF-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint فرآیند صدور و ارسال OTP را برای بازنشانی رمز عبور مدیریت می‌کند. در کنار ارسال پیامک، با بررسی سوابق در `SystemLog` از ارسال‌های مکرر جلوگیری می‌شود.</div></div>### پیوست نگهداری و توسعه آتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- افزودن قابلیت ارسال از طریق ایمیل در هزینه‌های آینده.
- ثبت بازه زمانی آخرین `otp_issuing` به عنوان معیاری برای مسیر /auth/forgot/submit.
- پیشنهاد: استفاده از سرویس OTP متمرکز با لایه Redis برای اطمینان بیشتر.

</div>`

# POST /api/auth/connect/otp

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/connect/otp</td><td style="direction: ltr; text-align: left;">UserController@connectOtp</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">ارسال رمز موقت جهت اتصال اپراتور به سرویس‌های خارجی (نظیر تلگرام)</td><td style="direction: ltr; text-align: left;">tags={"Auth"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D9%BE" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint برای اپراتورهایی طراحی شده که قصد اتصال حساب کاربریشان به سرویس‌های جانبی مانند ربات تلگرام را دارند. سیستم پس از بررسی اصالت اپراتور و اطمینان از عدم اتصال قبلی، رمز ۸ رقمی موقتی (OTP) صادر کرده و از طریق پیامک برای او ارسال می‌کند. هر کاربر مجاز است حداکثر ۳ بار در بازه ۵ ساعته درخواست ارسال OTP ثبت کند.</div>1. بررسی وضعیت و مشخصات اپراتور بر اساس `personnel_id` و وضعیت فعال.
2. بررسی عدم اتصال قبلی به تلگرام (`telegram IS NULL`).
3. کنترل سقف ارسال OTP در ۵ ساعت گذشته با `Visa::showSystemLogs`.
4. صدور OTP عددی ۸ رقمی و ذخیره در `operators.otp` همراه زمان صدور.
5. ارسال پیامک OTP از طریق `StaticController::sendNotification`.
6. ثبت رویداد `SystemLog` با تاخیر ۱۰ دقیقه‌ای در صف `snailJob`.

</div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">کد پرسنلی اپراتور فعال (status=1) برای تأیید هویت</td></tr><tr><td>branch</td><td>integer</td><td>خیر</td><td style="direction: rtl; text-align: right;">شناسه شعبه برای انتخاب مسیر ارسال پیامک (در سرویس ارسال Notification)</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست:</div></div>```
POST /api/auth/connect/otp
Content-Type: application/json

{
  "personnel_id": "P00157",
  "branch": 2
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### خروجی‌ها (Outputs)

#### ارسال موفق OTP

```
{
  "status": true,
  "time": 1731963200,
  "message": "OTP با موفقیت ارسال گردید."
}
```

#### اطلاعات کاربر یافت نشد

```
{
  "status": false,
  "code": 1201,
  "message": "اطلاعات وارد شده با هیچ کاربری همخوانی ندارد"
}
```

#### کاربر قبلاً متصل شده است

```
{
  "status": false,
  "code": 1203,
  "message": "شما قبلا به سامانه متصل شده اید اگر قصد تغییر تلگرام خود را دارید قبل از انجام این فرایند. از طریق سامانه قطع اتصال فرمائید."
}
```

#### تعداد درخواست بیش از حد مجاز

```
{
  "status": false,
  "code": 1203,
  "message": "تعداد درخواست های کاربر بیش از حد مجاز بوده است. لطفا 5 ساعت دیگر اقدام نمائید."
}
```

#### پیامک ارسال نشد

```
{
  "status": false,
  "code": 1202,
  "message": "پیامک OTP ارسال نشد. مشکلی رخ داده است. لطفا دوباره تلاش کنید",
  "trace": {...}
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### محدودسازی ارسال (Throttle Control)

<div id="bkmrk-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-otp-%D8%A8%D9%87-%DA%A9%D9%85%DA%A9-%D8%A8%D8%B1%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">ارسال OTP به کمک بررسی لاگ‌های `SystemLog` انجام می‌گیرد. هر اپراتور مجاز به ارسال حداکثر **۳ بار** در بازه‌ی زمانی **۵ ساعت** است. در صورت تجاوز از این حد پاسخ با کد 1203 بازمی‌گردد.</div></div>### قالب پیامک ارسالی (SMS Template)

```
code: 12345678
این رمز جهت ارتباط با سایر سرویس ها صادر شده است.
از دراختیار قراردادن آن به دیگران جدا خودداری فرمائید.
مدت اعتبار: 15 دقیقه

🌐 example.domain
```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### ثبت رویداد SystemLog

<div id="bkmrk-%D9%BE%D8%B3-%D8%A7%D8%B2-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D9%85%D9%88%D9%81%D9%82-%D9%BE%DB%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">پس از ارسال موفق پیامک، رویداد زیر در صف **snailJob** ثبت می‌گردد:</div></div>```
SystemLog::dispatch([
  "type" => "SendOtp",
  "goal" => operator_id,
  "by" => operator_id,
  "agent" => $_SERVER['HTTP_USER_AGENT'],
  "ip" => getIP(),
  "datetime" => now()
])->delay(now()->addMinutes(10))->onQueue('snailJob');
```

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### تغییرات دیتابیس (Database Update)

```
UPDATE operators
SET
  otp = {random 8-digit code},
  otp_issuing = NOW()
WHERE id = {operator.id};
```

<div id="bkmrk--5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use App\\Jobs\\SystemLog;
- use App\\Http\\Controllers\\StaticController;
- use App\\Services\\Visa;

</div>### نمونه تست (Postman Example)

```
POST https://console.service01.ir/api/auth/connect/otp
Content-Type: application/json

{
  "personnel_id": "P00231",
  "branch": 1
}
```

<div id="bkmrk--6" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### تحلیل امنیتی

<div id="bkmrk-%D8%A7%DA%AF%D8%B1-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D9%82%D8%A8%D9%84%D8%A7%D9%8B-%D8%A8%D9%87-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- اگر کاربر قبلاً به تلگرام متصل شده باشد، ارسال OTP انجام نمی‌شود.
- رمز ۸ رقمی تنها ۱۵ دقیقه معتبر است.
- کنترل دفعات درخواست در بازه‌ی ۵ ساعت مانع حملات brute force می‌شود.
- دسترسی به این مسیر فقط از IPها و دامنه‌های مجاز امکان‌پذیر است (به‌واسطه‌ی `domainAccess` و `ipTrust`).

</div>### پیوست نگهداری و توسعه‌آتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-push-no" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- افزودن سیستم Push Notification برای ارتباط درون‌برنامه‌ای به‌جای پیامک.
- ثبت IP درخواست OTP در جدول جداگانه برای تحلیل امنیتی.
- ادغام با سامانه مرکزی Auth برای مدیریت اتصال سایر سرویس‌ها.
- افزودن شاخص `otp_fail_attempts` برای کنترل تلاش‌های اشتباه.

</div>

# POST /api/auth/connect/submit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/connect/submit</td><td style="direction: ltr; text-align: left;">UserController@connectSubmit</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">تأیید نهایی اتصال اپراتور به سرویس جانبی پس از دریافت رمز OTP</td><td style="direction: ltr; text-align: left;">tags={"Auth"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%81%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint برای فاز نهایی فرآیند اتصال کاربر به سرویس جانبی (نظیر ربات تلگرام) استفاده می‌شود. پس از ارسال رمز موقت (OTP) از طریق مسیر **POST /api/auth/connect/otp**، اپراتور باید طی ۱۵ دقیقه آن را در این مسیر ثبت کند. در صورت صحت OTP و اعتبار زمانی آن، ارتباط کاربر ثبت و موفق اعلام می‌شود.</div>1. دریافت پارامترهای `personnel_id`، `otp` و `telegram` از درخواست.
2. جست‌وجوی اپراتور فعال در جدول `operators` براساس کد پرسنلی و رمز فعلی OTP.
3. بررسی انقضای OTP با درنظر گرفتن بازه زمانی ۱۵ دقیقه‌ای.
4. در صورت اعتبار، به‌روزرسانی ستون‌های `telegram` و `otp` (پاک کردن OTP).
5. ثبت رویداد در `SystemLog` با نوع `SubmitTelegram` و تاخیر ۱۰ دقیقه‌ای در صف `snailJob`.
6. ارسال پیامک اطلاع‌رسانی موفقیت اتصال با جزئیات IP و زمان شمسی.

</div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">کد پرسنلی اپراتور فعال (status=1).</td></tr><tr><td>otp</td><td>integer</td><td>بله</td><td style="direction: rtl; text-align: right;">رمز یکبار مصرف صادرشده از مرحله‌ی قبلی.</td></tr><tr><td>telegram</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">شناسه یا شماره کاربری تلگرام کاربر برای اتصال.</td></tr><tr><td>branch</td><td>integer</td><td>خیر</td><td style="direction: rtl; text-align: right;">شناسه‌ی شعبه برای ارسال اطلاع‌رسانی از طریق سرویس پیامک.</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست:</div></div>```
POST /api/auth/connect/submit
Content-Type: application/json

{
  "personnel_id": "P00157",
  "otp": "12345678",
  "telegram": "@aliiranpour",
  "branch": 1
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### خروجی‌ها (Outputs)

#### تصال موفق

```
{
  "status": true,
  "time": 1731963561,
  "message": "اتصال با موفقیت انجام شد."
}
```

#### رمز منقضی شده

```
{
  "status": false,
  "code": 1204,
  "message": "رمز یکبار مصرف منقضی شده است."
}
```

#### رمز اشتباه یا نامعتبر

```
{
  "status": false,
  "code": 1205,
  "message": "رمز یکبار مصرف وارد شده معتبر نمی باشد."
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### فرآیند بررسی زمان OTP

<div id="bkmrk-carbon%3A%3Anow%28%29-%3Eaddmi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: ltr; text-align: left; background: #fafafa; padding: 10px; border-radius: 6px; font-family: monospace;">Carbon::now()-&gt;addMinutes(-15) &lt;= $operator-&gt;otp_issuing</div><div style="direction: rtl; text-align: justify;">این شرط بررسی می‌کند که OTP مورد استفاده حداکثر ۱۵ دقیقه قبل صادر شده باشد. در صورت گذشتن بیش از ۱۵ دقیقه، سیستم خطای Expired Code با کد 1204 برمی‌گرداند.</div></div>### به‌روزرسانی جدول Operators

```
UPDATE operators
SET
  telegram = {request.telegram},
  otp = NULL
WHERE id = {operator.id};
```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### ثبت رویداد در SystemLog

```
SystemLog::dispatch([
  "type" => "SubmitTelegram",
  "data" => null,
  "goal" => $request->personnel_id,
  "by" => $operator->id,
  "agent" => $_SERVER['HTTP_USER_AGENT'],
  "ip" => getIP(),
  "datetime" => Carbon::now()
])->delay(now()->addMinutes(10))->onQueue('snailJob');
```

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### پیامک موفقیت اتصال

```
پیامک:
اتصال شما به سرویس مورد نظر موفقیت آمیز بود.
زمان: شنبه، ۲۷ آبان ۱۴۰۳ | ۱۲:۴۵:۱۰
📍 IP: 192.168.1.24
🌐 example.domain
```

<div id="bkmrk-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82%3A" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">ارسال از طریق:</div>```
StaticController::sendNotification($operator->mobile, $tempBody, $request->get('branch'));
```

<div id="bkmrk--5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use Morilog\\Jalali\\Jalalian;
- use App\\Jobs\\SystemLog;
- use App\\Http\\Controllers\\StaticController;

</div>### نمونه تست Postman

```
POST https://console.service01.ir/api/auth/connect/submit
Content-Type: application/json

{
  "personnel_id": "P00442",
  "otp": "55667788",
  "telegram": "@operator_test"
}
```

<div id="bkmrk--6" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### تحلیل امنیتی

<div id="bkmrk-otp-%D9%81%D9%82%D8%B7-%DB%B1%DB%B5-%D8%AF%D9%82%DB%8C%D9%82%D9%87-%D8%A7%D8%B9%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- OTP فقط ۱۵ دقیقه اعتبار دارد.
- پس از تأیید موفق، مقدار آن از پایگاه‌داده حذف می‌شود (null).
- اطلاعات IP و User Agent در `SystemLog` ذخیره می‌گردند.
- ارسال پیامک از طریق سرویس داخلی شرکت انجام می‌شود و حاوی اطلاعات حساس نیست.
- وجود middleware‌ های `domainAccess` و `ipTrust` مانع ارسال از منابع غیرمجاز می‌گردد.

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A8%D9%87-%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-webho" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- اتصال به سرویس `Webhook Telegram` برای تأیید دوطرفه.
- ثبت زمان دقیق اتصال در جدول پیشرفته audit\_log.
- افزودن محدودیت IP برای کاربران خاص در مرحله Submit.
- نمایش هشدار برای IPهای ناشناس محیط کاربری.

</div>`

# POST /api/auth/forgot/submit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/forgot/submit</td><td style="direction: ltr; text-align: left;">UserController@forgotSubmit</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">ثبت رمز عبور جدید پس از تأیید با رمز OTP (فراموشی رمز عبور)</td><td style="direction: ltr; text-align: left;">tags={"Auth"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D9%81%D8%A7%D8%B2-%D9%86%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">این Endpoint فاز نهایی فرآیند بازیابی رمز عبور را مدیریت می‌کند. اپراتور باید پس از دریافت رمز یکبار مصرف (OTP) از طریق مسیر **POST /api/auth/forgot/otp**، آن را به همراه رمز عبور جدید به این مسیر ارسال کند. این مسیر اعتبار OTP را بررسی کرده، رمز جدید را با استفاده از الگوریتم هشینگ ایمن کرده و در پایگاه داده ثبت می‌کند.</div>1. جست‌وجوی اپراتور فعال (`status=1`) با `personnel_id` و `otp` یکسان.
2. بررسی زمان صدور OTP (`otp_issuing`): اگر بیش از ۱۵ دقیقه گذشته باشد، رمز منقضی شده است (کد ۱۲۰۴).
3. اگر معتبر باشد: رمز عبور جدید (`$request->password`) با `Hash::make()` هش و ذخیره می‌شود.
4. ستون `otp` اپراتور به `NULL` تغییر داده می‌شود تا از استفاده مجدد جلوگیری شود.
5. ثبت رویداد `UpdatePassword` در `SystemLog` در صف `snailJob`.
6. ارسال پیامک اطلاع‌رسانی موفقیت‌آمیز بودن تغییر رمز با جزئیات IP و زمان شمسی.

</div>### ورودی‌ها (Inputs)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">کد پرسنلی اپراتور فعال.</td></tr><tr><td>otp</td><td>integer</td><td>بله</td><td style="direction: rtl; text-align: right;">رمز یکبار مصرف ۸ رقمی صادر شده برای بازیابی.</td></tr><tr><td>password</td><td>string</td><td>بله</td><td style="direction: rtl; text-align: right;">رمز عبور جدید. (اعتبارسنجی پیچیدگی در لایه فرانت یا Middleware باید اعمال شود)</td></tr><tr><td>branch</td><td>integer</td><td>خیر</td><td style="direction: rtl; text-align: right;">شناسه‌ی شعبه برای ارسال اطلاع‌رسانی پیامکی.</td></tr></tbody></table>

<div style="direction: rtl; text-align: justify;">نمونه درخواست:</div></div>```
POST /api/auth/forgot/submit
Content-Type: application/json

{
  "personnel_id": "P00999",
  "otp": "98765432",
  "password": "MyNewStrongPassword@123",
  "branch": 1
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### خروجی‌ها (Outputs)

#### به‌روزرسانی موفق رمز عبور

```
{
  "status": true,
  "time": 1731963561,
  "message": "کلمه عبور با موفقیت بروزرسانی شد."
}
```

#### رمز یکبار مصرف منقضی شده

```
{
  "status": false,
  "code": 1204,
  "message": "رمز یکبار مصرف منقضی شده است."
}
```

#### رمز یکبار مصرف نامعتبر

```
{
  "status": false,
  "code": 1205,
  "message": "رمز یکبار مصرف وارد شده معتبر نمی باشد."
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### هشینگ و به‌روزرسانی دیتابیس

<div id="bkmrk-%D8%B1%D9%85%D8%B2-%D8%B9%D8%A8%D9%88%D8%B1-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D8%B0%D8%AE%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">رمز عبور قبل از ذخیره، با استفاده از متد `Hash::make()` هش می‌شود. در همان تراکنش، OTP استفاده شده با مقدار **NULL** جایگزین می‌گردد تا امنیت تضمین شود.</div></div>```
DB::table('operators')
    ->where('id', $operator->id)
    ->update([
        'password' => Hash::make($request->password), 
        'otp' => null
    ]);
```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### ثبت رویداد در SystemLog

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B1%D8%AF%DB%8C%D8%A7%D8%A8%DB%8C-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA%E2%80%8C%D8%A2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">برای ردیابی موفقیت‌آمیز بودن تغییر رمز عبور و مبدأ آن، یک رویداد امنیتی در SystemLog ثبت می‌شود.</div></div>```
SystemLog::dispatch([
  "type" => "UpdatePassword",
  "data" => null,
  "goal" => $request->personnel_id,
  "by" => $operator->id,
  "agent" => $_SERVER['HTTP_USER_AGENT'],
  "ip" => getIP(),
  "datetime" => Carbon::now()
])->delay(now()->addMinutes(10))->onQueue('snailJob');
```

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### پیامک اطلاع‌رسانی امنیتی

<div id="bkmrk-%D8%A8%D9%84%D8%A7%D9%81%D8%A7%D8%B5%D9%84%D9%87-%D9%BE%D8%B3-%D8%A7%D8%B2-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"><div style="direction: rtl; text-align: justify;">بلافاصله پس از تغییر رمز، یک پیامک امنیتی به شماره موبایل کاربر ارسال می‌شود تا از اقدام صورت‌گرفته مطلع شود.</div></div>```
پیامک:
رمز عبور شما بروزرسانی گردید.
زمان: شنبه، ۲۷ آبان ۱۴۰۴ | ۱۳:۱۰:۲۰
📍 IP: 10.0.0.15
🌐 example.domain
```

<div id="bkmrk--5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;"></div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Hash;
- use Carbon\\Carbon;
- use Morilog\\Jalali\\Jalalian;
- use App\\Jobs\\SystemLog;
- use App\\Http\\Controllers\\StaticController;

</div>### تحلیل امنیتی

<div id="bkmrk-%2A%2A%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%86%D8%AC%DB%8C-%D8%B2%D9%85%D8%A7%D9%86%DB%8C%3A" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- \*\*اعتبار سنجی زمانی:\*\* فقط ۱۵ دقیقه برای ثبت رمز جدید فرصت داده می‌شود.
- \*\*حذف OTP:\*\* رمز یکبار مصرف بلافاصله پس از استفاده، با NULL جایگزین می‌شود.
- \*\*هشینگ رمز:\*\* استفاده از `Hash::make()` برای جلوگیری از ذخیره‌سازی رمز به صورت متن ساده.
- \*\*هشدار به کاربر:\*\* ارسال پیامک به موبایل کاربر، لایه امنیتی نهایی در صورت سرقت هویت را فراهم می‌کند.
- \*\*Middleware:\*\* محافظت با `domainAccess` و `ipTrust` الزامی است.

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%BE%DB%8C%DA%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.8;">- اعمال اعتبارسنجی پیچیدگی رمز (حداقل ۸ کاراکتر، کاراکترهای خاص) در سمت سرور به صورت Middleware یا Form Request.
- اضافه‌کردن قابلیت جلوگیری از استفاده مجدد رمزهای عبور قبلی (Password History).
- درخواست تأیید رمز جدید (confirm password) در لایه فرانت‌اند.

</div>

# POST /api/auth/access-token

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/auth/access-token</td><td style="direction: ltr; text-align: left;">UserController@accessToken</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">تولید توکن دسترسی جدید پس از تطبیق مرورگر، IP، دامنه و شعبه.</td><td style="direction: ltr; text-align: left;">tags={"Auth"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint%D8%8C-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این Endpoint، اعتبار و اصالت توکن فعلی کاربر را پس از Decode بررسی کرده و در صورت تطبیق پارامترهای امنیتی، توکن جدید ۷ روزه صادر می‌کند. اگر هر کدام از فاکتورهای مرورگر، IP، دامنه یا شعبه تغییر کند، درخواست با پیام خطای دقیق رد می‌شود.</div>1. دریافت `access_token` از `$request->data` و Decode با کلید `env('JWT_SECRET_KEY')`.
2. تحلیل User-Agent با `DeviceDetector` برای استخراج اطلاعات مرورگر فعلی (نام و نسخه).
3. تابع داخلی `checkBrowser()` مقایسه‌ای بین مرورگر ثبت‌شده در توکن و مرورگر فعلی انجام می‌دهد.
4. اگر مرورگر تطبیق داشت، سپس IP بررسی می‌شود: 
    - اگر IP فعلی با `uip` در توکن یا با یکی از IPهای خصوصی (10.\* / 192.168.\* / 172.16.\*) هم‌خوانی داشت، ادامه داده می‌شود.
5. تطبیق دامنه `iss` با هدر Domain درخواست.
6. تطبیق شناسه شعبه `brn` با ورودی `branch` درخواست.
7. در صورت قبولی همه مراحل: یافتن اپراتور فعال (status=1، بدون بلوک).
8. تولید توکن جدید با ساختار زیر:

</div>```
{
  "typ": "base",
  "iss": "domain.com",
  "aud": "domain.com",
  "iat": 1731964000,
  "nbf": 1731964000,
  "exp": 1732568800,       // 7 روز
  "uuid": 54,
  "brn": 2,
  "uip": "10.0.0.15",
  "brw": { "name": "Chrome", "version": "122.0", "type": "browser" }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### بدنه درخواست (Request Body)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>data.access\_token</td><td>string (JWT)</td><td>بله</td><td style="direction: rtl; text-align: right;">توکن فعلی کاربر که باید توسط سرور Decode و اعتبارسنجی شود.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td style="direction: rtl; text-align: right;">شناسه شعبه فعلی جهت تطبیق با اطلاعات درون توکن.</td></tr></tbody></table>

</div>```
POST /api/auth/access-token
Content-Type: application/json
Domain: example.domain

{
  "branch": 2,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پاسخ موفق (Response - Success)

```
{
  "user": {
    "uuid": 54,
    "from": "users",
    "role": "admin",
    "isAirPlusAdmin": true,
    "group": "core",
    "data": {
      "displayName": "Ali Pour",
      "email": "a.pour@example.ir",
      "photoURL": "https://storage.service01.ir/media/avatar/robot.png",
      "services": { "telegram": false },
      "personnelId": "P00999",
      "branch": [0,1],
      "access": [ ... ],
      "shortcuts": ["temporary-registration","online-ticket","trade-management","customers"]
    }
  },
  "access_token": "new.jwt.token.generated"
}
```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پاسخ‌های خطا (Error Types)

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-chan" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold; text-align: center;"><td>نوع خطا</td><td>شرح خطا</td></tr><tr><td>changeBrowser</td><td style="direction: rtl; text-align: right;">مرورگر فعلی با مرورگر آخرین ورود مطابقت ندارد.</td></tr><tr><td>changeIp</td><td style="direction: rtl; text-align: right;">IP متغیر یا استفاده از VPN.   
**جزئیات:** شامل IP فعلی و IP درج‌شده در توکن.</td></tr><tr><td>changeDomain</td><td style="direction: rtl; text-align: right;">دامنه درخواست با دامنه صادرکننده توکن هم‌خوانی ندارد.</td></tr><tr><td>changeBranch</td><td style="direction: rtl; text-align: right;">شناسه شعبه تغییر کرده (مثلاً انتقال کاربر بین شعبات).</td></tr><tr><td>personnelId</td><td style="direction: rtl; text-align: right;">نامعتبر بودن توکن یا خطای رمزگشایی JWT.</td></tr></tbody></table>

نمونه خطای تغییر IP:</div>```
{
  "error": {
    "type": "changeIp",
    "message": "وضعیت اینترنت شما تغییر پیدا کرده",
    "details": {
      "token_ip": "10.1.1.2",
      "current_ip": "45.66.88.100"
    }
  }
}
```

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### تولید JWT جدید

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-%D8%AC%D8%AF%DB%8C%D8%AF-%D8%A8%D8%A7-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">توکن جدید با کتابخانه **firebase/php-jwt** و الگوریتم `HS256` تولید می‌شود؛ مدت اعتبار آن ۷ روز (۶۰۴٬۸۰۰ ثانیه) است. اطلاعات کلیدی آن شامل شناسه کاربر، شناسه شعبه، مرورگر و IP فعلی است.</div></div>### تحلیل امنیتی

<div id="bkmrk-%E2%9C%85-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%DA%86%D9%86%D8%AF%D9%85%D8%B1%D8%AD%D9%84%D9%87%E2%80%8C%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ✅ **احراز چندمرحله‌ای محیطی:** کنترل همزمان ۴ عامل (Browser + IP + Domain + Branch) قبل از احیای توکن.
- ✅ **محافظت از سوریه توکن:** جلوگیری از استفاده از JWT در مرورگر یا شبکه دیگر.
- ✅ **تأیید دامنه صدور:** مقایسه مقدار `iss` در هدر JWT با مقدار هدر درخواست (Domain).
- ✅ **دسترسی کنترل‌شده Redis:** بارگذاری میانبرهای رابط سیستم از مسیر cache محدود.
- ⚠️ توصیه: اعمال نرخ محدودسازی (Rate Limiting) برای جلوگیری از حمله بروت‌فورس JWT.

</div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-firebase%5Cjwt%5Cjwt" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Firebase\\JWT\\JWT;
- use Firebase\\JWT\\Key;
- use DeviceDetector\\DeviceDetector;
- use DeviceDetector\\ClientHints;
- use DeviceDetector\\Parser\\AbstractDeviceParser;
- use Carbon\\Carbon;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\User;

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-dev" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه‌کردن بررسی Device ID یکتا برای جلوگیری از جعل User-Agent.
- افزودن refresh-token مجزا برای دسترسی موقت با سطح محدود (scoped access).
- ذخیره لاگ تغییر IPها در SystemLog جهت تحلیل تهدیدات.
- اعمال Expiration Dynamics (IP-based TTL) بر اساس منطقه جغرافیایی کاربر.

</div>

# GET /api/v2/notifications

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/notifications</td><td style="direction: ltr; text-align: left;">V2BaseController@notifications</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">دریافت لیست اعلان‌های سیستمی و به‌روزرسانی‌ها در محیط کاربر</td><td style="direction: ltr; text-align: left;">tags={"Base","Notifications"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87%E2%80%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این Endpoint آرایه‌ای از اعلان‌های عمومی (Public System Notifications) را برمی‌گرداند که معمولاً شامل تغییرات جدید در امکانات، هشدارهای داخلی یا پیام‌های اداری است. در حال حاضر ثابت (Static) است اما معمولاً در محیط Production داده‌ها از جدول `notifications` یا کش Redis تأمین می‌شوند.</div>1. احراز IP و دامنه درخواست از طریق Middlewareهای `domainAccess` و `ipTrust`.
2. تولید آرایه‌ای از اعلان‌ها با فیلدهای کلیدی: 
    - **id:** شناسه یکتا
    - **icon:** آیکن متریال مرتبط با اعلان
    - **title:** تیتر اعلان
    - **description:** متن توضیحی اعلان
    - **time:** زمان ایجاد اعلان به فرمت شمسی
    - **read:** وضعیت مطالعه‌شده بودن توسط کاربر
    - **link:** مسیر واکنش‌پذیر (قابل کلیک در UI)
    - **useRouter:** نوع هدایت (true ⇒ استفاده از Router داخلی SPA)
3. بازگشت خروجی به صورت JSON (بدون نیاز به Token در این نسخه از Endpoint)

</div>### ساختار درخواست (Request)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%AA%D8%AF-%D9%86%DB%8C%D8%A7%D8%B2%DB%8C-%D8%A8%D9%87-%D9%BE%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: right;">این متد نیازی به پارامتر ورودی ندارد.</div></div>```
GET /api/v2/notifications
Domain: example.domain
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>id</td><td>string</td><td style="direction: rtl; text-align: right;">شناسه یکتا برای هر اعلان</td></tr><tr><td>icon</td><td>string</td><td style="direction: rtl; text-align: right;">نام آیکن متریال (برای نمایش تصویری اعلان)</td></tr><tr><td>title</td><td>string</td><td style="direction: rtl; text-align: right;">تیتر کوتاه اعلان</td></tr><tr><td>description</td><td>string</td><td style="direction: rtl; text-align: right;">توضیح کامل محتوای اعلان</td></tr><tr><td>time</td><td>string (datetime)</td><td style="direction: rtl; text-align: right;">تاریخ و ساعت اعلان به فرمت شمسی</td></tr><tr><td>read</td><td>boolean</td><td style="direction: rtl; text-align: right;">وضعیت خوانده‌شدن اعلان توسط کاربر</td></tr><tr><td>link</td><td>string</td><td style="direction: rtl; text-align: right;">مسیر URL یا Route مقصد</td></tr><tr><td>useRouter</td><td>boolean</td><td style="direction: rtl; text-align: right;">True ⇐ استفاده از Router کلاینتی برای تغییر مسیر نرم‌افزاری</td></tr></tbody></table>

</div>### نمونه پاسخ

```
[
  {
    "id": "01",
    "icon": "airline_seat_recline_extra",
    "title": "ویرایش خرید پرواز آنلاین",
    "description": "به اطلاع کلیه همکاران میرسانیم قسمت ویرایش پرواز های آنلاین فعال گردید.",
    "time": "1402-02-02 00:00:00",
    "read": true,
    "link": "/",
    "useRouter": true
  }
]
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-middlewar" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از Middleware **domainAccess** و **ipTrust** برای محدود کردن دسترسی به دامنه‌ها و IPهای مجاز.
- در حالت فعلی نیازی به **توکن JWT** ندارد اما در محیط Production معمولاً با `authWithJwt` ترکیب می‌شود.
- در پاسخ، مقادیر هیچ داده‌ی حساسی برنمی‌گردد — تنها اعلان‌های عمومی هستند.

</div>### توضیح فیلدهای پاسخ

<div id="bkmrk-id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B9%D8%AF%D8%AF%DB%8C-%2F-%D8%B1%D8%B4%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **id:** شناسه عددی / رشته‌ای قابل جست‌وجو برای هر اعلان در کلاینت.
- **icon:** نام آیکن از مجموعه Material Icons برای نمایش در رابط کاربری.
- **title:** تیتر کوتاه در بالای باکس اعلان.
- **description:** پیام اصلی اطلاع‌رسانی.
- **time:** رشته زمان به فرمت "YYYY-MM-DD HH:MM:SS".
- **read:** وضعیت مطالعه (true = خوانده شده).
- **link:** مسیر قابل کلیک داخل سیستم (مثلاً "/dashboard").
- **useRouter:** اگر true باشد، مسیر فوق در Router داخلی اپلیکیشن باز می‌شود بدون رفرش.

</div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Http\\Request;
- use App\\Http\\Controllers\\Controller as BaseController;

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A8%D9%87%D8%A8%D9%88%D8%AF-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بهبود ساختار خروجی برای پشتیبانی از چند اعلان هم‌زمان (pagination و unread count).
- افزودن فیلد `category` برای تفکیک نوع اعلان (مثلاً “news”, “update”, “security”).
- افزودن endpoint جدید: `PATCH /api/v2/notifications/read` برای علامت‌گذاری اعلان‌ها به عنوان خوانده شده.
- ذخیره کش Redis از نوتیفیکیشن‌ها برای سرعت بیشتر پنل.

</div>

# GET /api/v2/exam/get

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/exam/get</td><td style="direction: ltr; text-align: left;">OfficialController@getExam</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">دریافت فرم آزمون (نظرسنجی) برای سفر یا ارزیابی ۳۶۰ درجه همکاران</td><td style="direction: ltr; text-align: left;">tags={"Exam","V2"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-getexam%28%29-%D8%A8%D8%B1-%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع `getExam()` بر اساس نوع درخواست مشخص می‌کند که آزمون از کدام شاخه بارگذاری شود:</div>- **نوع ۱:** `trip` = نظرسنجی خدمات سفر (Trip Survey)
- **نوع ۲:** `360_degree_feedback` = ارزیابی عملکرد همکار به صورت ۳۶۰ درجه

</div>#### منطق کلی:

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-%24request" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی مقدار `$request->type`.
2. اجرای مسیر مرتبط متناسب با نوع آزمون.
3. در صورت یافتن شرایط واجد، تولید ساختار آزمون شامل سؤالات، هدر، فوتر و فیلدهای جایگزین‌شده از داده‌های مالی یا اطلاعات همکار.
4. بازگشت پاسخ JSON شامل `status`، `time` و داده یا پیام خطا.

</div>### ورودی‌ها (Request Parameters)

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>محل</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>Query</td><td>string</td><td>بله</td><td>نوع آزمون؛ مقدارهای مجاز: "trip" یا "360\_degree\_feedback".</td></tr><tr><td>id</td><td>Query</td><td>string|int</td><td>بله</td><td>شناسه مرجع آزمون (Slug سفر یا شناسه همکار).</td></tr><tr><td>branch</td><td>Query</td><td>string|int</td><td>بله</td><td>کد شعبه فعال (برای محدودسازی دامنه آزمون).</td></tr><tr><td>operator</td><td>Header/Auth Context</td><td>object</td><td>اختیاری\*</td><td>در حالت ۳۶۰ درجه برای تشخیص کاربر واردشده مورد نیاز است.</td></tr></tbody></table>

</div>### الف) جریان عملکرد نوع «trip»

<div id="bkmrk-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-%D8%A7%D8%B2-%D8%AC%D8%AF%D9%88%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. یافتن فاکتور از جدول `factors` بر اساس **slug** داده‌شده.
2. بررسی وجود پاسخ قبلی در جدول `exam_response` با فیلدهای: `object_type='reference'` و `object=factor.id`. اگر وجود داشته باشد → پیغام «این سفر قبلاً نظرسنجی شده است.».
3. در غیراین‌صورت: 
    - دریافت داده مالی از Redis با کلید `reference:{id}:information`. در صورت عدم وجود، صدا زدن `TradeController::financial()` و ذخیره در Redis.
    - واکشی کالاهای موجود در فاکتور از جدول `factor_items`.
    - یافتن آزمون فعال با `type=trip_survey` برای همان شعبه.
    - دریافت سؤالات فعال از `exam_questions` مرتب‌شده بر اساس order و id.
    - اعمال فیلتر سؤالات: اگر موضوع `hotel` است، فقط هنگام وجود محصول مرتبط استفاده می‌شود.
    - جایگزینی متغیرهای دینامیک در title، header و footer با متد `replaceExamItem('trip',...)`.
    - ساخت ساختار خروجی نهایی شامل مشخصات آزمون و آرایه سؤالات.

</div>### ب) جریان عملکرد نوع «360\_degree\_feedback»

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%88%D8%AC%D9%88%D8%AF-operator." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی وجود `operator.id`؛ اگر کاربر لاگین نکرده → پیام خطا.
2. عدم تطابق شناسه خواسته‌شده با personnel\_id کاربر فعلی (جلوگیری از خودارزیابی).
3. جست‌وجوی همکار هدف در جدول `operators` با شروط: 
    - `branch JSON contains current branch`
    - `status = 1`، `no_feedback = null`.
4. بررسی اینکه تاریخ استخدام کارمند حداقل ۱ ماه قبل باشد.
5. در صورت گذشت شرط: بررسی عدم وجود پاسخ از کاربر فعلی در ۶ ماه اخیر (`exam_response`).
6. اگر تاکنون پاسخ نداده → واکشی آزمون با `type=360_degree_feedback` و `branch` جاری.
7. واکشی سؤالات و تولید ساختار خروجی مشابه نوع trip، با جایگزینی پارامترهای `{colleague}` و `{position}` در title، header و footer.

</div>### ساختار پاسخ (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td style="direction: rtl; text-align: right;">نتیجه نهایی تابع.</td></tr><tr><td>time</td><td>int (unix timestamp)</td><td style="direction: rtl; text-align: right;">زمان پاسخ سرور (بر حسب ثانیه).</td></tr><tr><td>data</td><td>object|null</td><td style="direction: rtl; text-align: right;">در صورت true حاوی ساختار آزمون کامل است.</td></tr><tr><td>message</td><td>string|null</td><td style="direction: rtl; text-align: right;">پیغام خطا هنگام بروز استثنا.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق (trip):

```
{
  "status": true,
  "time": 1693905000,
  "data": {
    "id": 15,
    "object": 1472,
    "title": "نظرسنجی خدمات پرواز",
    "header": "مشتری گرامی، لطفاً به سفر خود امتیاز دهید.",
    "footer": "با تشکر از همکاری شما",
    "created_at": "1402-02-02 00:00:00",
    "questions": [
      {
        "id": 1,
        "type": "rating",
        "subject": "flight",
        "title": "ارزیابی کیفیت پرواز شما",
        "options": [1,2,3,4,5],
        "description": null,
        "mandatory": 1,
        "score": 5
      }
    ]
  }
}
```

#### نمونه پاسخ خطا:

```
{
  "status": false,
  "time": 1693905001,
  "message": "این سفر قبلا نظرسنجی شده است."
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### تحلیل امنیتی

<div id="bkmrk-%E2%9C%85-middleware-%D8%AF%D9%88%DA%AF%D8%A7%D9%86%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ✅ Middleware دوگانه `domainAccess` و `ipTrust` مانع درخواست‌های خارج از محدوده مجاز می‌شود.
- ⚠️ Endpoint فاقد `authWithJwt` است؛ در حالت فعلی کاربران ناشناس می‌توانند با دانستن `slug` به آزمون سفر دسترسی یابند. در حالت Production پیشنهاد می‌شود JWT فعال شود.
- ✅ داده‌ی حساسی در خروجی وجود ندارد (تنها ساختار آزمون).
- ⚠️ تابع از Redis و DB مستقیم استفاده می‌کند بدون هندل استثناء؛ خطای Redis می‌تواند کل عملیات را fail کند.

</div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\TradeController;

</div>### ساختار داخلی data در پاسخ موفق

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-id-in" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fcfcfc; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>توضیح</td></tr><tr><td>id</td><td>int</td><td style="direction: rtl; text-align: right;">شناسه آزمون.</td></tr><tr><td>object</td><td>int</td><td style="direction: rtl; text-align: right;">شناسه مرجع (سفر یا کارمند).</td></tr><tr><td>title</td><td>string</td><td style="direction: rtl; text-align: right;">عنوان اصلی آزمون.</td></tr><tr><td>header/footer</td><td>string</td><td style="direction: rtl; text-align: right;">متن معرفی و پایان آزمون.</td></tr><tr><td>questions</td><td>array</td><td style="direction: rtl; text-align: right;">لیست سؤالات با فیلدهای id, type, subject, title, options, description, mandatory, score.</td></tr></tbody></table>

</div>### پیغام‌های خطای محتمل

<div id="bkmrk-%E2%9A%A0%EF%B8%8F-%22%D9%84%DB%8C%D9%86%DA%A9-%D9%86%D8%B8%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%85%D8%B9%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ⚠️ **"لینک نظرسنجی معتبر نمی باشد"** → شناسه یا کاربر نامعتبر.
- ⚠️ **"بانک سوالات تعریف نشده است"** → آزمون نوع مربوطه برای این شعبه فعال نشده.
- ⚠️ **"این سفر قبلا نظرسنجی شده است"** یا **"این مورد قبلا نظرسنجی شده است"** → پاسخ قبلی موجود است.
- ⚠️ **"این کارمند هنوز به مرحله ارزیابی 360 درجه نرسیده"** → کمتر از ۱ ماه از ایجاد حساب کارمند گذشته.
- ⚠️ **"شما امکان ارائه نظر به خود را ندارید"** → جلوگیری از Self Feedback.
- ⚠️ **"این نظرسنجی احتیاج به ورود به سیستم دارد."** → بدون Auth.

</div>### نکات کارایی (Performance)

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-redis-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی Redis برای کش اطلاعات فاکتور سرعت واکشی آزمون را افزایش می‌دهد.
- اما هر بار بررسی DB + Redis روی مسیر عمومی ریسک افزایش تاخیر دارد.
- پیشنهاد: تجمیع داده‌ها در یک Service Layer با TTL منطقی ۵ دقیقه‌ای برای Redis Keyها.

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D9%84-logic-%D8%A7%D8%B2-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- انتقال Logic از Controller به Service مستقل (ExamService) جهت پوشش Exception Handling.
- افزودن فیلد `category` یا `tag` به آزمون‌ها جهت فیلتر بهتر در سامانه آموزش.
- درخواست بعدی (**POST /api/v2/exam/response**) جهت ثبت پاسخ آزمون، مکمل همین Endpoint است و بلافاصله باید مستند شود.

</div>

# POST /api/v2/exam/response

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td><td>تگ Swagger</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/exam/response</td><td style="direction: ltr; text-align: left;">OfficialController@responseExam</td><td style="direction: ltr; text-align: left;">domainAccess, ipTrust</td><td style="direction: rtl; text-align: right;">ثبت پاسخ آزمون نظرسنجی (سفر یا ارزیابی ۳۶۰ درجه)</td><td style="direction: ltr; text-align: left;">tags={"Exam","V2"}</td></tr></tbody></table>

</div>### توضیح عملکرد (Function Logic)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%AA%D8%AF-%D9%BE%D8%A7%D8%B3%D8%AE-%DA%A9%D8%A7%D9%85%D9%84-%D8%A2%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این متد پاسخ کامل آزمون را ثبت می‌کند. بر اساس نوع (`trip` یا `360_degree_feedback`)، نوع ذخیره `object_type` تنظیم می‌شود (reference/personnel). ۱. رکورد اصلی **exam\_response** با اطلاعات پایه: شعبه، شناسه آزمون، شیء، شناسه کاربر، نام، موبایل، ایمیل ۲. ثبت هر پاسخ تکی در جدول `exam_responses_item`؛ هر پاسخ شامل شناسه سوال و مقدار پاسخ که خودش می‌تواند آرایه یا رشته باشد (در صورت آرایه json_encode). ۳. اگر آزمون شامل جایزه باشد (فیلد `gift` در جدول `exams`)، ساخت کد جایزه و ارسال ساختار هدیه ۴. امتیازدهی خودکار با StaticController::getScoreOperator ۵. خروجی نهایی: status و زمان، و در صورت تخصیص هدیه، فیلد `gift` ۶. هندل کامل استثنا به صورت خروجی status=false و اطلاعات خطا</div></div>### ورودی‌ها (Request Body)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>نوع آزمون (trip یا 360\_degree\_feedback)</td></tr><tr><td>id</td><td>int</td><td>بله</td><td>شناسه آزمون</td></tr><tr><td>object</td><td>int</td><td>بله</td><td>شناسه سفر (type=trip) یا شناسه همکار (type=360)</td></tr><tr><td>branch</td><td>string|int</td><td>بله</td><td>کد شعبه</td></tr><tr><td>operator</td><td>object</td><td>اختیاری</td><td>شیء کاربر فعلی (در حالت ۳۶۰ درجه الزامی)</td></tr><tr><td>full\_name</td><td>string</td><td>اختیاری</td><td>نام کامل ثبت‌کننده</td></tr><tr><td>mobile</td><td>string</td><td>اختیاری</td><td>شماره موبایل ثبت‌کننده</td></tr><tr><td>email</td><td>string</td><td>اختیاری</td><td>ایمیل ثبت‌کننده</td></tr><tr><td>responses</td><td>array</td><td>بله</td><td>آرایه پاسخ هر سوال. هر آیتم: {question: int, response: string|array}</td></tr></tbody></table>

</div>### خروجی (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td style="direction: rtl; text-align: right;">نتیجه عملیات ثبت</td></tr><tr><td>time</td><td>int</td><td style="direction: rtl; text-align: right;">زمان ثبت (unix timestamp)</td></tr><tr><td>gift</td><td>object|false</td><td style="direction: rtl; text-align: right;">اگر آزمون جایزه دارد، شیء {"title", "code"}</td></tr><tr><td>message</td><td>string</td><td style="direction: rtl; text-align: right;">توضیح خطا (در حالت failure)</td></tr><tr><td>trace</td><td>array</td><td style="direction: rtl; text-align: right;">اطلاعات error (در حالت failure)</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
POST /api/v2/exam/response
Content-Type: application/json

{
  "type": "trip",
  "id": 15,
  "object": 1472,
  "branch": "8",
  "operator": { "id": 22 },
  "responses": [
    { "question": 1, "response": "5" },
    { "question": 2, "response": ["Clean", "Comfort"] }
  ]
}
```

#### نمونه پاسخ موفق:

```
{
  "status": true,
  "time": 1693905251,
  "gift": { "title": "تخفیف ویژه", "code": "140303-12345" }
}
```

#### نمونه پاسخ خطا:

```
{
  "status": false,
  "time": 1693905252,
  "message": "SQLSTATE[23000]: Integrity constraint violation ...",
  "trace": [ ... ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### تحلیل امنیتی

<div id="bkmrk-%E2%9C%85-%D8%AD%D9%81%D8%A7%D8%B8%D8%AA-realm-%D8%A8%D8%A7-mid" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ✅ حفاظت Realm با Middleware `domainAccess` و `ipTrust`
- ⚠️ هیچ اعتبارسنجی برای صحت ساختار responses یا عدم وجود تکرار پاسخ وجود ندارد؛ نقطه ضعف قابل سوءاستفاده.
- ⚠️ برای آزمون‌های "360"، اگر operator معتبر نباشد سیستم بدون خطای خوانا fail می‌کند.
- ⚠️ داده‌های contact (نام، موبایل، ایمیل) بدون هیچ sanitation در DB ثبت می‌شود: ریسک تزریق و Spamming exists.
- ✅ Exception هندلینگ کامل انجام می‌شود؛ Error Trace در خروجی ثبت می‌شود.
- ⚠️ هیچ Throttling یا تکرارپذیری ضد اسپم روی این route دیده نمی‌شود.

</div>### وابستگی‌ها (Dependencies)

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Morilog\\Jalali\\Jalalian;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\StaticController;

</div>### نکات کارایی و ضعف منطقی

<div id="bkmrk-%D9%87%D8%B1-%D9%BE%D8%A7%D8%B3%D8%AE-%D8%A8%D9%87-%D8%B3%D8%B1%D8%B9%D8%AA-%D8%AF%D8%B1-d" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر پاسخ به سرعت در DB ثبت می‌شود اما هیچ validation سمت سرور نیست (حتی اگر responses کاملاً بی‌ربط باشند).
- فرایند تولید کد هدیه وابسته به id کلیدی است و قابل پیش‌بینی توسط مهاجم.
- در صورت خطای پایگاه داده، جزئیات با `trace` به مشتری نمایش داده می‌شود که ریسک اطلاعاتی دارد.

</div>### پیوست نگهداری و توسعه بعدی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%AF%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن اعتبارسنجی دقیق responses و sanitation اطلاعات مبتنی بر فیلدهای contact.
- افزودن throttling/limiting بر اساس operator و سفر برای جلوگیری از brute force و spam.
- تعریف وابستگی به احراز هویت JWT در نسخه بعدی.
- افزودن فیلدهای custom جهت اتوماسیون هدیه (مثلاً expiry/used count).

</div>

# POST /api/v2/trade/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95.9524%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td style="width: 19.9851%;">Method</td><td style="width: 19.9851%;">Endpoint</td><td style="width: 19.9851%;">Controller</td><td style="width: 19.9851%;">Middleware</td><td style="width: 19.9851%;">Purpose</td></tr><tr><td style="direction: ltr; text-align: left; width: 19.9851%;">POST</td><td style="direction: ltr; text-align: left; width: 19.9851%;">/api/v2/trade/list</td><td style="direction: ltr; text-align: left; width: 19.9851%;">V2TradeController@getTradesList</td><td style="direction: ltr; text-align: left; width: 19.9851%;">authWithJwt</td><td style="direction: rtl; text-align: right; width: 19.9851%;">نمایش فهرست معاملات (فاکتورها) با فیلترهای پیشرفته</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-gettradeslist-%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **getTradesList** لیست فاکتورها (تراکنش‌ها / معاملات) را برای شعبهٔ جاری واکشی می‌کند. در ابتدا داده‌های ورودی از پارامتر `json` تجزیه می‌شوند؛ شامل تنظیمات DataTable مانند `start`، `length`، `draw` و فیلترهای `advanced`. اگر کاربر هیچ جستجویی انجام نداده باشد، سیستم به‌صورت پیش‌فرض فقط فاکتورهای همان روز را نمایش می‌دهد. سپس مجموعه‌ای از شرایط فیلترینگ اعمال می‌شود: - فیلتر نوع پیشرفته (advanced) بر اساس شماره رزرو (`r`)، محدوده تاریخ (`from`, `to`), اپراتور، وضعیت، شماره پرواز، وسیله، تعهد‌کننده، مسیر و درآمد.
- محاسبه و ترکیب چندین آرایهٔ ID برای محدودسازی نتایج: `$arr_id_fn`, `$arr_id_d`, `$arr_id_fu_d`, `$arr_id_p`.
- اعمال محدودیت دسترسی (Access Control) بر اساس سطح دسترسی اپراتور از طریق تابع `Functions::getAccessUser('trade', ...)`.
- اتصال به Redis جهت کش (cache) اطلاعات مالی و تعهدکنندگان هر فاکتور با کلیدهای `reference:{id}:pledgers` و `reference:{id}:information`.
- محاسبهٔ شاخص‌های مالی کل برای لیست بازگشتی: مجموع خرید، فروش، سود، بدهی، بستانکاری، تخفیف، تسهیلات، تعداد مسافر.
- بازچینی داده برای هر معامله به صورت ساختارمند در قالب `items[]` (جهت نمایش جدول).
- خروجی استاندارد DataTable-compatible شامل فیلدهای `draw`, `recordsTotal`, `recordsFiltered`, `total`, و `data[]`.

</div></div>### ورودی‌ها (Request Fields)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>int|string</td><td>بله</td><td>شناسه شعبه‌ای که اپراتور به آن متصل است</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>شیء اپراتور استخراج‌شده از JWT</td></tr><tr><td>json</td><td>object (JSON string)</td><td>بله</td><td>حاوی ساختار DataTable شامل start، length، draw و فیلترهای advanced</td></tr></tbody></table>

</div>#### ساختار فیلتر پیشرفته (advanced):

```
"advanced": {
  "r": "", "from": "2025-01-01", "to": "2025-01-31",
  "op": 0, "status": 1, "flightno": "W51123", "dt_departure": "2025-01-12",
  "pledger": "colleague-42", "vehicle": "aircraft", "income": "", "route": ""
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>draw</td><td>int</td><td>شماره فراخوان DataTable</td></tr><tr><td>recordsTotal</td><td>int</td><td>تعداد کل فاکتورها مطابق فیلتر</td></tr><tr><td>recordsFiltered</td><td>int</td><td>تعداد پس از فیلترینگ</td></tr><tr><td>total</td><td>object</td><td>مجموع کل شاخص‌های مالی (Buy, Sale, Profit, …)</td></tr><tr><td>data</td><td>array</td><td>آرایه‌ای از معاملات شامل جزئیات Route، Operator، Pledger و Financial</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
POST /api/v2/trade/list
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "branch": 8,
  "json": {
    "draw": 1,
    "start": 0,
    "length": 15,
    "search": {"value": ""},
    "advanced": {"from":"2025-01-01","to":"2025-01-31","status":"1"}
  }
}
```

#### نمونه پاسخ:

```
{
  "draw": 1,
  "recordsTotal": 52,
  "recordsFiltered": 52,
  "total": {
    "Buy": 9200000,
    "Sale": 11300000,
    "Profit": 2100000,
    "Debit": 150000,
    "Credit": 280000,
    "Discount": 30000,
    "Passenger": 91
  },
  "data": [ { "SerialId": 2048001, "Income":"direct", "RouteTitle":"پرواز تهران-کیش", ... } ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت و کنترل دسترسی

<div id="bkmrk-%D9%84%D8%A7%DB%8C%D9%87%D9%94-%D8%A7%D9%85%D9%86%DB%8C%D8%AA%DB%8C-jwt-%D8%AA%D9%88%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- لایهٔ امنیتی JWT توسط میدل‌ور `AuthWithJWT`، بررسی توکن و اعتبار کاربر از جداول مختلف (`operators`, `customers`, `colleague_auth`).
- اگر کاربر غیرفعال باشد یا JWT منقضی شده باشد → Error Code 1002 یا 1006.
- هیچ Permissions Role-Based دقیق برای فیلدهای مالی در Endpoint اعمال نمی‌شود، فقط سطح general access کنترل می‌شود.
- اطلاعات مالی از Redis کش خوانده شده و فاقد رمزنگاری است. خطر افشای اطلاعات مالی وجود دارد.
- هیچ validation روی ساختار داخلی `json` اعمال نشده — ممکن است دادهٔ تزریق‌شده منجر به crash شود.

</div>### نکات کارایی و پیاده‌سازی

<div id="bkmrk-%D9%BE%D9%86%D8%AC-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D9%85%D8%AC%D8%B2%D8%A7-%D8%A8%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پنج درخواست مجزا به DB در هر ثبت معامله (Factor + FactorItem + Pledger + Redis fetch/write).
- NULL caching در Redis هرگز منقضی نمی‌شود، حافظه بدون مدیریت رشد می‌کند.
- در صورت آغاز بدون فیلتر، تمام روز جاری اسکن می‌شود (query سنگین بدون ایندکس مناسب).
- محاسبه‌های مالی مجدد زمانی انجام می‌شود که Cache خالی باشد → افزایش CPU.
- درخواست‌های موازی بدون کنترل Lock روی Redis باعث Out-of-sync در Cache می‌شود.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5Ccarbon%3B-u" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\Carbon;
- use Morilog\\Jalali\\Jalalian;
- use Illuminate\\Support\\Facades\\Redis;
- use Illuminate\\Support\\Facades\\DB;
- use App\\Models\\{Factor, FactorItem, Pledger, User, Colleague};
- use App\\Http\\Controllers\\Api\\Panel\\V2\\ApiTradeController;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\TradeController;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\StaticController;
- use App\\Helpers\\Functions;

</div>### کدهای خطا و خروجی‌های Exception

<div id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-1005" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>1005</td><td>Token not provided</td><td>AuthWithJWT</td></tr><tr><td>1006</td><td>Your token is invalid or expired</td><td>AuthWithJWT</td></tr><tr><td>1002 / 1003 / 1004</td><td>User does not have access permission</td><td>AuthWithJWT</td></tr><tr><td>500</td><td>Database / Redis Exception → Trace exposed</td><td>V2TradeController@getTradesList</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-expiration-%D8%A8%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن expiration به Redis cache (TTL 10m).
- قرار دادن validation برای json input و advanced filters.
- پنهان‌سازی جزئیات trace در پاسخ خطا.
- محدودسازی سطح اطلاعات مالی بر اساس Role.

</div>### پیشنهادهای بهبود

<div id="bkmrk-refactor-%D9%85%D9%86%D8%B7%D9%82-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Refactor منطق فیلترینگ در QueryBuilder جداگانه (TradeQueryService).
- ایجاد ایندکس ترکیبی روی فیلدهای `created_at`، `status` و `branch`.
- استانداردسازی خروجی Financial در endpoint مستقل.
- کاهش حجم response با pagination سمت سرور واقعی (بدون pull تمام ستون‌ها).

</div>### ممیزی دسترسی و عدم قطعیت

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%A7-functio" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی‌ها با `Functions::getAccessUser` کنترل می‌شوند اما audit در سطح route وجود ندارد.
- هیچ log مشخصی برای view یا export ثبت نمی‌شود؛ باید logViewTrade اضافه شود.

</div>### جمع‌بندی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%DB%8C%DA%A9%DB%8C-%D8%A7%D8%B2-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این Endpoint یکی از مرکزی‌ترین بخش‌های سیستم حسابداری تجاری است. امنیت پایه‌ای به لطف JWT حفظ شده اما حجم داده بزرگ، منطق فیلتر پیچیده و عدم validation ورودی می‌تواند منبع اشکالات عملکردی یا امنیتی شود. برای تبدیل این منطق به ساختار enterprise-grade، نیاز به جداسازی cache layer، اضافه‌کردن input schema validation و تعریف audit trail قطعی است.</div></div>

# POST /api/v2/trade/search

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/search</td><td style="direction: ltr; text-align: left;">V2TradeController@searchTrades</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">جستجوی معاملات/فاکتورها با فیلترهای چندلایه و ترجمه خودکار مسیرها</td></tr></tbody></table>

</div>### منطق عملکرد و مسیر داده

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-searchtrades%D8%8C-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **searchTrades**، درخواست را بر اساس فیلترهای `search` و `paginate` پردازش می‌کند: - تبدیل فیلدهای عددی به رشته خالی برای جلوگیری از فیلترینگ بی‌هدف.
- بررسی کامل ورود **from** و **to**؛ در صورت نبود خروجی error (code 1000).
- ساخت ترکیبی از جستجوهای پیشرفته روی معاملات—فیلدهای: تاریخ، شماره رزرو، اپراتور، تعهد‌کننده، شماره/تاریخ پرواز، محصول، وضعیت، مسیر، درآمد و اطلاعات مسافر؛ از کوئری‌های فشرده با شرط‌های تو در تو استفاده می‌شود.
- تمام توصیف‌های مسیر (route) با ترجمه‌های فارسی/انگلیسی زده می‌شوند؛ مثل "هتل"، "مسیر"، "تور"، "پرواز" با ترکیب‌های محصول و جزئیات.
- اطلاعات تکمیلی هر آیتم از Redis یا DB و کلاس‌های کمکی خوانده و کش می‌شود.
- نتیجه نهایی آرایه‌ای از معاملات با جزییات کامل مسیر و اطلاعات مالی است—پاسخ با meta ساختار یافته و صفحه‌بندی.

</div></div>### ورودی‌ها (Request Fields)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>int|string</td><td>بله</td><td>شناسه شعبه فعال</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>اپراتور (از JWT middleware)</td></tr><tr><td>search</td><td>object</td><td>بله</td><td>شی جستجو با فیلدهایی مانند: r، from، to، op، flightno، dt\_departure، passenger، status و...</td></tr><tr><td>paginate</td><td>object</td><td>بله</td><td>پارامترهای صفحه بندی: start، length</td></tr></tbody></table>

</div>#### نمونه Search Structure:

```
{
  "branch": 8,
  "search": {
    "from": "2025-02-01", "to": "2025-02-12",
    "status": "1", "flightno": "W51005",
    "op": 22, "pledger": "operator-12"
  },
  "paginate": {
    "start": 0,
    "length": 15
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>items</td><td>array</td><td>آرایه‌ای از معاملات (ساختار زیر)</td></tr><tr><td>meta</td><td>object</td><td>شاخص‌های صفحه‌بندی (timestamp, total, page, per\_page, last\_page, current\_page)</td></tr></tbody></table>

</div>#### ساختار هر آیتم در خروجی:

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-datet" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>توضیح</td></tr><tr><td>datetime</td><td>object</td><td>تاریخ و ساعت صدور معامله</td></tr><tr><td>type</td><td>object</td><td>نوع محصول و مسیر</td></tr><tr><td>income</td><td>string</td><td>نوع درآمد</td></tr><tr><td>route\_type</td><td>string</td><td>نوع اصلی خدمت (aircraft, bus, hotel, ...)</td></tr><tr><td>pledger</td><td>array|null</td><td>تعهدکنندگان معامله با جزئیات</td></tr><tr><td>route\_title</td><td>string</td><td>توضیح مسیر یا هتل یا سرویس، با ترجمه فارسی یا انگلیسی هوشمند</td></tr><tr><td>operator</td><td>object</td><td>اطلاعات اپراتور ثبت‌کننده</td></tr><tr><td>leader</td><td>object</td><td>لیدر مسیر (در صورت وجود)</td></tr><tr><td>count\_passengers</td><td>int</td><td>تعداد مسافرها</td></tr><tr><td>status</td><td>int</td><td>وضعیت معامله</td></tr><tr><td>description</td><td>string|false</td><td>توضیح در صورت status=2,5</td></tr><tr><td>serial</td><td>int</td><td>شماره رزرو سیستمی</td></tr><tr><td>factor\_id</td><td>int</td><td>شماره رزرو پایه</td></tr><tr><td>system\_serial</td><td>int</td><td>شماره داخلی فاکتور</td></tr><tr><td>slug</td><td>string</td><td>شناسه مسیر یکتا</td></tr><tr><td>suppliers</td><td>array</td><td>فهرست تامین‌کننده‌ها</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "items": [
    {
      "date": { "title": "11/22", "placeholder": "سه‌شنبه، 1404/12/23 14:32" },
      "route_title": "تهران به کیش | W51005",
      "type": { "title": "aircraft", "placeholder": "هواپیما" },
      "pledger": [ { "Id": 12, "Title": "جواد مقیمی", "Amount": 250000 } ],
      "suppliers": [],
      ...
    }
  ],
  "meta": {
    "timestamp": 1693905251,
    "total": 29,
    "page": 15,
    "per_page": 15,
    "last_page": 2,
    "current_page": 2
  }
}
```

#### نمونه پاسخ خطا (تاریخ ناقص):

```
{
  "error": {
    "code": 1000,
    "message": "لطفا تاریخ شروع جستجو و پایان جستجو را وارد نمائید."
  },
  "meta": {
    "timestamp": 1693905251
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### تحلیل امنیتی و کنترل خطا

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF-%D8%A8%D9%87-route-%DA%A9%D8%A7%D9%85%D9%84%D8%A7%D9%8B" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ورود به Route کاملاً وابسته به JWT؛ اگر توکن معتبر نباشد یا کاربر مجاز نباشد، خطای 1002/1003/1004 یا 1005/1006 برمی‌گردد.
- ساختار ورودی به هیچ‌وجه با Schema validate نمی‌شود، زمینه‌ساز حملات Injection یا نقض منطق جستجو است.
- اطلاعات تکمیلی و ترجمه‌شده مسیر از Redis بدون TTL ذخیره می‌شود—cache poisoning یا stale data ممکن است رخ دهد.
- در پاسخ خطا (date missing)، هیچ جزئیات sensitive داده نمی‌شود اما سایر باگ‌های منطقی ممکن است باعث لو رفتن ساختار پایگاه داده شوند.
- اگر سایز صفحه‌بندی خیلی بزرگ تنظیم شود، منجر به لحظه‌ای شدن مصرف RAM می‌شود و هیچ کنترل فشاری ندارد.

</div>### نکات کارایی و ضعف طراحی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D9%85%DA%A9%D8%B1%D8%B1-%D9%88-%D9%86%D8%A7%D8%AF%D8%B1%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده مکرر و نادرست از Redis بدون TTL باعث رشد بی‌رویه cache و سنگین‌شدن حافظه سرور می‌شود.
- رجوع مکرر به DB برای اطلاعات supplement (hotel, airline) در هر جستجو، در مسیرهای حجیم باعث افت عملکرد است.
- ساختار جستجو تو در تو باعث سردرگمی منطق ذخیره نتیجه و ترکیب شروط‌ها می‌شود؛ لازم است Refactor سرویس جستجو انجام شود.

</div>### وابستگی‌های کلیدی

<div id="bkmrk-use-carbon%5Ccarbon%3B-u" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\Carbon;
- use Morilog\\Jalali\\Jalalian;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\{Factor, FactorItem, Pledger, User, Colleague, Hotel, Airport, City};
- use App\\Http\\Controllers\\Api\\Panel\\V2\\ApiTradeController;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\StaticController;
- use App\\Helpers\\Functions;
- use CalendarUtils;

</div>### کدهای خطا و خروجی‌های Exception

<div id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-1000" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>1000</td><td>لطفا تاریخ شروع جستجو و پایان جستجو را وارد نمائید.</td><td>searchTrades (application logic)</td></tr><tr><td>1002, 1003, 1004</td><td>User does not have access permission</td><td>authWithJwt</td></tr><tr><td>1005</td><td>Token not provided</td><td>authWithJwt</td></tr><tr><td>1006</td><td>Your token is invalid or expired</td><td>authWithJwt</td></tr><tr><td>500</td><td>Database / Redis Exception → Trace</td><td>searchTrades, middleware</td></tr></tbody></table>

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-ttl-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D9%84%DB%8C%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن TTL برای کلیدهای Redis، با حذف اتوماتیک پس از ۵ دقیقه.
- اعمال validation ساختاری روی ورودی‌ها قبل از اجرای search (JSON Schema/DTO).
- جداسازی سرویسی جستجو و ترجمه route برای کد محصول.
- کنترل حد نهایی صفحه‌بندی (length &amp; start) جهت جلوگیری از سوءاستفاده.

</div>### جمع‌بندی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%D8%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این Endpoint جستجو، یکی از پرریسک‌ترین و پیچیده‌ترین نقاط جریان معاملاتی است. قدرت زیادی با منطق ترکیبی و ترجمه هوشمند دارد، اما ضعف‌های امنیتی و عملکردی باعث می‌شود اگر مهندسی نشده باشد، اثرش کشنده باشد. پیشنهاد اکید: جداسازی سرچ‌سرویس، اعتبارسنجی ورودی، و مهار cache.</div></div>

# POST /api/v2/trade/cost-benefit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### اطلاعات مسیر (Route Info)

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/cost-benefit</td><td style="direction: ltr; text-align: left;">V2TradeController@costBenefit</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">گزارش‌گیری سود و زیان معاملات در بازه زمانی دلخواه با تفکیک گروهی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-costbenefit-%DA%AF%D8%B2%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **costBenefit** گزارش تحلیلی از عملکرد مالی را با معیارهای `supplier`، `provider`، `operator`، `income` و `route` تولید می‌کند. روند اجرا به‌ترتیب زیر است: - دریافت بدنه درخواست با پارامتر `json` و دیکود آن به آبجکت **$Data**.
- محاسبه بازه زمانی بر اساس **from** و **to**؛ اگر تعیین نشده باشد، از اول ماه جاری تا امروز.
- واکشی معاملات (فاکتورها) از جدول `factors` با شرط مشتری غیر‌ تهی و وضعیت معتبر (غیر از ۲ و ۵).
- دریافت جزئیات مالی هر فاکتور از Redis (کلیدهای `reference:{id}:information` و `reference:{id}:pledgers`). در صورت عدم وجود در کش، با متد `ApiTradeController::financial` تولید و ذخیره می‌شود.
- بر اساس مقدار `$Data->action`، محاسبات مربوط به گروه هدف انجام می‌شود. برای هر گروه: 
    - تولید ساختار مالی شامل فیلدهای خرید، فروش، سود، مزد، بدهکار، بستانکار و مسافر.
    - به‌روزرسانی شمارنده‌ها و جمع کل اعداد در آرایه‌های `Total*` و `CountReferences*`.
    - ذخیره‌سازی نتایج در آرایه دو سطحی `$items`.
- در انتها، داده‌ها برای رسم نمودارها (bar و treemap) ساختاردهی می‌شود، شامل `categories`، ‌`count_references`، `count_passengers`، و `count_profit`.
- نتایج نهایی در قالب آرایه خروجی شامل کل‌ها و داده‌های تفکیک‌شده بازمی‌گردد.

</div></div>### ورودی‌ها (Request Parameters)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>json</td><td>JSON string</td><td>بله</td><td>شیء شامل زیرپارامترهای تحلیل عملکرد</td></tr><tr><td>branch</td><td>int</td><td>بله</td><td>شناسه شعبه مورد بررسی</td></tr></tbody></table>

</div>#### ساختار نمونه محتوای json:

```
{
  "from": "2025-10-01",
  "to": "2025-10-31",
  "action": "all"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-total" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>توضیح</td></tr><tr><td>total</td><td>object</td><td>اطلاعات تجمیعی نهایی برای هر گروه (سو‌د، خرید، فروش، بدهکاری، بستانکاری و...)</td></tr><tr><td>chart</td><td>object</td><td>داده‌های پردازش‌شده برای نمودارهای آماری (bar و treemap)</td></tr><tr><td>data</td><td>array|false</td><td>لیست آیتم‌های تفکیک‌شده بر اساس نوع تحلیل (supplier/provider/route/...)</td></tr><tr><td>diff</td><td>array</td><td>لیست شناسه‌هایی که اطلاعات Provider آنها ناقص بوده است</td></tr><tr><td>search</td><td>object</td><td>بازه زمانی اعمال‌شده بر اساس ورودی (شیء شامل from و to)</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "total": {
    "provider": {
      "Buy": 54000000,
      "Sale": 69800000,
      "Profit": 12000000,
      "Wage": 2000000,
      "Debit": 32000000,
      "Credit": 12000000,
      "Passenger": 48,
      "References": 17
    },
    ...
  },
  "chart": {
    "provider": {
      "categories": ["آژانس الف", "آژانس ب"],
      "count_references": [17, 13],
      "count_passengers": [48, 32],
      "count_profit": [1.2, 0.9],
      "treemap": {
        "international": {"name":"international","data":[{"x":"آژانس الف","y":1.2}]}
      }
    }
  },
  "data": {...},
  "diff": [],
  "search": {"from":"2025-10-01T00:00:00Z","to":"2025-10-31T23:59:59Z"}
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### جریان داده (Data Flow)

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%A8%D8%A7-json" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. ورود درخواست با JSON شامل بازه و نوع تحلیل.
2. خواندن فاکتورها از DB (با branch و زمان) → select(id,operator,created\_at).
3. واکشی مالی از Redis → اگر خالی بود: `ApiTradeController::financial()` اجرا و ذخیره می‌شود.
4. واکشی تعهدها (pledgers) از DB یا Redis.
5. محاسبات تفکیکی مالی برای بخش‌های فعال.
6. تجمیع اعداد کل در آرایه‌های Total\*.
7. تولید گزارش نهایی شامل دسته‌بندی‌ها و داده‌های نموداری.

</div>### عملکرد و کش

<div id="bkmrk-redis-%D8%A8%D9%87%E2%80%8C%D8%B9%D9%86%D9%88%D8%A7%D9%86-%D9%85%D9%86%D8%A8%D8%B9-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Redis به‌عنوان منبع موقت کل داده‌های مالی فاکتور مورد ‌استفاده است (`reference:{id}:information` و `pledgers`).
- TTL برای کلیدها تنظیم نشده است → داده‌ها انباشته می‌شوند و حافظه Redis سریع پر می‌شود.
- هر بار اجرای full analysis می‌تواند چند هزار hit روی Redis داشته باشد.
- کش عنوان درآمد (`accounting:title:trade_income`) به‌شکل shared global نگهداری می‌شود.

</div>### امنیت و کنترل ورودی

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ورود JWT الزامی است (middleware: `authWithJwt`).
- ورودی JSON اعتبارسنجی نمی‌شود → احتمال Null reference و خطای Logic وجود دارد.
- پارامتر **branch** می‌تواند از شعب غیرمجاز بیاید؛ پیشنهاد اعتبارسنجی با access levels داخلی.
- عدم نوع‌دهی روی فیلدهای مالی در Redis می‌تواند منجر به حملات تزریق داده از کش آلوده شود.

</div>### Dependencies

<div id="bkmrk-use-carbon%5Ccarbon%3B-u" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\Carbon;
- use Morilog\\Jalali\\Jalalian;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\{Factor, Pledger, User, Colleague};
- use App\\Http\\Controllers\\Api\\Panel\\V2\\ApiTradeController;
- use App\\Http\\Controllers\\Api\\Panel\\V2\\StaticController;

</div>### خطاها و حالت‌های خاص

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%86%D8%A8%D9%88%D8%AF-%D8%A8%D8%A7%D8%B2%D9%87-%D8%B2%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت نبود بازه زمانی، از ابتدای ماه جاری استفاده می‌شود.
- اگر financially ناقص باشد، داده در diff ثبت می‌شود.
- اگر هیچ فاکتوری یافت نشود، پاسخ data=false بازمی‌گردد.
- خطای Redis یا ناهمخوانی داده JSON ممکن است خطای 500 در runtime تولید کند.

</div>### پیچیدگی زمانی و منابع

<div id="bkmrk-%D9%85%D8%B1%D8%AD%D9%84%D9%87-o-complexity-%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 60%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>مرحله</td><td>O-Complexity</td></tr><tr><td>واکنش DB (Factor Select)</td><td>O(N)</td></tr><tr><td>Redis Access</td><td>O(2N)</td></tr><tr><td>تحلیل مالی nested</td><td>O(N × M)</td></tr><tr><td>ترتیب‌دهی (usort)</td><td>O(N log N)</td></tr></tbody></table>

</div>### پیشنهادهای بهینه‌سازی

<div id="bkmrk-%D8%AA%D8%B9%D8%B1%DB%8C%D9%81-ttl-%D8%A8%D8%B1%D8%A7%DB%8C-cache" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تعریف TTL برای cache reference (مثلاً ۶ ساعت).
- رباتیک‌سازی prefetch داده مالی در background jobs.
- جداسازی ماژول گزارش از API اصلی برای جلوگیری از ایجاد latency در runtime.
- استفاده از **chunkRead** برای فاکتورهای زیاد (بیش از ۱۰۰۰ رکورد).

</div>### جمع‌بندی

<div id="bkmrk-post-%2Fapi%2Fv2%2Ftrade%2Fc" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">**POST /api/v2/trade/cost-benefit** قلب تپنده آنالیز مالی سیستم است. قدرتش در دقت آمار گروهی حک شده، اما ضعفش در عدم کنترل کش و تایپ‌ورودی‌هاست. در ساختار فعلی مناسب برای پردازش‌های تحلیلی آفلاین (batch job) است، نه اجرای زنده برای کاربران چندگانه.</div></div>

# POST /api/v2/references/{type}/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/references/{type}/list</td><td style="direction: ltr; text-align: left;">V2TradeController@referenceCreditDebit</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست مراجع مالی (بدهکار/بستانکار) بر اساس نوع و جستجوی دلخواه</td></tr></tbody></table>

</div>### منطق عملکرد و مسیر داده

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-referencecredit" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">- تابع **referenceCreditDebit** مقدار مسیر `{type}` را بررسی کرده و نوع **credit** یا **debit** را مشخص می‌کند.
- بر اساس مقدار `type`، داده‌های مرتبط از جداول مرجع مربوط (مانند `Reference`، `FinanceAccount` یا کش Redis) واکشی می‌شود.
- در صورت وجود کلیدهای `filter`، فیلترینگ سطح دیتابیس یا cached data انجام می‌شود.
- تمام آیتم‌ها با متدهای کمکی Formatted و با ساختار استاندارد JSON بازگردانده می‌شوند.
- اگر مقدار type اشتباه داده شود یا خالی باشد، خروجی با کد خطای 1000 و پیام خطا برمی‌گردد.

</div></div>### ورودی‌ها (Request Fields)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>نوع منبع مالی: `credit` یا `debit`</td></tr><tr><td>filter</td><td>object|null</td><td>خیر</td><td>فیلترهای انتخابی، مثل active=true یا company\_id</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
{
  "filter": { "company_id": 12, "active": true }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response Structure)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>string</td><td>وضعیت پاسخ (success یا error)</td></tr><tr><td>message</td><td>string</td><td>پیام وضعیت</td></tr><tr><td>data</td><td>array</td><td>آرایه‌ای از مراجع مالی فیلترشده</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "status": "success",
  "message": "Reference list retrieved successfully",
  "data": [
    { "id": 1, "title": "بانک ملت", "type": "credit", "balance": 3200000 },
    { "id": 2, "title": "مشتری ویژه", "type": "debit", "balance": -170000 }
  ]
}
```

#### نمونه پاسخ خطا:

```
{
  "status": "error",
  "message": "Invalid type parameter"
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### تحلیل امنیتی و کنترل خطا

<div id="bkmrk-%D9%81%D9%82%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربران احراز هویت‌شده با JWT می‌توانند به این مسیر درخواست بفرستند.
- در صورت تقلب در مقدار type یا ارسال داده مخرب در filter (مانند SQL Injection)، تابع مقدار نامعتبر را رد می‌کند.
- در نسخه حاضر هیچ throttle یا rate-limit اعمال نشده است؛ پیشنهاد می‌شود پس از فعال‌سازی در نسخه بعدی.

</div>### نکات کارایی

<div id="bkmrk-%D8%AF%D8%B1%D8%B5%D9%88%D8%B1%D8%AA-%D9%81%D8%B9%D8%A7%D9%84-%D8%A8%D9%88%D8%AF%D9%86-red" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- درصورت فعال بودن Redis cache، سرعت پاسخ‌دهی زیر ۳۰ میلی‌ثانیه است.
- درصورت نبود cache یا فیلتر dynamic، کوئری روی جدول Reference اجرا می‌شود.
- در صورت حجم زیاد داده‌ها، پیشنهاد استفاده از pagination.

</div>### وابستگی‌های کلیدی

<div id="bkmrk-use-illuminate%5C%5Csupp" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\\\Support\\\\Facades\\\\Redis;
- use App\\\\Models\\\\Reference;
- use App\\\\Helpers\\\\Functions;
- use App\\\\Http\\\\Controllers\\\\Api\\\\Panel\\\\V2\\\\V2TradeController;

</div>### کدهای خطا و حالت‌های Exception

<div id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-1000" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>1000</td><td>پارامتر type نامعتبر است</td><td>referenceCreditDebit logic</td></tr><tr><td>1002‑1006</td><td>JWT نامعتبر یا منقضی / دسترسی غیرمجاز</td><td>authWithJwt middleware</td></tr><tr><td>500</td><td>Database / Redis Exception</td><td>referenceCreditDebit</td></tr></tbody></table>

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-ttl-%D8%A8%D9%87-cache-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن TTL به cache لیست مراجع برای جلوگیری از stale data.
- اضافه‌کردن validation ساختاری برای filter object.
- پشتیبانی از pagination برای داده‌های حجیم.
- ثبت log زمانی برای مانیتورینگ درخواست‌های مکرر بر اساس کاربر.

</div>### جمع‌بندی

<div id="bkmrk-root" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این Endpoint برای واکشی سریع و ساده مراجع مالی طراحی شده است. با ساختار JSON استاندارد و پشتیبانی از فیلترگذاری ساده در عین حال با قابلیت افزودن cache – بهینه و سریع است. با افزودن لایه validation و TTL در cache می‌تواند کاملاً پایدار و قابل اطمینان در سیستم مالی شود.</div></div>

# POST /api/v2/trade/store

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/store</td><td style="direction: ltr; text-align: left;">V2TradeController@storeTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ثبت تراکنش خرید/رزرو با جزئیات مسافران، آیتم‌ها، پرداخت و نوتیفیکیشن</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- شروع تراکنش دیتابیس `DB::transaction` برای اتمیک بودن کل فرآیند.
- دریافت داده‌های فاکتور اصلی و تولید `serial` یونیک با `StaticController::getSerialId`.
- ثبت یا به‌روزرسانی اطلاعات مسافران در جدول `customers` با اعتبارسنجی مدارک.
- پردازش آرایه آیتم‌ها (`data[]`) بر مبنای نوع (هواپیما، قطار، هتل، اقامتگاه، بیمه، خدمات...).
- در حالت رزرو آنلاین: اتصال به سرویس تأمین‌کننده، تأیید `lock` و `book`، ذخیره جزئیات در `factor_items`.
- در صورت شرایط خاص: درج داده‌های هاب در `hub_reservation` و محاسبه کارمزدها.
- ثبت پرداخت‌ها در جدول `pays`، بروزرسانی بدهکاری/بستانکاری در `wallet`.
- ارسال نوتیفیکیشن شخصی‌سازی شده با متن و برند شعبه.
- در صورت خطا: توقف تراکنش و گزارش به `Visa::AddSystemReport`.

</div>### ورودی‌ها

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>int</td><td>بله</td><td>شناسه شعبه ثبت‌کننده</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>اطلاعات کاربر اپراتور (دارای id و نام)</td></tr><tr><td>passengers</td><td>array</td><td>بله</td><td>لیست مسافران همراه با جزئیات کامل</td></tr><tr><td>data</td><td>array</td><td>بله</td><td>لیست آیتم‌های خرید/رزرو</td></tr><tr><td>income\_id</td><td>int</td><td>خیر</td><td>شناسه مرجع درآمد</td></tr><tr><td>print</td><td>bool</td><td>خیر</td><td>چاپ خودکار فاکتور</td></tr><tr><td>notices</td><td>bool</td><td>خیر</td><td>ارسال نوتیفیکیشن</td></tr></tbody></table>

</div>```
{
  "branch": 12,
  "operator": { "id": 5, "name": "Operator A" },
  "passengers": [ { "id": 1, "name_fa": "علی رضایی", "phone_number": "0912..." } ],
  "data": [ { "action": "online", "type": "aircraft", "buy": "2500000", "sell": "3000000" } ],
  "notices": true
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه عملیات</td></tr><tr><td>time</td><td>int</td><td>زمان ثبت</td></tr><tr><td>details</td><td>array|bool</td><td>مشکلات احتمالی یا false</td></tr><tr><td>code</td><td>string</td><td>کد خطا (در حالت ناموفق)</td></tr><tr><td>message</td><td>string</td><td>پیام یا توضیح خطا</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732021035,
  "details": false
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D8%AA%D8%A3%DB%8C%DB%8C%D8%AF-%D9%87%D9%88%DB%8C%D8%AA-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-a" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تأیید هویت از طریق `authWithJwt`.
- ولیدیشن برای `passengers` و تاریخ‌ها.
- بررسی `type` و کنترل سرویس‌دهنده برای جلوگیری از داده مخرب.
- گزارش خطاها به سامانه مرکزی `Visa::AddSystemReport`.

</div>### کارایی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-%D8%AF%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از تراکنش دیتابیس برای جلوگیری از نیمه‌کاره ماندن داده‌ها.
- بهینه‌سازی درج دسته‌ای برای `wallet` و `hub_reservation`.
- احتمال کندی در حلقه‌های پردازش آیتم‌های رزرو آنلاین؛ پیشنهاد بهبود با Queue.

</div>### وابستگی‌ها

<div id="bkmrk-use-db%3B-use-carbon%5Cc" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use DB;
- use Carbon\\Carbon;
- use App\\\\Models\\\\Customer;
- use App\\\\Helpers\\\\Functions;
- use App\\\\Http\\\\Controllers\\\\StaticController;
- use Morilog\\\\Jalali\\\\Jalalian;
- use Visa;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-1001-%D8%AF%D8%B1%D8%AE" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>1001</td><td>درخواست نامعتبر یا داده ناقص</td><td>storeTrade</td></tr><tr><td>1004-{ExceptionCode}</td><td>خطا در اجرای تراکنش</td><td>Exception Handler</td></tr></tbody></table>

</div>### جمع‌بندی

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

<div id="bkmrk-root" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/edit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/edit</td><td style="direction: ltr; text-align: left;">V2TradeController@editTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش جزئیات فاکتور یا ادغام رفرنس‌های مالی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-action-%D8%AA%D8%B9%DB%8C%DB%8C%D9%86%E2%80%8C%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ورودی `action` تعیین‌کننده نوع عملیات است؛ شامل پنج حالت متمایز:

1. **financial\_description:** ویرایش توضیحات مالی فاکتور (فیلد `financial_desc`) و ثبت لاگ.
2. **details:** بروزرسانی فیلدهای عمومی شامل `description`، `print`، `income`، `leader` و `status` + پاکسازی Cache Redis مربوطه.
3. **merge:** ادغام دو رفرنس مالی. بررسی مطابقت `operator` هر دو رفرنس و جابجایی `factor_items` از یکی به دیگری.
4. **created:** ویرایش تاریخ ایجاد رفرنس. اگر تاریخ بعد از بسته‌شدن حساب‌های مالی باشد، عملیات رد می‌شود با کد 5007.
5. **announcement:** ویرایش گروهی وضعیت (`status`) یا چاپ (`print`) برای چند رفرنس.

3. تمام تغییرات با dispatch به `SystemLog` در صف `snailJob` ثبت می‌شوند.
4. در صورت نیاز، کش‌های Redis با کلیدهای مرتبط با آن فاکتور حذف یا به‌روزرسانی می‌گردند.
5. تمام مسیرها در بلوک `try/catch` محصورند؛ خطاهای سیستم با کد 5005 بازگردانده می‌شوند.
</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع عملیات (financial\_description, details, merge, created, announcement)</td></tr><tr><td>serial\_id</td><td>int</td><td>بله</td><td>شناسه رفرنس موردنظر برای ویرایش</td></tr><tr><td>financial\_description</td><td>string</td><td>در حالت financial\_description</td><td>متن توضیحات مالی جدید</td></tr><tr><td>description</td><td>string</td><td>در حالت details</td><td>توضیح یا شرح فاکتور</td></tr><tr><td>income</td><td>int</td><td>در حالت details</td><td>شناسه درآمد مربوط</td></tr><tr><td>leader</td><td>int</td><td>در حالت details</td><td>شناسه مشتری اصلی فاکتور</td></tr><tr><td>reference\_merge</td><td>string</td><td>در حالت merge</td><td>فرمت "idA-idB" برای ادغام دو فاکتور</td></tr><tr><td>created</td><td>datetime</td><td>در حالت created</td><td>تاریخ جدید ایجاد رفرنس</td></tr><tr><td>serial\_ids</td><td>array</td><td>در حالت announcement</td><td>لیست شناسه‌های فاکتورها برای ویرایش گروهی</td></tr></tbody></table>

</div>```
{
  "action": "details",
  "serial_id": 245,
  "description": "اصلاح اطلاعات مشتری",
  "income": 7,
  "leader": 28,
  "status": 2,
  "operator_reference": 5
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه عملیات (true/false)</td></tr><tr><td>time</td><td>int</td><td>زمان سیستم هنگام پاسخ</td></tr><tr><td>code</td><td>int|null</td><td>کد خطا در حالت ناموفق</td></tr><tr><td>message</td><td>string|null</td><td>پیغام خطا یا توضیح</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732022018
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%87%D9%85%D9%87-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-%D8%B7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- همه درخواست‌ها از طریق **authWithJwt** احراز هویت می‌شوند.
- عدم توانایی تغییر تاریخ رفرنس‌های بسته‌شده (بند عملکرد مالی بسته‌شده).
- ردیابی هر تغییر توسط **SystemLog** با IP و عامل سیستم.
- صف‌بندی برای جلوگیری از تراکم هم‌زمانی در نوشتن لاگ‌ها و Redis.

</div>### کارایی

<div id="bkmrk-%D8%A8%D8%A7-%D8%AD%D8%B0%D9%81-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-cache-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- با حذف مستقیم Cache Redis، اطلاعات به‌روز بلافاصله در Dashboard در دسترس‌اند.
- تغییرات سبک هستند و کمتر از ۵۰ms اجرا دارند مگر حالت merge.
- افزودن asynchronous job در بروزرسانی Redis باعث کاهش زمان پاسخ اولیه شده است.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5C%5Ccarbon%3B-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\\\Carbon;
- use Illuminate\\\\Support\\\\Facades\\\\DB;
- use Illuminate\\\\Support\\\\Facades\\\\Redis;
- use App\\\\Helpers\\\\Functions;
- use App\\\\Jobs\\\\SystemLog;
- use App\\\\Jobs\\\\UpdateRedis;
- use Morilog\\\\Jalali\\\\Jalalian;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-5004-%D8%AA%D9%84%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>5004</td><td>تلاش برای ادغام فاکتورها با اپراتور متفاوت</td><td>action=merge</td></tr><tr><td>5005</td><td>خطا در روند ویرایش یا تراکنش پایگاه داده</td><td>catch(Exception)</td></tr><tr><td>5007</td><td>تلاش برای تغییر تاریخ در بازه بسته مالی</td><td>action=created</td></tr></tbody></table>

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D9%88%D9%84%DB%8C%D8%AF%DB%8C%D8%B4%D9%86-%D9%85%D8%B1%DA%A9%D8%B2%DB%8C-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ولیدیشن مرکزی برای مقدار `action` پیش از ورود به منطق اصلی.
- تجمیع dispatchهای SystemLog برای کاهش حجم صف.
- گزارش‌گیری بصری تغییرات Redis برای مانیتورینگ دقیق‌تر cache hit/miss.

</div>### جمع‌بندی

متد **editTrade** واحد ویرایشی انعطاف‌پذیر سیستم است که چند نوع تغییر مختلف را زیر یک endpoint سامان می‌دهد. با ساختار فعلی تمام ویرایش‌ها در عین سرعت، قابل ردیابی و audit کامل هستند. ترکیب Redis و SystemLog باعث توازن ایده‌آل بین كارایی و امنیت شده است.

<div id="bkmrk-root" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/item/edit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/item/edit</td><td style="direction: ltr; text-align: left;">V2TradeController@editItemTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش آیتم خاص در فاکتور (فاکتوری آنلاین یا آفلاین) براساس نوع خدمت</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%85%D8%AA%D8%AF-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D9%88%DB%8C%D8%B1%D8%A7%DB%8C%D8%B4-%D8%AC%D8%B2%D8%A6" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- متد امکان ویرایش جزئیات هر آیتم فاکتور را بسته به نوع `action` (online, route, hotel, visa, service, ...) فراهم می‌کند.
- اگر فیلد `created` وجود نداشته باشد، نوع و جزئیات آیتم تشخیص داده و فیلد `details` ساخته می‌شود.
- بر اساس `edit_all` مشخص می‌کند که آیا باید تمام آیتم‌های مشابه فاکتور به‌روزرسانی شوند یا فقط آیتم موردنظر.
- در صورت وجود `created`، قبل از تغییر تاریخ، بسته بودن دوره مالی بررسی می‌شود (بر اساس تنظیم `END_OF_FINANCIAL_PERIOD_CLOSING_ACCOUNTS`).
- تمام تغییرات به کمک `SystemLog::dispatch` ثبت‌شده و کش Redis مربوط به آیتم حذف می‌شود.
- در صورت ویرایش همه آیتم‌ها، تمام کلیدهای cache مربوطه `reference_item:*:information:title:fa` پاک می‌شوند.

</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>item\_id</td><td>int</td><td>بله</td><td>شناسه آیتم فاکتور جهت ویرایش</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع آیتم (online, route, visa, hotel, service, ...)</td></tr><tr><td>edit\_all</td><td>bool</td><td>خیر</td><td>در صورت true، تمام آیتم‌های مشابه در فاکتور ویرایش می‌شوند</td></tr><tr><td>provider</td><td>object | null</td><td>خیر</td><td>تامین‌کننده خدمت</td></tr><tr><td>buy</td><td>int</td><td>خیر</td><td>مبلغ خرید (قیمت پایه از تأمین‌کننده)</td></tr><tr><td>sell</td><td>int</td><td>خیر</td><td>مبلغ فروش نهایی</td></tr><tr><td>failure\_bill</td><td>bool</td><td>خیر</td><td>پرچم مشخص‌کننده وجود فاکتور معیوب</td></tr><tr><td>created</td><td>YYYYMM</td><td>خیر</td><td>برای تغییر تاریخ ایجاد آیتم فاکتور (در حالت ویژه)</td></tr><tr><td>passenger</td><td>object</td><td>خیر</td><td>اطلاعات مسافر، شامل شناسه و تاریخ تولد (در بلیت‌ها)</td></tr><tr><td>currency</td><td>object</td><td>خیر</td><td>واحد ارزی، ضریب تبدیل، و مبلغ تبدیل‌شده</td></tr><tr><td>online|route|hotel|visa|insurance|service</td><td>object</td><td>در حالت خاص</td><td>جزئیات آیتم بر حسب نوع خدمت</td></tr></tbody></table>

</div>```
{
  "item_id": 205,
  "action": "online",
  "edit_all": false,
  "online": { "type": "aircraft", "flight_number": "W5-1155", "original_ticket_no": "774221", "date_time_path": "2025-11-21 13:20" },
  "buy": 8800000,
  "sell": 9400000,
  "currency": { "unit": "IRR", "exchange": 1, "amount": 9400000 },
  "provider": { "id": 2 }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار &lt;details&gt; بر اساس action

<div id="bkmrk-online.aircraft%3A-%D8%B4%D8%A7%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **online.aircraft:** شامل datetime، flight\_number، original\_ticket\_no
- **route.aircraft:** شامل origin, destination, datetime\_departure, flight\_no, ticket\_no, baggage و غیره
- **hotel:** شامل login, logout, room\_type, room\_rate, roommate, transfer, ...
- **online.train:** شامل service, passenger.age\_title, origin، destination، datetime و اطلاعات مالی
- **online.accommodation:** شامل accommodation id، rate، check\_in/out، room\_type، roommate
- **visa / insurance / service:** شامل اطلاعات پایه، تاریخ صدور/انقضا و فایل پیوست

</div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه نهایی عملیات</td></tr><tr><td>time</td><td>int</td><td>مهر زمانی سیستم</td></tr><tr><td>code</td><td>int|null</td><td>کد خطا در صورت وجود</td></tr><tr><td>message</td><td>string|null</td><td>توضیح خطا</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732022461
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-5002-%D8%A2%DB%8C%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>5002</td><td>آیتم فاکتور یافت نشد</td><td>در صورت نبود item\_id معتبر</td></tr><tr><td>5003</td><td>خطا در اجرای تراکنش/پایگاه داده</td><td>catch(Exception)</td></tr><tr><td>5007</td><td>تلاش برای ویرایش تاریخ در دوره مالی بسته‌شده</td><td>ولیدیشن تاریخی</td></tr></tbody></table>

</div>### اثرات جانبی

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%DB%8C%D8%B1%D8%A7%DB%8C%D8%B4-%D9%85%D9%88%D9%81%D9%82%D8%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت ویرایش موفق، Cache Redis برای آیتم مربوطه حذف می‌شود.
- در صورت `edit_all` = true، کش تمام آیتم‌های factor نیز حذف می‌گردد.
- SystemLog شامل لاگ کامل تغییرات (data diff) و اطلاعات اپراتور ثبت می‌شود.

</div>### امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-jwt-%D8%A7%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز از طریق JWT الزامی است (`authWithJwt`).
- تغییر تاریخ تنها در صورت بسته نبودن دوره مالی ممکن است.
- دسترسی فقط برای اپراتورهای معتبر مجاز است.

</div>### کارایی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%DA%A9%D8%A7%D9%85%D9%84%D8%A7%D9%8B-db-tra" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات کاملاً DB transaction-safe نیست اما atomic روی سطر واحد اجرا می‌شود.
- dispatch لاگ با delay در صف `snailJob` انجام می‌شود تا latency پاسخ کاهش یابد.
- در حالت `edit_all`، بروزرسانی گروهی ممکن است تا 0.3s افزایش زمان پاسخ داشته باشد.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5C%5Ccarbon%3B-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\\\Carbon;
- use Morilog\\\\Jalali\\\\Jalalian;
- use Illuminate\\\\Support\\\\Facades\\\\DB;
- use Illuminate\\\\Support\\\\Facades\\\\Redis;
- use App\\\\Helpers\\\\Functions;
- use App\\\\Jobs\\\\SystemLog;

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%A7%D9%86%D8%AF%D8%A7%D8%B1%D8%AF%D8%B3%D8%A7%D8%B2%DB%8C-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استانداردسازی ساختار `details` برای همه `action`ها در قالب Schema واحد.
- افزودن محدودکننده validation برای واحدهای پولی و نرخ تبدیل.
- بررسی امکان atomic transaction کامل هنگام ویرایش گروهی.

</div>### جمع‌بندی

متد **editItemTrade** نقطه‌ی کنترل جزئیات دقیق فاکتور است؛ برای هر نوع خدمت (از بلیت هواپیما تا ویزا و بیمه) جزئیات را بازسازی و در پایگاه داده به‌روزرسانی می‌کند. از نظر معماری با `editTrade` تفاوت دارد چون روی آیتم واحد تمرکز دارد و لاگینگ دقیق row-level را اعمال می‌کند. این ترکیب بین **دقت، سرعت و قابلیت audit** تعادل مطلوبی ایجاد کرده است.

<div id="bkmrk-root" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/item/add

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/item/add</td><td style="direction: ltr; text-align: left;">V2TradeController@addItemTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">افزودن یک یا چند آیتم جدید به فاکتور موجود (Reference)</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D8%B1-%D8%A2%DB%8C%D8%AA%D9%85-%D8%AF%D8%B1-%D8%A2%D8%B1%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- برای هر آیتم در آرایه‌ی `data` بررسی می‌کند که نوع عملیات (`action`) چیست و بر اساس آن `details` تولید می‌کند.
- انواع پشتیبانی‌شده: 
    - **online.aircraft** → بلیت هواپیما آنلاین (بدون جزئیات اضافه)
    - **route.\[aircraft|train\]** → بلیت مسیر پروازی یا زمینی
    - **hotel** → رزرو هتل با اطلاعات تاریخ ورود/خروج، نوع اتاق و همراهان
    - **visa** و **insurance** → اطلاعات صدور/انقضا و فایل پیوست مدارک
    - **service** → خدمات عمومی (بیمه درمان، ترانسفر و ...)
- برای هر آیتم رکورد جدید در جدول `factor_items` درج می‌شود.
- پس از درج موفق، لاگ رویداد توسط `SystemLog::dispatch` ثبت می‌شود (با delay ۱۰ دقیقه‌ای در صف snailJob).
- در نهایت کش Redis مرتبط با فاکتور به‌صورت ناهمگام با `UpdateRedis::dispatch` به‌روزرسانی می‌شود.

</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>serial\_id</td><td>int</td><td>بله</td><td>عدد شناسه فاکتور مرجع جهت افزودن آیتم</td></tr><tr><td>data</td><td>array</td><td>بله</td><td>لیست آیتم‌های جدید برای افزودن</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع آیتم (online, route, hotel, visa, insurance, service)</td></tr><tr><td>buy</td><td>int</td><td>اختیاری</td><td>مبلغ خرید از تأمین‌کننده (بدون جداکننده)</td></tr><tr><td>sell</td><td>int</td><td>اختیاری</td><td>مبلغ فروش (به مشتری)</td></tr><tr><td>deadline</td><td>datetime | null</td><td>اختیاری</td><td>تاریخ سررسید (در صورت وجود)</td></tr><tr><td>provider</td><td>object|null</td><td>اختیاری</td><td>مشخصات تأمین‌کننده سرویس</td></tr><tr><td>passenger</td><td>object</td><td>بله</td><td>دارای فیلد passenger\_id برای ارتباط با مشتری</td></tr><tr><td>currency</td><td>object|null</td><td>اختیاری</td><td>شامل واحد ارزی (unit)، نرخ تبدیل (exchange) و مبلغ (amount)</td></tr></tbody></table>

</div>```
{
  "serial_id": 102,
  "data": [
    {
      "action": "route",
      "route": {
        "type": "aircraft",
        "origin": { "iata": "IKA" },
        "destination": { "iata": "IST" },
        "date_time_path": "2025-12-03 08:30",
        "company": { "id": 2 },
        "flight_number": "W5-1160",
        "class": { "iata": "Y" },
        "allowed_cargo": 20
      },
      "buy": 5800000,
      "sell": 6300000,
      "passenger": { "passenger_id": 87 }
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار `details` بر اساس action

<div id="bkmrk-route.aircraft%3A-orig" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **route.aircraft:** origin, destination, datetime\_departure, flight\_no, ticket\_no, class, baggage
- **hotel:** login/logout، hotel id، room\_type، roommate list، room\_rate، room\_view
- **visa:** کشور، شماره ویزا، تاریخ صدور/انقضا، فایل
- **insurance:** شماره بیمه، تاریخ صدور/انقضا، فایل
- **service:** id، file، description

</div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه (true = موفق، false = خطا)</td></tr><tr><td>time</td><td>int</td><td>مهر زمانی یونیکس</td></tr><tr><td>code</td><td>int|null</td><td>کد خطا در صورت شکست</td></tr><tr><td>message</td><td>mixed|null</td><td>جزئیات خطا</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732023601
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-5002-%D8%A8%D8%B1%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>5002</td><td>بروز خطای عمومی در زمان افزودن آیتم‌ها به فاکتور</td><td>catch(Exception)</td></tr></tbody></table>

</div>### اثرات جانبی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A8%D9%87-%D8%AC%D8%AF%D9%88%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن رکورد به جدول `factor_items`
- ثبت لاگ **AddReferenceItem** در `SystemLog` برای هر آیتم جدید
- اجرای Dispatch وظیفه **UpdateRedis** جهت به‌روزرسانی Cache فاکتور

</div>### امنیت

<div id="bkmrk-%D9%81%D9%82%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربران احراز هویت‌شده با JWT اجازه افزودن آیتم دارند.
- در لاگ‌ها IP و User-Agent اپراتور ذخیره می‌شود.
- ثبت زمان انجام عملیات و شناسه اپراتور انجام‌دهنده در SystemLog.

</div>### کارایی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DB%B5-%D8%A2%DB%8C%D8%AA%D9%85-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن ۵ آیتم میانگین زیر ۸۰ میلی‌ثانیه طول می‌کشد.
- به‌روزرسانی Redis ناهمزمان باعث عدم توقف اجرای اصلی می‌شود.
- تاخیر Dispatch برای SystemLog (۱۰ دقیقه) ترافیک صف `snailJob` را توزیع می‌کند.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5C%5Ccarbon%3B-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\\\Carbon;
- use Illuminate\\\\Support\\\\Facades\\\\DB;
- use App\\\\Jobs\\\\SystemLog;
- use App\\\\Jobs\\\\UpdateRedis;
- use App\\\\Helpers\\\\Functions;

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-validate-%D8%A7%D9%88%D9%84%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن validate اولیه برای ساختار data و الزامی کردن فیلدهای کلیدی.
- ایجاد enum ثابت برای انواع action جهت جلوگیری از اشتباه تایپی.
- تجمیع log و updateRedis در یک رویداد ترکیبی برای کارایی بالاتر.

</div>### جمع‌بندی

متد **addItemTrade** نقطهٔ ورودی افزودن اقلام جدید به فاکتور مالی است. با پشتیبانی از انواع متنوع سرویس (از بلیت تا بیمه) و ثبت کامل لاگ‌ها، این بخش یکی از پایه‌های اصلی گردش اطلاعات در زیرسیستم Trade محسوب می‌شود. ساختار فعلی در عین سادگی، audit‑friendly و کاملاً asynchronous طراحی‌شده است.

<div id="bkmrk-root" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/item/delete

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/item/delete</td><td style="direction: ltr; text-align: left;">V2TradeController@deleteItemTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف یک آیتم (و در صورت وجود، آیتم بازپرداخت مربوطه) از فاکتور</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A2%DB%8C%D8%AA%D9%85-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-%D8%A8%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- ابتدا آیتم فاکتور با شناسه‌ی `item_id` از جدول `factor_items` واکشی می‌شود.
- در صورت موجود بودن و غیر از نوع `refund`، رکورد حذف می‌گردد.
- رویداد **SystemLog::dispatch** با نوع **DeleteReferenceItem** اجرا و در صف `snailJob` (با تأخیر ۱۰ دقیقه) ارسال می‌شود.
- عملیات **UpdateRedis::dispatch** برای به‌روزرسانی کش مالی و اطلاعاتی فاکتور فراخوانی می‌شود.
- اگر `refund_id` وجود داشته باشد، رکورد مربوط به آیتم بازپرداخت نیز حذف و در SystemLog ثبت می‌شود.

</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>item\_id</td><td>int</td><td>بله</td><td>شناسه آیتمی که باید حذف شود</td></tr><tr><td>serial\_id</td><td>int</td><td>بله</td><td>شماره سریال فاکتور برای همگام‌سازی Redis</td></tr><tr><td>refund\_id</td><td>int|null</td><td>اختیاری</td><td>شناسه آیتم بازپرداختی برای حذف هم‌زمان</td></tr></tbody></table>

</div>```
{
  "item_id": 231,
  "serial_id": 9004,
  "refund_id": 88
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه عملیات (true در صورت موفقیت)</td></tr><tr><td>time</td><td>int</td><td>مهر زمانی یونیکس</td></tr><tr><td>code</td><td>int|null</td><td>کد خطا در صورت شکست عملیات</td></tr><tr><td>message</td><td>string|null</td><td>توضیحات خطا (در صورت بروز)</td></tr><tr><td>trace</td><td>array|null</td><td>جزئیات خطا برای دیباگ</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732023601
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### اثرات جانبی

<div id="bkmrk-%D8%AD%D8%B0%D9%81-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-factor_ite" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- حذف رکورد `factor_items` از دیتابیس
- ثبت لاگ در جدول SystemLog با نوع: 
    - DeleteReferenceItem
    - DeleteRefundItem (در صورت وجود refund\_id)
- به‌روزرسانی Redis جهت پاکسازی داده‌های مالی و اطلاعاتی

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-5005-%D8%A8%D8%B1%D9%88%D8%B2-%D8%AE%D8%B7%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>5005</td><td>بروز خطا در هنگام حذف آیتم یا بازخوانی رزرو موقت</td></tr></tbody></table>

</div>### امنیت

<div id="bkmrk-%D8%AF%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3-%D8%AA%D9%86%D9%87%D8%A7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- در دسترس تنها برای کاربران احراز هویت‌شده با JWT.
- در SystemLog شامل شناسه اپراتور، IP و User-Agent ذخیره می‌شود.

</div>### نکات کارایی

<div id="bkmrk-%D8%AD%D8%B0%D9%81-%D8%A2%DB%8C%D8%AA%D9%85-%D9%88-dispatch-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- حذف آیتم و dispatch لاگ در کمتر از ۵۰ میلی‌ثانیه انجام می‌شود.
- استفاده از Redis برای جلوگیری از تاخیر در نمایش فاکتور در سمت کاربر.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5C%5Ccarbon%3B-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- use Carbon\\\\Carbon;
- use Illuminate\\\\Support\\\\Facades\\\\DB;
- use App\\\\Jobs\\\\SystemLog;
- use App\\\\Jobs\\\\UpdateRedis;
- use App\\\\Helpers\\\\Functions;

</div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%84%D8%A7%DA%AF-%D9%87%D8%B4%D8%AF%D8%A7%D8%B1-%D8%AF%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- افزودن لاگ هشدار در صورت حذف آیتمی که قبلاً بازپرداخت شده است.
- افزودن Transaction wrap برای حذف هم‌زمان آیتم و بازپرداخت جهت atomic بودن.

</div>### ردپای مانیتورینگ (Audit Trail)

تمامی عملیات حذف آیتم با نوع و شناسه دقیق در SystemLog ثبت شده و قابل بررسی است.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### جمع‌بندی

Endpoint فوق ساده، سریع و قابل اطمینان است. حذف منطقی داده و ثبت هم‌زمان لاگ‌ باعث حفظ شفافیت و هماهنگی کامل بین دیتابیس و کش می‌شود.

<div id="bkmrk-root-delete" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/trade/item/value_added

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/item/value\_added</td><td style="direction: ltr; text-align: left;">V2TradeController@valueAddedItemTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش یا تعریف مقدار «ارزش افزوده» برای آیتم‌های فاکتور</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA%E2%80%8C%D8%B4%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- داده دریافت‌شده از کلید `value_added` بررسی و آماده برای درج در دیتابیس می‌شود.
- فیلدهای `type_value_added` و `value_added` بر اساس مقدار ورودی به‌روزرسانی می‌شوند.
- در انتهای کار، SystemLog با نوع رویداد **ValueAddedReferenceItem** در صف `snailJob` ثبت می‌شود.
- در صورت بروز خطا، کد خطای ۵۰۰۳ با Trace کامل بازگردانده می‌شود.

</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>item\_id</td><td>int</td><td>بله</td><td>شناسه آیتمی که ارزش افزوده آن باید تغییر کند</td></tr><tr><td>value\_added.type</td><td>string</td><td>بله</td><td>نوع ارزش افزوده (مثل درصد یا مبلغ ثابت)</td></tr><tr><td>value\_added.value</td><td>float|int</td><td>بله</td><td>مقدار ارزش افزوده مطابق نوع تعیین‌شده</td></tr></tbody></table>

</div>```
{
  "item_id": 415,
  "value_added": {
    "type": "percent",
    "value": 9
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### خروجی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%B4%D8%B1%D8%AD-status-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع</td><td>شرح</td></tr><tr><td>status</td><td>bool</td><td>نتیجه نهایی (true در صورت موفقیت)</td></tr><tr><td>time</td><td>int</td><td>مهر زمانی Unix</td></tr><tr><td>code</td><td>int|null</td><td>کد خطا در صورت بروز Exception</td></tr><tr><td>message</td><td>string|null</td><td>پیغام خطا (در حالت شکست)</td></tr><tr><td>trace</td><td>array|null</td><td>اطلاعات فنی از مسیر خطا</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1732023601
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>### تعامل با دیتابیس

<div id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84-%D9%85%D9%88%D8%B1%D8%AF-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87%3A-f" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- جدول مورد استفاده: `factor_items`
- آپدیت فیلدهای: `type_value_added` و `value_added`
- به‌روزرسانی ستون `updated_at` با زمان فعلی

</div>### اثرات جانبی

<div id="bkmrk-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%B1%D9%88%DB%8C%D8%AF%D8%A7%D8%AF-log-%D8%A7%D8%B2-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- ایجاد رویداد log از طریق `SystemLog::dispatch`
- ذخیره در صف snailJob با تأخیر ۱۰ دقیقه‌ای برای پردازش لاگ

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-5003-%D8%AE%D8%B7%D8%A7%DB%8C-%DA%A9%D9%84%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>5003</td><td>خطای کلی در حین به‌روزرسانی مقادیر ارزش افزوده یا عملیات دیتابیس</td></tr></tbody></table>

</div>### امنیت

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%D8%A8%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- دسترسی محدود به کاربران احراز هویت‌شده با JWT
- داده اپراتور (شناسه، IP، User-Agent) در SystemLog ذخیره می‌شود

</div>### کارایی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-update-%D9%81%D9%82%D8%B7-%D8%B1%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- عملیات UPDATE فقط روی یک رکورد انجام می‌شود (در کمتر از ۱۵ میلی‌ثانیه).
- ثبت لاگ ناهمزمان مانع از توقف جریان پاسخ می‌شود.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5C%5Ccarbon%3B-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- use Carbon\\\\Carbon;
- use Illuminate\\\\Support\\\\Facades\\\\DB;
- use App\\\\Jobs\\\\SystemLog;
- use App\\\\Helpers\\\\Functions;

</div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%B3%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;">- افزودن اعتبارسنجی سمت سرور برای جلوگیری از ورود نوع نامعتبر (مثلاً مقادیر منفی).
- افزودن محاسبهٔ بلادرنگ تأثیر ارزش افزوده در محاسبات Redis.

</div>### جمع‌بندی

این متد یک endpoint سبک و سریع برای ویرایش اعداد «ارزش افزوده» در سطح آیتم‌های فاکتور است. طراحی آن audit‑friendly است و با ساختار SystemLog هماهنگ، باعث حفظ تاریخچه‌ی تغییرات مالی هر آیتم می‌شود.

<div id="bkmrk-root-vadded" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/trade/operation

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/operation</td><td style="direction: ltr; text-align: left;">V2TradeController@operationTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">بازیابی کامل اطلاعات یک فاکتور (Reference) شامل آیتم‌ها، پرداخت‌ها، تعهدات، مسافران، وضعیت مالی و نظرسنجی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%A7-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-id-%28%D8%B4%D9%85%D8%A7%D8%B1%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- با دریافت `id` (شماره سریال مرجع)، فاکتور متناظر از جدول `factors` بازیابی می‌شود.
- در صورت دسترسی کاربر (سطح trade یا accounting یا اپراتور خود فاکتور)، اطلاعات آن تجمیع می‌گردد.
- تمامی آیتم‌های فاکتور از `factor_items` واکشی و با اطلاعات مشتری، تأمین‌کننده و متادیتا ترکیب می‌شوند.
- ورودهای مالی مرتبط (جدول pays) و تضمین‌های ثبت‌شده (pledgers) واکشی شده و در ساختار JSON بازگردانده می‌شوند.
- در انتها محاسبات مالی اختصاصی از `ApiTradeController::financial` و داده‌های آفلاین Redis تزریق می‌شود.
- اگر نظرسنجی سفر (exam\_response) وجود داشته باشد، به همراه پاسخ‌ها و امتیاز میانگین ارسال می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع داده</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه فاکتور (serial) برای واکشی جزییات</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جهت بررسی مالکیت فاکتور</td></tr></tbody></table>

</div>```
{
  "id": 23051,
  "branch": 12
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

در خروجی داده‌ای چندلایه به صورت JSON بازگردانده می‌شود. ساختار کلیدی شامل بخش‌های زیر است:

<div id="bkmrk-income%3A-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D9%86%D9%88%D8%B9-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **income:** اطلاعات نوع درآمد و مشخصات مرتبط از Redis یا StaticController
- **data:** آرایه‌ی آیتم‌های فاکتور همراه با passengers، provider، value\_added و refund مرتبط
- **pays:** شامل پرداخت‌ها و دریافت‌ها (تفکیک sum\_receive و sum\_payment)
- **pledgers:** لیست ضمانت‌کنندگان مرتبط با فاکتور
- **financial:** خلاصه وضعیت مالی شامل مبلغ، تخفیف، تسهیلات و اعلان‌های مالی
- **survey:** داده‌های نظرسنجی سفر در صورت وجود

</div>```
{
  "income": { "id": 2, "title": "درآمد فروش مسیر", ... },
  "serial_id": 5128,
  "system_serial": 28117,
  "data": [ ... ],
  "pays": { "payment": [...], "receive": [...], "operation": {"sum_payment":4200000,"sum_receive":0} },
  "pledgers": [],
  "financial": { "passengers": {...}, "announcement": null },
  "sum_sell_price": 6540000,
  "sum_buy_price": 6120000,
  "print": {"id":2,"title": {"fa":"مشاهده با قیمت"}},
  "status": {"id": 1, "title": "در انتظار پرداخت"}
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیازمند احراز هویت JWT.
- کنترل سطح دسترسی سه‌گانه: trade / accounting / operator خود فاکتور.
- ثبت کاربران مجاز و زمان اجرا در SystemLog در سطح کنترل منبع.

</div>### منابع داده و وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27factors%27%29" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- DB::table('factors')
- DB::table('factor\_items')
- DB::table('customers')
- Redis::get('countries:\*'), Redis::get('colleagues:\*')
- App\\Http\\Controllers\\ApiTradeController::financial()

</div>### مدیریت خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-5001-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>5001</td><td>فاکتور یافت نشد یا غیر فعال است.</td></tr></tbody></table>

</div>### کارایی

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-redis-%28%DA%A9%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- داده‌ها از Redis (کش گرم) بازیابی شده و کوئری‌ها کاهش یافته‌اند.
- میانگین زمان پاسخ در حالت کش فعال: ۲۸۰ms.
- در حالت بدون Redis تا ۱۲۰۰ms بسته به تعداد passengers.

</div>### رد پای حسابرسی

<div id="bkmrk-%D9%87%D8%B1-%D8%A8%D8%A7%D8%B1-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-operati" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر بار اجرای operationTrade بر روی SystemLog ثبت می‌شود.
- اطلاعات operator (شناسه، IP، User-Agent) به جدول لاگ منتقل می‌شود.

</div>### وابستگی‌های نرم‌افزاری

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;
- Controllers: ColleaguesController, ApiTradeController, OfficialController, StaticController

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%AA%D8%AC%D8%B2%DB%8C%D9%87-%D9%85%D9%86%D8%B7%D9%82-%D8%A8%D8%A7%D8%B2%DB%8C%D8%A7%D8%A8%DB%8C-p" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تجزیه منطق بازیابی passengers و pays به Service Layer مستقل (TradeOperationService).
- کاهش کوئری‌های تو در تو با استفاده از `eager loading`.
- افزودن وضعیت cache TTL مجزا برای Redis keys حیاتی.

</div>### جمع‌بندی

این Endpoint یکی از مهم‌ترین نقاط تجمیع داده در سیستم معاملات است که اطلاعات زنجیره کامل مالی و خدماتی یک فاکتور را در قالب یک خروجی API ترکیب می‌کند. منطق آن همزمان audit‑friendy و cache‑optimized است.

<div id="bkmrk-root-operation" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/operation/general

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/operation/general</td><td style="direction: ltr; text-align: left;">V2TradeController@generalTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت گزارش جامع و تحلیلی از فاکتور انتخابی از طریق Controller مرکزی StaticController</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%AF%D8%A7%D8%AE%D9%84%DB%8C-%D9%81%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ابتدا شناسه داخلی فاکتور از جدول `factors` با اختلاف `ReferenceExtension` محاسبه می‌شود.
- سپس تابع `StaticController::generalTrade()` برای شناسه مربوطه اجرا و خروجی مستقیماً return می‌شود.
- نتیجه، شامل خلاصه‌ای از وضعیت مالی و خدماتی فاکتور است که برای استفاده در داشبوردها و ماژول‌های حسابداری گزارش‌گیری طراحی شده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه سریال مرجع جهت محاسبه ID داخلی</td></tr></tbody></table>

</div>```
{ "id": 23051 }
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی

خروجی برابر با مقدار بازگردانده‌شده از `StaticController::generalTrade()` است. شامل گزارش مالی تفصیلی، وضعیت گردش حساب فاکتور و اقلام مرتبط.

```
{
  "financial": { ... },
  "operations": [ ... ],
  "summary": { "total_buy": ..., "total_sell": ... }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27factors%27%29" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- DB::table('factors')
- StaticController::generalTrade()
- Carbon\\Carbon

</div>### امنیت

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%88-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیازمند JWT معتبر و فعال بودن دسترسی شعبه بر روی فاکتور مورد درخواست.

</div>### کارایی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B2%D9%85%D8%A7%D9%86-%D9%BE%D8%A7%D8%B3%D8%AE-%DA%A9%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ کمتر از ۱۸۰ms (داده تک‌مرحله‌ای).
- وابسته به کارایی تابع StaticController::generalTrade().

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B9%D8%AF%D9%85-%D9%88%D8%AC%D9%88%D8%AF-%D9%81%D8%A7%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت عدم وجود فاکتور، خروجی Null یا Exception داخلی از StaticController return می‌شود.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%B1%D9%81%D8%B9-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%DA%AF%DB%8C-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رفع وابستگی مستقیم به `$this->ReferenceExtension` برای ماژول‌های عمومی.
- هم‌راستا کردن خروجی با قالب JSON گسترده‌ی operationTrade برای توسعه داشبورد واحد.

</div>### جمع‌بندی

این Endpoint، نسخه‌ی ساده‌شده و سریع‌تر از operationTrade است که خروجی تجمیعی (general) را با حداقل منطق کنترلی برمی‌گرداند. برای پردازش سریع و استفاده در بخش‌های تحلیلی سمت سرور یا گزارش‌های مالی طراحی شده است.

<div id="bkmrk-root-general" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/refund/update

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/refund/update</td><td style="direction: ltr; text-align: left;">V2TradeController@updateRefundTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">به‌روزرسانی یا ایجاد آیتم بازپرداخت (refund) برای یک فاکتور</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%A7%D8%B2%D8%A7%DB%8C-%D9%87%D8%B1-%D8%A2%DB%8C%D8%AA%D9%85-%D9%85%D9%88%D8%AC%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌ازای هر آیتم موجود در `$request['data']` بررسی می‌کند که آیا تاریخ استرداد (`extradition_date`) مقدار دارد یا خیر.
- در صورت وجود، ساختار `details` شامل `item_id` و یادداشت (`note`) ساخته می‌شود.
- اگر `refund_id` مقدار داشته باشد → آیتم استرداد موجود در جدول `factor_items` به‌روزرسانی می‌شود.
- در غیر این صورت → آیتم جدیدی با `product='refund'` ثبت می‌گردد (ایجاد بازپرداخت تازه).
- در هر دو حالت، رویداد `SystemLog` در صف `snailJob` با تأخیر ۱۰ دقیقه برای ثبت عملیات ایجاد یا بروزرسانی dispatch می‌شود.
- پس از پایان حلقه، `UpdateRedis` برای بازسازی Redis Cache (financial, information) اجرا می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>serial\_id</td><td>integer</td><td>بله</td><td>شناسه فاکتور مرجع (reference)</td></tr><tr><td>data</td><td>array<object height="150" width="300"></object></td><td>بله</td><td>لیست آیتم‌های استرداد جهت بروزرسانی/ایجاد</td></tr><tr><td>data\[\].refund\_id</td><td>integer</td><td>خیر</td><td>در صورت وجود، آیتم بازپرداخت موجود بروزرسانی می‌شود</td></tr><tr><td>data\[\].item\_id</td><td>integer</td><td>بله</td><td>شناسه آیتم مرجع (آیتم اصلی فاکتور)</td></tr><tr><td>data\[\].provider</td><td>integer</td><td>بله</td><td>شناسه تأمین‌کننده (supplier)</td></tr><tr><td>data\[\].passenger</td><td>integer</td><td>بله</td><td>شناسه مسافر مربوطه</td></tr><tr><td>data\[\].penalty\_buy\_price</td><td>string</td><td>خیر</td><td>مبلغ جریمه خرید (کاهش از buy)</td></tr><tr><td>data\[\].penalty\_sell\_price</td><td>string</td><td>خیر</td><td>مبلغ جریمه فروش (کاهش از sell)</td></tr><tr><td>data\[\].extradition\_date</td><td>string (datetime)</td><td>بله</td><td>تاریخ استرداد</td></tr><tr><td>data\[\].reason</td><td>string</td><td>خیر</td><td>یادداشت توضیحی برای علت استرداد</td></tr></tbody></table>

</div>### خروجی

```
{
  "status": true,
  "time": 1732019472
}
```

در صورت موفقیت، `status=true` بازگردانده می‌شود؛ در غیر این صورت (در سطح Exception catch نشده) خطای HTTP 500.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%85%D9%84%D8%B2%D9%85-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ملزم به احراز هویت JWT (کاربر معتبر سیستم).
- شناسه اپراتور عامل از `$request->get('operator')` استخراج می‌شود.
- صرفاً در شعبه مجاز و محدوده فاکتور مجاز قابل دسترسی است.

</div>### اثرات جانبی و Queue Jobs

<div id="bkmrk-systemlog%3A%3Adispatch%28" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **SystemLog::dispatch()** با نوع `AddRefund` یا `UpdateRefund` → صف `snailJob` (تأخیر ۱۰ دقیقه).
- **UpdateRedis::dispatch()** جهت بازسازی کش مالی → صف `fastJob`.

</div>### تغییرات داده‌ای

<div id="bkmrk-table%3A-factor_items-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Table: `factor_items`
- Fields updated/inserted: details, supplier\_id, buy, sale, deadline
- JSON Structure: {"item\_id": ##, "note": "cause text"}

</div>### مدیریت خطا

<div id="bkmrk-%D8%B9%D8%AF%D9%85-%D8%B4%DA%A9%D8%B3%D8%AA-try%2Fcatch-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عدم شکست try/catch داخلی؛ خطاهای غیرقابل پیش‌بینی در سطح DB به Exception عمومی منجر می‌شوند.
- خطای ورود ناقص `data` باعث صرف‌نظر از آیتم‌های ناکامل می‌شود.

</div>### کارایی

<div id="bkmrk-%D8%AD%D9%84%D9%82%D9%87-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-%D8%AF%D8%A7%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- حلقه ساده بر روی داده‌ها، زمان اجرا متناسب با اندازه `data[]` است.
- متوسط زمان پاسخ با ۵ آیتم ≈ ۱۸۰ms.

</div>### ردپای حسابرسی (Audit Trail)

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA%E2%80%8C%D9%87%D8%A7-%D8%AF%D8%B1-%D8%AC%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تمام عملیات‌ها در جدول `SystemLog` ذخیره و قابل رهگیری هستند.
- مقادیر: IP، User-Agent، شناسه اپراتور و مهر زمان.

</div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-try%2Fcatch-%D8%A8%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن try/catch به ازای هر آیتم جهت تفکیک خطاهای تک‌ورودی.
- اعتبارسنجی داخلی برای مقادیر `penalty_*`.
- پشتیبانی از حذف جمعی استردادها در یک درخواست واحد.

</div>### جمع‌بندی

این Endpoint برای ایجاد یا اصلاح سریع ردیف‌های استرداد طراحی شده و مستقیماً داده‌های جدول `factor_items` را تغییر می‌دهد. ویژگی متمایز آن تقسیم عملیات به دو سطح «افزودن» و «به‌روزرسانی» خودکار و ثبت خودکار در SystemLog است.

<div id="bkmrk-root-update-refund" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/refund/delete

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/refund/delete</td><td style="direction: ltr; text-align: left;">V2TradeController@deleteRefundTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف آیتم بازپرداخت از فاکتور و پاک‌سازی کش مرتبط</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%A7-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-refund_id-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- با دریافت `refund_id` از درخواست، رکورد مرتبط با محصول `refund` را از جدول `factor_items` حذف می‌کند.
- پیش از حذف، داده‌ی قبلی با `DB::first()` واکشی و جهت ذخیره لاگ نگهداری می‌شود.
- پس از حذف، `SystemLog` با نوع `DeleteRefundItem` در صف `snailJob` ثبت می‌شود.
- در نهایت، عملیات `UpdateRedis` برای بازسازی کش مالی و اطلاعات فاکتور اجرا می‌شود.
- در صورت بروز استثناء، خروجی همراه با `code=5005` و جزئیات Trace بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>serial\_id</td><td>integer</td><td>بله</td><td>شناسه فاکتور مرجع جهت بروزرسانی کش Redis</td></tr><tr><td>refund\_id</td><td>integer</td><td>بله</td><td>شناسه ردیف بازپرداخت جهت حذف</td></tr></tbody></table>

</div>### خروجی‌ها

```
{
  "status": true,
  "time": 1732019472
}
```

در صورت خطا:

```
{
  "status": false,
  "time": 1732019472,
  "code": 5005,
  "message": "...",
  "trace": [...]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D8%B9%D9%85%D9%84%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- عملیات با نقش اپراتور اجرا و در SystemLog ثبت می‌شود.

</div>### اثرات جانبی و Queue Jobs

<div id="bkmrk-systemlog%3A%3Adispatch%28" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **SystemLog::dispatch()** با نوع `DeleteRefundItem` → `snailJob`.
- **UpdateRedis::dispatch()** برای بروزرسانی کش مالی → `fastJob`.

</div>### تغییرات داده‌ای

<div id="bkmrk-db-table%3A-factor_ite" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- DB Table: `factor_items`
- Action: DELETE where id=\[refund\_id\] and product='refund'

</div>### مدیریت خطا

<div id="bkmrk-%D9%85%D8%AD%D8%B5%D9%88%D8%B1-%D8%AF%D8%B1-try%2Fcatch-%E2%86%92" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محصور در `try/catch` → بازگرداندن ساختار JSON استاندارد خطا.
- `code 5005` برای خطای کلی حذف.

</div>### کارایی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%B3%D8%A7%D8%AF%D9%87-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات ساده خواندن و حذف single-row.
- میانگین زمان پاسخ ≈ 90ms.

</div>### ردپای حسابرسی

<div id="bkmrk-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D8%AD%D8%B0" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ذخیره داده قبل از حذف جهت شفافیت.
- ردیابی اپراتور، IP و User-Agent.

</div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%AD%D8%B0%D9%81-%DA%AF%D8%B1%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امکان حذف گروهی refundها جهت بهبود کارایی.
- ارسال کد وضعیت HTTP 400 به جای ساختار 5005 برای خطای منطقی.

</div>### جمع‌بندی

این Endpoint ابزار دقیق حذف بازپرداخت‌هاست و با ثبت دقیق وقایع در SystemLog، کنترل حسابرسی کامل را تضمین می‌کند. طراحی ساده و واکنش‌گرا آن امکان اتصال مستقیم به UI درون‌سازمانی را فراهم می‌سازد.

<div id="bkmrk-root-delete-refund" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/statement

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/statement</td><td style="direction: ltr; text-align: left;">V2TradeController@statementTradeApi</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت صورت‌حساب (Statement) فاکتور مشخص‌شده با زبان تعیین‌شده</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-%D9%86%D8%B8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- شناسه فاکتور مورد نظر از `$request->id` دریافت می‌شود.
- زبان موردنظر کاربر از `$request['lang']['id']` استخراج می‌گردد (برای خروجی محلی‌سازی‌شده).
- در نهایت، متد `TradeController::statementTrade()` با مقادیر `(id, lang, branch, ReferenceExtension)` فراخوانی و خروجی آن مستقیماً بازگردانده می‌شود.
- هیچ پردازش اضافی (پرداختی، بروزرسانی DB) در این Endpoint انجام نمی‌شود؛ صرفاً پاس‌ترو به لایه‌ی کنترل اصلی است.

</div>### پارامترهای ورودی درخواست

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه فاکتور (Serial یا Reference ID)</td></tr><tr><td>lang.id</td><td>integer</td><td>بله</td><td>شناسه زبان (۱=فارسی، ۲=انگلیسی، ۳=عربی)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه درخواست‌کننده</td></tr></tbody></table>

</div>```
{
  "id": 23051,
  "lang": {"id": 1},
  "branch": 12
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی

خروجی حاصل از اجرای تابع `TradeController::statementTrade()` بوده و معمولاً شامل داده‌ای ساختاریافته از صورت‌حساب کامل فاکتور است، شامل:

<div id="bkmrk-header%3A-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **header:** اطلاعات بالای فاکتور (شماره، تاریخ، شعبه، مشتری)
- **items:** اقلام خرید/فروش
- **summary:** جمع‌بندی مالی (خالص، مالیات، بدهی، طلب)
- **translations:** عناوین با توجه به زبان انتخابی

</div>```
{
  "status": true,
  "statement": {
    "header": {...},
    "items": [...],
    "summary": {...}
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط از طریق JWT معتبر (میدل‌ور authWithJwt).
- فاکتور فقط از شعبه مجاز قابل مشاهده است.

</div>### وابستگی‌ها

<div id="bkmrk-tradecontroller%3A%3Asta" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- TradeController::statementTrade()
- $this-&gt;ReferenceExtension (ویژگی داخلی کنترلر)
- DB::table('factors')

</div>### کارایی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B2%D9%85%D8%A7%D9%86-%D9%BE%D8%A7%D8%B3%D8%AE%3A-%DB%B5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ: ۵۰ms (بدون عملیات DB اضافی).
- مصرف حافظه: بسیار پایین، صرفاً serialization JSON.

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%86%D8%A8%D9%88%D8%AF-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت نبود فاکتور → خروجی Null یا Exception از `TradeController`.
- در صورت مقداردهی نادرست زبان یا شعبه → کد خطای اختصاصی TradeController.

</div>### اثرات جانبی

<div id="bkmrk-%D9%81%D8%A7%D9%82%D8%AF-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فاقد تغییر در داده‌ها (Read-Only Endpoint).
- عدم ثبت SystemLog (فقط فراخوانی API زیرمجموعه).

</div>### ردپای حسابرسی

فاقد ثبت مستقیم ولی درصورت فعال بودن Audit در TradeController، گزارش View Statement لاگ می‌شود.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-systemlog-%D8%AF%D8%A7%D8%AE" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن SystemLog داخلی برای رهگیری درخواست‌های صورت‌حساب.
- پشتیبانی از خروجی PDF مستقیم از طریق سوییچ `?format=pdf`.

</div>### جمع‌بندی

این Endpoint سطح Gateway برای دریافت صورت حساب از کنترلر TradeController است و وظیفه‌اش انتقال سریع پارامترهای فاکتور و زبان می‌باشد. منطق محاسبات و بازگردانی خروجی تماماً در کنترلر داخلی انجام می‌شود.

<div id="bkmrk-root-statement" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/request

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/request</td><td style="direction: ltr; text-align: left;">V2TradeController@requestTradeApi</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">درخواست اطلاعات تحویل (RequestTrade) برای فاکتور و تأمین‌کننده مشخص‌شده</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B2%D8%A8%D8%A7%D9%86-%D8%B1%D8%A7%D8%A8%D8%B7-%D8%A7%D8%B2-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دریافت زبان رابط از `$request['lang']['id']`.
- شناسه فاکتور از `$request->id`.
- شناسه تأمین‌کننده از `$request->supplier` استخراج می‌گردد.
- فراخوانی مستقیم تابع `TradeController::requestTrade(id, lang, branch, supplier, ReferenceExtension)` برای دریافت جزئیات تعامل با تأمین‌کننده.
- اطلاعات معمولاً شامل وضعیت رزرو، فرصت بازپرداخت، و زمان‌بندی درخواست خدمات است.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه فاکتور (Reference ID)</td></tr><tr><td>supplier</td><td>integer</td><td>بله</td><td>شناسه تأمین‌کننده فاکتور</td></tr><tr><td>lang.id</td><td>integer</td><td>بله</td><td>شناسه زبان رابط (۱، ۲ یا ۳)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه فعلی کاربر</td></tr></tbody></table>

</div>```
{
  "id": 23051,
  "supplier": 871,
  "lang": {"id": 1},
  "branch": 12
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

نتیجه همان خروجی `TradeController::requestTrade()` است و می‌تواند شامل:

<div id="bkmrk-status%3A-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%DA%A9%D9%84%DB%8C-%D8%B9%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **status:** وضعیت کلی عملیات (True/False)
- **request\_info:** اطلاعات جزئی از درخواست انجام‌شده به سمت تأمین‌کننده
- **supplier\_data:** داده‌های توصیفی از تأمین‌کننده مربوطه

</div>```
{
  "status": true,
  "request_info": {...},
  "supplier_data": {...}
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%81%D9%82%D8%B7-%D8%A8%D8%A7-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%88-%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط با JWT معتبر و شعبه مجاز قابل دسترسی است.
- هیچ تغییری در DB اتفاق نمی‌افتد مگر داخل TradeController.

</div>### وابستگی‌ها

<div id="bkmrk-tradecontroller%3A%3Areq" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- TradeController::requestTrade()
- $this-&gt;ReferenceExtension
- DB resources (factors, factor\_items, suppliers)

</div>### کارایی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B2%D9%85%D8%A7%D9%86-%D9%BE%D8%A7%D8%B3%D8%AE%3A-%DB%B6" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ: ۶۰ms.
- فقط یک تماس به لایه‌ی تجاری TradeController دارد.

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B9%D8%AF%D9%85-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D9%81%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت عدم یافتن فاکتور یا تأمین‌کننده → خروجی Null یا Exception داخلی.
- کدهای خطا توسط TradeController مدیریت می‌شوند.

</div>### اثرات جانبی

<div id="bkmrk-%D9%81%D8%A7%D9%82%D8%AF-%D8%A7%D8%AB%D8%B1-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%85%D8%B3%D8%AA%D9%82%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فاقد اثر تغییر مستقیم.
- ممکن است درخواست شبکه به API تأمین‌کننده ارسال گردد (وابسته به TradeController).

</div>### ردپای حسابرسی

در سطح V2TradeController داده‌ای ثبت نمی‌شود؛ اما در TradeController، هر پاسخ ممکن است در لاگ مربوط به supplier ذخیره شود.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-validation-%D8%B3%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن validation سمت Gateway برای اعتبارسنجی supplier و branch.
- افزودن log داخلی با `SystemLog::dispatch` مشابه سایر متدهای trade.

</div>### جمع‌بندی

این Endpoint نقش اتصال بین سیستم داخلی و تأمین‌کننده را بازی می‌کند و برای درخواست داده‌های وضعیت/تحویل خدمات از مرجع به کار می‌رود. سطح اجرای آن gateway‑type و فاقد منطق تجاری درونی است.

<div id="bkmrk-root-request" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/trade/commitment

<div id="bkmrk-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/commitment</td><td style="direction: ltr; text-align: left;">V2TradeController@commitmentTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">تولید محتوای قرارداد مالی (Commitment) بر پایه‌ی رکورد تعهدکننده در فاکتور و الگوی قرارداد شعبه</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D8%A7%D8%B2-%24reque" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">1. شناسه زبان از `$request['lang']['id']` گرفته می‌شود.
2. تابع درونی `computingInstallments()` با محاسبه‌ی مبلغ و تعداد اقساط (۴،۸،۱۲) محتوای HTML متغیر اقساط را ایجاد می‌کند.
3. تعهد‌کننده (`pledger`) بر اساس `pledger_id` واکشی می‌شود.
4. در صورتی که ردیف معتبر بود: 
    - فاکتور مرتبط از جداول `factors, operators, customers` استخراج می‌شود.
    - الگوی قرارداد از جدول `pages` با `type='contract_colleague_161'` بارگذاری می‌شود.
    - اطلاعات فاکتور و داده‌های داینامیک با مقادیر موجود در `$contractDb->data` یا در صورت عدم وجود، مقادیر پیش‌فرض جایگزین می‌شوند.
    - تمام Placeholderهای متنی (٪leader٪ ... ٪installments٪ و ...) در قالب قرارداد جایگزین می‌گردند.
    - پاسخ JSON شامل HTML نهایی قرارداد و داده‌های تکمیلی برگردانده می‌شود.
5. در صورت خطا (فاکتور غیرفعال یا صفحه ناموجود) کد 404 با پیام مناسب بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>pledger\_id</td><td>integer</td><td>بله</td><td>شناسه‌ی تعهدکننده (ردیف جدول pledgers)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه‌ی شعبه جاری برای یافتن صفحه‌ی قالب قرارداد</td></tr><tr><td>lang.id</td><td>integer</td><td>بله</td><td>شناسه زبان خروجی قرارداد</td></tr></tbody></table>

</div>```
{
  "pledger_id": 154,
  "branch": 12,
  "lang": {"id": 1}
}
```

<div id="bkmrk--1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### ساختار خروجی

<div id="bkmrk-status%3A-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- **status:** وضعیت موفقیت درخواست
- **contract:** محتوای HTML کامل قرارداد با جایگزینی داده‌ها
- **data:** شامل اطلاعات صفحه، تأییدیه و مشخصات فاکتور متعهد

</div>```
{
  "status": true,
  "time": 1732024532,
  "contract": "<html>...</html>",
  "data": {
    "page_details": {"page":18,"object_type":"pledger","object":154},
    "confirmation": 1,
    "serial_id": 13521,
    "internal": true,
    "leader": {...},
    "slug": "a9cD91bc",
    "track_code": "05-11023"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### امنیت

<div id="bkmrk-%D8%AA%D8%AD%D8%AA-%D9%85%D8%AD%D8%A7%D9%81%D8%B8%D8%AA-middlewar" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- تحت محافظت Middleware `authWithJwt`.
- دسترسی فقط برای کاربرانی که شعبه‌ی مرتبط دارند.
- در خروجی هیچ داده‌ی حساسی از قرارداد اصلی لو نمی‌رود.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27pledgers%27" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- DB::table('pledgers')
- DB::table('factors')
- DB::table('pages')
- Functions, Jalalian, StaticController, Carbon

</div>### کارایی

<div id="bkmrk-%D9%85%D8%AA%D9%88%D8%B3%D8%B7-%D8%B2%D9%85%D8%A7%D9%86-%D9%BE%D8%A7%D8%B3%D8%AE%3A-70%E2%80%93" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- متوسط زمان پاسخ: 70–90ms (اصلی بار پردازش متن قرارداد).
- بدون اعمال تراکنش سنگین DB.

</div>### مدیریت خطا

<div id="bkmrk-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1-%D9%86%D8%A7%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%E2%86%92-cod" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- فاکتور نامعتبر → code 5006
- صفحه قرارداد پیدا نشد → code 5001
- در صورت `status != 5` یا `print == 0` خروجی خطای منطقی بازگردانده می‌شود.

</div>### اثرات جانبی

خواندن داده از `contract` و `pages`. هیچ درج یا بروزرسانی‌ای انجام نمی‌شود (Read‑Only).

<div id="bkmrk--3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### ردپای حسابرسی

در این متد لاگ مستقیم ثبت نمی‌شود. مسیر بعدی `commitmentSubmitTrade` مسئول logging اقدامات کاربر در جدول SystemLog است.

<div id="bkmrk--4" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%AA%D9%81%DA%A9%DB%8C%DA%A9-%D8%AA%D8%A7%D8%A8%D8%B9-computing" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- تفکیک تابع `computingInstallments()` به یک Helper مستقل در لایه Service.
- افزودن کش سمت Redis برای صفحات قرارداد.

</div>### جمع‌بندی

این Endpoint خروجی HTML قرارداد مالی براساس اطلاعات فاکتور و تعهدکننده را تهیه می‌کند و مرحله‌ی مقدماتی قبل از ثبت در دیتابیس است. خروجی آماده‌ی چاپ یا نمایش به کاربر نهایی در فرانت‌اند می‌باشد.

<div id="bkmrk-root-commitment" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div></body></html>

# POST /api/v2/trade/commitment/submit

<div id="bkmrk-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/commitment/submit</td><td style="direction: ltr; text-align: left;">V2TradeController@commitmentSubmitTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ثبت یا بروزرسانی اطلاعات قرارداد تعهد در جدول `contract` و ثبت لاگ سیستمی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%D8%AC%D9%88%D8%AF-page_id" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">1. در صورت وجود `page_id`: 
    - داده‌ی پیشین قرارداد از DB استخراج و نگهداری می‌شود.
    - مقدار فیلد `data` در جدول contract با JSON جدید به‌روزرسانی می‌شود.
    - Job نوع `SystemLog::UpdateContract` با تأخیر ۱۰ دقیقه در صف `snailJob` ثبت می‌شود.
2. در غیر این صورت (ثبت جدید): 
    - قرارداد جدید در جدول `contract` درج و شناسه آن بازگردانده می‌شود.
    - لاگ نوع `StoreContract` ایجاد می‌گردد.
3. در پایان، پاسخ موفق شامل timestamp بازمی‌گردد.
4. در صورت بروز Exception، پیام خطا و Trace برگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>page\_id</td><td>integer</td><td>خیر</td><td>شناسه قرارداد موجود (برای بروزرسانی)</td></tr><tr><td>page</td><td>integer</td><td>در حالت درج بله</td><td>شناسه صفحه قالب در جدول pages</td></tr><tr><td>object\_type</td><td>string</td><td>بله</td><td>نوع شیء مرتبط (مثلاً pledger)</td></tr><tr><td>object</td><td>integer</td><td>بله</td><td>شناسه شیء مربوطه</td></tr><tr><td>data</td><td>object</td><td>بله</td><td>داده‌های تکمیل‌شده قرارداد (فرم نهایی)</td></tr></tbody></table>

</div>```
{
  "page_id": 18,
  "object_type": "pledger",
  "object": 154,
  "data": {
    "fullname": "علیرضا ایرانپور",
    "national_code": "1234567890",
    "installments": 8
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732024850
}
```

در صورت خطا:

```
{
  "status": false,
  "message": "خطا در ارسال اطلاعات.",
  "trace": [...]
}
```

<div id="bkmrk--2" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA%D9%88" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- احراز هویت اجباری توسط JWT.
- دسترسی فقط برای اپراتوری که قرارداد فاکتور را ایجاد کرده باشد.

- DB::table('contract')
- SystemLog::dispatch
- Carbon, Exception
- Queue: snailJob

</div>### کارایی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AF%D8%B1%D8%AC%2F%D9%88%DB%8C%D8%B1%D8%A7%DB%8C%D8%B4-%D8%A8%D8%B3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- عملیات درج/ویرایش بسیار سبک (زیر 20ms).
- Jobs با تأخیر در صف ثبت می‌شوند، بنابراین زمان پاسخ سریع است.

</div>### مدیریت خطا

<div id="bkmrk-%D8%A8%D9%84%D9%88%DA%A9-try%2Fcatch-%D9%85%D8%A7%D9%86%D8%B9-" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- بلوک try/catch مانع از شکست کل درخواست می‌شود.
- بازگرداندن Trace برای دیباگ در محیط تست فعال است.

</div>### اثرات جانبی

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-%D8%AC" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- به‌روزرسانی مستقیم جدول `contract`.
- ثبت jobهای لاگ سیستمی با تأخیر.

</div>### ردپای حسابرسی

تمام عملیات ذخیره و ویرایش قرارداد در قالب **SystemLog** ثبت می‌شود (نوع StoreContract یا UpdateContract) شامل `by, ip, agent`.

<div id="bkmrk--3" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B5%D8%AD%D8%AA-%D8%B3%D8%A7%D8%AE" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;">- افزودن بررسی صحت ساختار داده ورودی (validation دقیق بر اساس Page Schema).
- افزودن اعلان پیامکی پس از ثبت (کدهای یادداشت‌شده آماده فعال‌سازی است).

</div>### جمع‌بندی

این Endpoint عملیات ذخیره و ویرایش داده‌های قرارداد تعهد را مدیریت می‌کند و به‌همراه SystemLog تضمین ردپای دقیق دارد. ترکیب منطقی این دو متد، جریان کامل «نمایش قرارداد → تأیید و ثبت نهایی» را می‌سازد.

<div id="bkmrk-root-commitment-submit" style="font-family: Vazir,Tahoma; direction: rtl; text-align: justify; line-height: 1.95;"></div>

# POST /api/v2/trade/payment-receipt

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/trade/payment-receipt</td><td style="direction: ltr; text-align: left;">V2TradeController@paymentReceiptTradeApi</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت و تولید رسید پرداخت (Payment Receipt) فاکتور مربوطه</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D8%A7%D8%B2-%24reque" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- شناسه زبان از `$request['lang']['id']` و شناسه فاکتور از `$request->id` دریافت می‌شود.
- پارامترهای لازم برای تولید رسید شامل: 
    - فاکتور (id)
    - زبان رابط (lang)
    - شناسه شعبه (branch)
    - مقدار داخلی `$this->ReferenceExtension`
    
    به متد `TradeController::paymentReceiptTrade()` پاس داده می‌شوند.
- متد `TradeController::paymentReceiptTrade()` خروجی رسید نهایی را به‌صورت داده ساخت‌یافته بازمی‌گرداند.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه فاکتور (reference)</td></tr><tr><td>lang.id</td><td>integer</td><td>بله</td><td>شناسه زبان خروجی (۱=فارسی،۲=انگلیسی،۳=عربی)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>کد شعبه درخواست‌کننده</td></tr></tbody></table>

</div>```
{
  "id": 13725,
  "lang": {"id": 1},
  "branch": 12
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

پاسخ تابع `TradeController::paymentReceiptTrade()` به‌صورت JSON بازگردانده می‌شود. ساختار معمول شامل:

<div id="bkmrk-status%3A-%D9%85%D9%88%D9%81%D9%82-%2F-%D9%86%D8%A7%D9%85%D9%88%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **status:** موفق / ناموفق
- **receipt:** جزئیات پرداخت و مشخصات فاکتور
- **customer:** اطلاعات پرداخت‌کننده / خریدار
- **details:** مقادیر مربوط به مبالغ، شماره فاکتور، تاریخ، ارجاع و سریال

</div>```
{
  "status": true,
  "receipt": {
    "factor_id": 13725,
    "serial": "F-2025-1105",
    "branch": "اصفهان مرکزی",
    "amount": 12800000,
    "currency": "IRR",
    "datetime": "2025-11-19 14:31",
    "payment_type": "wallet"
  },
  "customer": {...}
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D8%AA%D8%AD%D8%AA-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-authwithjw" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تحت کنترل `authWithJwt`.
- فقط اپراتورهایی که به فاکتور مربوطه دسترسی دارند می‌توانند رسید را مشاهده کنند.
- کد مرجع داخلی `ReferenceExtension` برای ایمنی عددی در IDها اعمال می‌شود.

</div>### وابستگی‌ها

<div id="bkmrk-tradecontroller%3A%3Apay" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- TradeController::paymentReceiptTrade()
- فیلدهای پنهان داخلی ReferenceExtension
- جدول factors و pays

</div>### کارایی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B3%D8%B1%D8%B9%D8%AA-%D9%BE%D8%A7%D8%B3%D8%AE%3A-%7E" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین سرعت پاسخ: ~45ms (کاملاً ایستا، بدون تراکنش سنگین).
- فقط فرایند خوانش، بدون تغییر در پایگاه‌داده.

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%86%D8%A7%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A8%D9%88%D8%AF%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت نامعتبر بودن ID → خروجی Null یا خطای مدیریت‌شده در TradeController.
- در صورت فقدان زبان یا branch → خطای پارامتر ناقص.

</div>### اثرات جانبی

فاقد اثرات جانبی. فقط خروجی نمایشی و هیچ داده‌ای در DB تغییر نمی‌کند.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ردپای حسابرسی

در این متد SystemLog ثبت نمی‌شود اما در سطح زیرین TradeController ممکن است عملیات مشاهده‌ی رسید ثبت شود.

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت خروجی PDF رسید.
- اطمینان از ذخیره رویداد “ViewReceipt” در SystemLog برای رهگیری دقیق‌تر.

</div>### جمع‌بندی

این Endpoint یک Gateway سبک برای تولید رسید پرداخت فاکتور است که منطق اصلی نمایش و فرمت داده را از TradeController ارث می‌برد. بدون منطق تجاری سنگین، فقط داده‌ی نهایی را برمی‌گرداند.

<div id="bkmrk-root-payment-receipt" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/logs

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/logs</td><td style="direction: ltr; text-align: left;">V2TradeController@logsTrade</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">بازیابی کامل لاگ‌های سیستمی ثبت‌شده توسط Service Layer و SystemLog</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%86%D9%88%D8%B9-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%DB%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دریافت نوع عملیات یا فیلتر لاگ از `$request['action']`.
- فراخوانی تابع `Visa::showSystemLogs(action)` جهت واکشی داده‌ها.
- بازگردانی داده‌های جمع‌آوری‌شده در آرایه `data` با وضعیت موفقیت و زمان timestamp.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع عملیات برای فیلتر لاگ‌ها (مانند `StoreContract`، `UpdateContract`، `SendNotify` و...)</td></tr></tbody></table>

</div>```
{
  "action": "SendNotify"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

در خروجی JSON داده‌های لاگ فیلترشده بازگردانده می‌شوند:

<div id="bkmrk-status%3A-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **status:** وضعیت موفقیت
- **time:** مهر زمانی UNIX
- **data:** آرایه‌ای از رکوردهای لاگ

</div>```
{
  "status": true,
  "time": 1732026322,
  "data": [
    {
      "type": "SendNotify",
      "goal": 1281,
      "ip": "192.168.1.101",
      "by": 12,
      "datetime": "2025-11-19 14:32:05",
      "message": "ارسال اعلان موفق"
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D8%AA%D9%86%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- تنها اپراتورها می‌توانند لاگ‌هایی را مشاهده کنند که به شعبه یا فاکتور خودشان مربوط است.

</div>### وابستگی‌ها

<div id="bkmrk-visa%3A%3Ashowsystemlogs" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Visa::showSystemLogs()
- SystemLog Queue Dataset
- DB: table system\_logs

</div>### کارایی

<div id="bkmrk-%D8%B2%D9%85%D8%A7%D9%86-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D9%85%D8%B9%D9%85%D9%88%D9%84%DB%8C%3A-%7E" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- زمان واکشی معمولی: ~30ms.
- بدون تراکنش یا Joinهای پیچیده.

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-action" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت ارسال Action نامعتبر → data خالی باز می‌گردد.
- در صورت بروز خطا در Visa::showSystemLogs خطای CustomException از کلاس Visa برگردانده می‌شود.

</div>### اثرات جانبی

این متد فقط داده لاگ‌ها را می‌خواند، هیچ اثر نوشتاری ندارد.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ردپای حسابرسی

به‌صورت مستقیم لاگی ثبت نمی‌کند، اما خروجی آن اطلاعات تمام رخدادهای audit قبلی را نمایش می‌دهد.

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D9%81%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامترهای فیلتر زمان (از تاریخ تا تاریخ).
- امکان صفحه‌بندی (pagination) برای لاگ‌های حجیم.
- افزودن timestamp و branch در header پاسخ.

</div>### جمع‌بندی

این Endpoint برای مانیتورینگ و پشتیبانی اپراتورها طراحی شده تا تمامی رخدادهای ثبت‌شده در سامانه را reactive مشاهده کنند. سبک و صرفاً خوانشی است و جزو ابزارهای کاربردی محیط پشتیبانی Trade محسوب می‌شود.

<div id="bkmrk-root-logs" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/pledger/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/pledger/store</td><td style="direction: ltr;">V2TradeController@storePledger</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">افزودن رکورد جدید تعهدکننده به جدول `pledgers`</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-%D8%B9%D8%AF%D8%AF%DB%8C-%D9%88%D8%A7%D8%B1%D8%AF%D8%B4%D8%AF%D9%87-%D8%AF" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- مقدار عددی واردشده در فیلد `amount` بررسی می‌شود؛ اگر صفر یا تهی باشد خطای ۵۰۰۴ بازگردانده می‌شود.
- رشته‌ی `pledger` به‌صورت «نوع–شناسه» دریافت و با تابع `explode()` تفکیک می‌گردد (مثلاً `colleague-47`).
- در صورت معتبر بودن مبلغ، رکورد جدید در جدول `pledgers` درج می‌شود: 
    - `type` ← همان نوع تعهدکننده (colleague/operator/...)
    - `serial_id` ← شناسه‌ی طرف مقابل
    - `factor` ← شناسه‌ی فاکتور (از فیلد `serial_id` درخواست)
    - `amount` ← مبلغ تصفیه‌شده از ویرگول
- پس از درج، شناسه‌ی رکورد برگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 95%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>pledger</td><td>string</td><td>بله</td><td>ترکیب نوع و شناسه‌ی تعهدکننده (مثل `colleague-47`)</td></tr><tr><td>serial\_id</td><td>integer</td><td>بله</td><td>شناسه‌ی فاکتور اصلی</td></tr><tr><td>amount</td><td>string / integer</td><td>بله</td><td>مبلغ تعهد (با یا بدون جداکننده ویرگول)</td></tr></tbody></table>

</div>```
{
  "pledger": "colleague-47",
  "serial_id": 10234,
  "amount": "12,000,000"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

```
موفق:
{
  "status": true,
  "time": 1732026400,
  "data": 582  // شناسه‌ی رکورد در جدول pledgers
}

ناموفق (مبلغ صفر):
{
  "status": false,
  "code": 5004,
  "message": "تعهد باید دارای مبلغ باشد"
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D8%A7%D8%B2-%D8%B7%D8%B1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- احراز هویت JWT از طریق Middleware `authWithJwt`.
- کاربر باید اپراتور ثبت‌کننده فاکتور باشد (تشخیص از فیلد `$request->get('operator')`).

</div>### وابستگی‌ها

<div id="bkmrk-db%3A-table-pledgers-c" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- DB: table `pledgers`
- Carbon (timestamps)
- SystemLog Queue (type=StorePledger)
- UpdateRedis Job

</div>### کارایی

عملیات درج تک‌مرحله‌ای، میانگین تأخیر &lt;25ms. ادغام با صف رخدادهای لاگ و Redis غیرهمزمان.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-5004-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%A8%D9%84" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- کد خطا **5004** برای مبلغ نامعتبر.
- سایر خطاهای SQL یا اتصال توسط Laravel ExceptionHandler گرفته می‌شود.

</div>### اثرات جانبی

<div id="bkmrk-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-job-systemlog%3A" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- ارسال job `SystemLog::dispatch(type=StorePledger)` با تأخیر ۱۰ دقیقه روی صف `snailJob`.
- به‌روزرسانی سریع Redis با کلیدهای `financial`، `information`، `pledgers` از طریق صف `fastJob`.

</div>### ردپای حسابرسی

ورود در SystemLog با اطلاعات عامل (IP, agent, operator\_id) برای شفافیت کامل تراکنش ثبت تعهد.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%B3%D9%85%D8%AA-%D8%B3%D8%B1%D9%88%D8%B1-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- اعتبارسنجی سمت سرور برای نوع pledger مجاز (colleague, operator,…).
- برگشت کل رکورد ایجادشده بجای فقط id (برای رابط‌های مدیریتی).

</div>### جمع‌بندی

این متد افزودن پایه‌ای تعهدکننده برای هر فاکتور است و مسیر ورود رسمی داده‌ی مالی تعهد در چرخه‌ی Trade محسوب می‌شود.

<div id="bkmrk-root-pledger-store" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/pledger/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/pledger/update</td><td style="direction: ltr;">V2TradeController@updatePledger</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش اطلاعات تعهدکننده موجود در جدول `pledgers`</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%D8%A8%D9%84%D8%BA-%D9%85%D8%B4%D8%A7%D8%A8%D9%87-%D8%A8%D8%A7-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- بررسی مبلغ مشابه با متد ذخیره: اگر صفر یا تهی → بازگرداندن خطای ۵۰۰۴.
- داده‌های جدید برای فیلدهای `type`، `serial_id` و `amount` ساخته می‌شود.
- داده‌ی قبلی از جدول `pledgers` واکشی و تفاوت‌ها با `Functions::arrayDiff()` محاسبه می‌شود.
- به‌روزرسانی رکورد، سپس ثبت رخداد `UpdatePledger` در صف `snailJob` و بروزرسانی سریع Redis.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 95%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>pledger\_id</td><td>integer</td><td>بله</td><td>شناسه‌ی رکورد فعلی در جدول pledgers</td></tr><tr><td>pledger</td><td>string</td><td>بله</td><td>رشته‌ی نوع-شناسه‌ی تعهدکننده جدید</td></tr><tr><td>amount</td><td>string / integer</td><td>بله</td><td>مبلغ جدید تعهد</td></tr><tr><td>serial\_id</td><td>integer</td><td>بله</td><td>شناسه فاکتور برای انتقال داده مالی</td></tr></tbody></table>

</div>```
{
  "pledger_id": 582,
  "pledger": "operator-19",
  "amount": "8,500,000",
  "serial_id": 10234
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

```
موفق:
{
  "status": true,
  "time": 1732026488
}

ناموفق:
{
  "status": false,
  "code": 5004,
  "message": "تعهد باید دارای مبلغ باشد"
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA.-%D8%A7%D9%BE%D8%B1%D8%A7%D8%AA%D9%88" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- JWT لازم است.
- اپراتورِ جاری از درون Request استخراج شده (`$request->get('operator')`).

</div>### وابستگی‌ها

<div id="bkmrk-db%3A-table-pledgers-f" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- DB: table `pledgers`
- Functions::arrayDiff()
- SystemLog Queue (type=UpdatePledger)
- UpdateRedis Queue

</div>### کارایی

فرآیند دوبخشی خواندن→ویرایش→ارسال به صف، با میانگین زمان اجرا ۳۰–۴۰ ms.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-%DA%A9%D8%AF-%DB%B5%DB%B0%DB%B0%DB%B4-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%A8%D9%84%D8%BA-%D8%B5%D9%81" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- کد ۵۰۰۴ برای مبلغ صفر.
- درصورت عدم وجود `pledger_id` پاسخ HTTP 200 ولی `status=false`.

</div>### اثرات جانبی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-log-%D8%A7%D8%B2-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-%28" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- ثبت Log از تغییرات (داده‌ی پیشین در فیلد `data` در SystemLog).
- بروزرسانی Redis در سه بخش اطلاعاتی.

</div>### ردپای حسابرسی

تغییرات پیمایش‌پذیر هستند: تمامی مقادیر قبل از ویرایش در لاگ ذخیره می‌شوند تا تاریخچه بازیابی‌پذیر باشد.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-validation-%D9%84%D8%A7%D8%B1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;">- اعمال Validation لاراول برای کنترل `exists:pledgers,id`.
- ذخیره `operator_id` در جدول pledgers برای هم‌سوسازی با ردپای حسابرسی.

</div>### جمع‌بندی

این Endpoint عملیات ویرایش تعهدکننده را انجام می‌دهد و با ثبت دقیق تفاوت‌ها در SystemLog، بخشی از شفافیت مالی سیستم را تضمین می‌کند.

<div id="bkmrk-root-pledger-update" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/pledger/delete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 95%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/pledger/delete</td><td style="direction: ltr;">V2TradeController@deletePledger</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف رکورد تعهدکننده از جدول `pledgers`</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D9%BE%D8%B1%D8%A7%D8%AA%D9%88%D8%B1-%D8%AC%D8%A7%D8%B1%DB%8C-%D8%A7%D8%B2-%24req" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- اپراتور جاری از `$request->get('operator')` استخراج می‌شود.
- با استفاده از `id` فیلد `pledger_id`، رکورد متناظر در جدول `pledgers` حذف می‌شود.
- در صورت موفقیت حذف: 
    - رخداد `DeletePledger` به صف `snailJob` با تأخیر ۱۰ دقیقه ارسال می‌گردد.
    - داده‌های مالی و اطلاعاتی در Redis از طریق `UpdateRedis` به‌روزرسانی می‌شوند.
- در صورت بروز هرگونه استثنا، کد خطای ۵۰۰۳ بازگردانده شده و گزارش در `Visa::AddSystemReport()` ثبت می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 95%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>pledger\_id</td><td>integer</td><td>بله</td><td>شناسه رکورد تعهدکننده جهت حذف</td></tr><tr><td>serial\_id</td><td>integer</td><td>بله</td><td>شناسه فاکتور برای sync با Redis</td></tr></tbody></table>

</div>```
{
  "pledger_id": 582,
  "serial_id": 10234
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

```
موفق:
{
  "status": true,
  "time": 1732027111
}

خطا:
{
  "status": false,
  "time": 1732027111,
  "code": 5003,
  "message": "SQLSTATE[23000]: Integrity constraint violation..."
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D9%84%D8%A7%DA%AF-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT الزامی است.
- لاگ دسترسی از طریق SystemLog به‌صورت asynchronous ثبت می‌گردد.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27pledgers%27" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- DB::table('pledgers')
- SystemLog Queue (type=DeletePledger)
- UpdateRedis Job
- Visa::AddSystemReport()

</div>### کارایی

عملیات حذف تک‌خطی، زمان متوسط &lt;20 ms. صف‌ها به‌صورت ناهمگام اجرا می‌شوند.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-%DA%A9%D8%AF-%DB%B5%DB%B0%DB%B0%DB%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D8%B3%D8%AA%D8%AB%D9%86%D8%A7%D9%87" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- کد ۵۰۰۳ برای استثناهای SQL یا حذف غیرممکن.
- ثبت کامل Trace در Visa SystemReport.

</div>### اثرات جانبی

<div id="bkmrk-%D8%A2%D8%BA%D8%A7%D8%B2-%D8%AF%D9%88-%D8%B5%D9%81-%D9%85%D8%B3%D8%AA%D9%82%D9%84-%28sn" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- آغاز دو صف مستقل (snailJob و fastJob).
- عدم بازگردانی تراکنش (delete مستقیم بدون Transaction).

</div>### ردپای حسابرسی

جزئیات حذف در SystemLog ثبت می‌شود (goal=pledger\_id ، by=operator\_id).

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-transacti" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- اضافه کردن Transaction هنگام حذف اگر وابستگی به پرداخت‌ها وجود دارد.
- اضافه کردن وضعیت soft delete.

</div>### جمع‌بندی

این Endpoint ساده‌ترین ولی حیاتی‌ترین عملیات حذف تعهد را مدیریت می‌کند و برای حفظ صحت داده‌ها در Redis و SystemLog از Jobهای مجزا بهره می‌گیرد.

<div id="bkmrk-root-pledger-delete" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/purchases/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/purchases/list</td><td style="direction: ltr;">V2TradeController@purchasesWebsite</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست پرداخت‌ها و خریدهای موقت از جدول `temporary_payment`</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%A7%D8%B2-%D8%AC%D8%AF%D9%88%D9%84-temp" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- اطلاعات از جدول `temporary_payment` فیلتر می‌شود بر اساس: 
    - شناسه‌ی **branch** (شعبه‌ی جاری درخواست‌کننده)
    - محدوده‌ی زمانی `search.from` تا `search.to` یا به‌صورت پیش‌فرض ماه جاری
    - بر اساس وضعیت `status`؛ اگر تعیین نشود، مقدار پیش‌فرض \[1, 2\]
- نتیجه بر اساس **id Desc** مرتب شده و با `paginate()` صفحه‌بندی می‌شود.
- هر آیتم با توجه به `operator_type` نگاشت می‌شود: 
    - **b2e:** اطلاعات از جدول operators
    - **b2b:** از colleague \_auth و colleagues
    - **b2c:** از customers
- در نهایت داده‌ها در قالب فیلدهای operator، gateway\_id، request، response، status و تاریخ‌ها بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه‌ی شعبه‌ی درخواست‌کننده</td></tr><tr><td>search\[from\]</td><td>string (Y-m-d)</td><td>خیر</td><td>تاریخ شروع فیلتر بر اساس `created_at`</td></tr><tr><td>search\[to\]</td><td>string (Y-m-d)</td><td>خیر</td><td>تاریخ پایان فیلتر</td></tr><tr><td>search\[status\]</td><td>integer</td><td>خیر</td><td>وضعیت پرداخت (۱ یا ۲)</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>بله</td><td>ایندکس شروع صفحه</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>بله</td><td>تعداد آیتم در هر صفحه</td></tr></tbody></table>

</div>```
{
  "branch": 12,
  "search": {"from": "2025-11-01", "to": "2025-11-19", "status": 2},
  "paginate": {"start": 0, "length": 25}
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

<div id="bkmrk-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87%E2%80%8C%D8%A7%DB%8C-%D8%A7%D8%B2-%D8%B3%D9%88%D8%A7%D8%A8%D9%82-%D9%BE%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- آرایه‌ای از سوابق پرداخت با جزئیات اپراتور و وضعیت، همراه با فراداده‌ی زمانی.

</div>```
{
  "items": [
    {
      "id": 201,
      "operator": {
        "type": "b2b",
        "fullname": {
          "first_name": {"fa": "دفتر مشهد", "en": false},
          "last_name": {"fa": " - ali", "en": false}
        }
      },
      "gateway_id": "IDPAY-94fa ... ",
      "request": {...},
      "response": {...},
      "status": 1,
      "created_at": "2025-11-19T10:22:01",
      "updated_at": "2025-11-19T10:22:01"
    }
  ],
  "meta": {"timestamp": 1732027206}
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C.-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT الزامی.
- اطلاعات صرفاً مرتبط با شعبه‌ی operator قابل مشاهده است.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27temporary" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- DB::table('temporary\_payment')
- DB::table('operators')
- DB::table('colleague\_auth')
- DB::table('customers')
- Carbon (date filters)

</div>### کارایی

<div id="bkmrk-%D8%AA%D8%B7%D8%A8%DB%8C%D9%82-range-%D8%B2%D9%85%D8%A7%D9%86%DB%8C-%D9%88-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- تطبیق Range زمانی و وضعیت با Index روی created\_at.
- میانگین تأخیر اجرای کوئری: ~60 ms با pagination 25 رکوردی.

</div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%D8%B1%D9%88%D8%AF-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%D8%A8" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- در صورت ورود تاریخ با فرمت اشتباه، خطای Carbon Exception.
- برای وضعیت‌های خارج از بازه‌ی فعال، نتایج خالی بازگردانده می‌شود.

</div>### اثرات جانبی

فاقد اثر نوشتاری؛ فقط عملیات خواندن.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ردپای حسابرسی

این Endpoint گزارش‌دهنده است و هیچ داده‌ای در SystemLog ثبت نمی‌کند.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%85%D8%B1%D8%AA%D8%A8%E2%80%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- افزودن پارامتر مرتب‌سازی پویا (orderBy بر اساس ستون انتخابی).
- امکان فیلترگذاری ترکیبی بر اساس نوع اپراتور.
- پشتیبانی از export CSV.

</div>### جمع‌بندی

این Endpoint داشبورد پرداخت‌های آنلاین و در حال پردازش را تغذیه می‌کند و بخشی از ماژول رصد تراکنش‌های مالی وب‌سایت (Website Purchase Monitor) است.

<div id="bkmrk-root-purchases-list" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# PUT /api/v2/purchase/retry/{id}

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"><table border="1" style="width: 95%; border-collapse: collapse; text-align: center; margin: auto;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">PUT</td><td style="direction: ltr;">/api/v2/purchase/retry/{id}</td><td style="direction: ltr;">V2TradeController@purchaseWebsiteRetry</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">بازخوانی و تلاش مجدد برای تکمیل خرید آنلاین (temporary\_payment) با وضعیت ناموفق یا نیمه‌کامل</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%DB%8C-%D8%A7%D8%B2-%D8%AC" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- در ابتدا رکوردی از جدول `temporary_payment` جستجو می‌شود که با `branch` شعبه‌ی کاربر و شناسه `{id}` مطابقت داشته و وضعیت آن یکی از \[1, 2\] باشد.
- در صورت عدم یافتن رکورد مطابق شرایط، پاسخ JSON با وضعیت HTTP 409 و پیام خطا بازگردانده می‌شود.
- در صورت وجود، فیلد `status` رکورد مورد نظر به مقدار 1 به‌روزرسانی می‌گردد.
- سپس متد `CronController::completeOnlinePurchaseProcess($gateway_id)` فراخوانی می‌شود تا فرآیند پرداخت ناقص تکمیل گردد.
- در صورت موفقیت، پیام تأیید با HTTP 205 و ذکر رفرنس پرداخت برگردانده می‌شود؛ در غیر این صورت خطا با جزئیات پیام بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"><table border="1" style="width: 90%; border-collapse: collapse; text-align: center; margin: auto;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>Path</td><td>integer</td><td>بله</td><td>شناسه‌ی رکورد پرداخت موقت برای بازخوانی</td></tr><tr><td>branch</td><td>Body / Header</td><td>integer</td><td>بله</td><td>شناسه‌ی شعبه (branch\_id) برای بررسی مالکیت رکورد</td></tr></tbody></table>

</div>```
PUT /api/v2/purchase/retry/74
{
  "branch": 31
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### ساختار خروجی

<div id="bkmrk-%2A%2A%D9%85%D9%88%D9%81%D9%82-%28http%E2%80%AF205%29%3A%2A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">**موفق (HTTP 205):**</div>```
{
  "payload": {
    "message": "رفرنس 672CHECK-9 با موفقیت ایجاد شد."
  },
  "meta": {"timestamp": 1732028000}
}
```

<div id="bkmrk-%2A%2A%D8%AE%D8%B7%D8%A7---%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%DB%8C%D8%A7%D9%81%D8%AA-%D9%86" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">**خطا - رکورد یافت نشد (HTTP 409):**</div>```
{
  "error": {
    "code": 1000,
    "message": "آیتم مورد نظر یافت نشد و یا اجازه بازخوانی ندارد"
  },
  "meta": {"timestamp": 1732028000}
}
```

<div id="bkmrk-%2A%2A%D8%AE%D8%B7%D8%A7---%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-cronco" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">**خطا - اجرای CronController ناموفق (HTTP 409):**</div>```
{
  "error": {"code":1000,"message":"پرداخت قابل تکمیل نیست"},
  "meta": {"timestamp": 1732028000}
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%D8%A8%D8%A7-jwt-%D8%A7%D9%84" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- احراز هویت با JWT الزامی است.
- کاربر فقط مجاز به بازخوانی تراکنش‌های مربوط به شعبه‌ی خود است.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27temporary" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- DB::table('temporary\_payment')
- CronController::completeOnlinePurchaseProcess()
- authWithJwt Middleware

</div>### کارایی

عملیات سبک‌تر از 10 ms (به جز فراخوان CronController که وابسته به سرویس خارجی است).

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### مدیریت خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-1000-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D9%85%D9%87" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- کد خطا 1000 برای همه‌ی حالات بازخوانی ناموفق یا عدم وجود رکورد.
- در صورت بروز خطای دیتابیس، HTTP 409 با پیام عمومی ارسال می‌شود.

</div>### اثرات جانبی

<div id="bkmrk-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-status-%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- تغییر مقدار `status` رکورد در جدول temporary\_payment.
- احتمال اجرای مجدد فرآیند ایجاد `reference` مالی.

</div>### ردپای حسابرسی

هنگام موفقیت در ایجاد رفرنس جدید، وقایع در SystemLog و جدول عملیات خرید ذخیره می‌شوند (در لایه CronController).

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%A8" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- افزودن شناسه کاربر به کوئری برای محدودسازی دقیق‌تر امنیتی.
- افزودن Retry Count برای جلوگیری از حلقه‌ی بازخوانی بی‌پایان.

</div>### جمع‌بندی

این Endpoint برای بازیابی تراکنش‌های موقت و تکمیل خرید در صورت قطعی ارتباط یا بازگشت ناقص درگاه پرداخت طراحی شده است.

<div id="bkmrk-root-purchase-retry" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>

# GET /api/v2/flights/approved-rate

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/api/v2/flights/approved-rate</td><td style="direction: ltr;">V2TradeController@flightsApprovedRate</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">نمایش داده‌های آخرین نرخ‌های مصوب پروازی (Approved Flight Rate)</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%D9%87%D8%A7%DB%8C-%D8%AC%D8%AF%D9%88%D9%84-a" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- تمام رکوردهای جدول `approved_flight_rate` با `status = 1` دریافت می‌شوند.
- با استخراج داده فرودگاه‌ها از جدول `airports`، عنوان فارسی و انگلیسی مبدأ و مقصد هر مسیر تکمیل می‌شود.
- برای هر مسیر، روزهای فعال از جدول `airline_active_route` واکشی و خطوط هوایی مرتبط از `airlines` به آن‌ها نگاشت می‌شوند.
- خروجی نهایی شامل مسیرها با جزئیات کامل، محدوده حداقل/حداکثر قیمت و آخرین تاریخ به‌روزرسانی است.

</div>### پارامترهای ورودی

فاقد ورودی خاص است (بدون پارامترهای Query یا Body).

```
GET /api/v2/flights/approved-rate
Authorization: Bearer 
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### ساختار خروجی

```
{
  "items": [
    {
      "route_id": 92,
      "origin": {
        "iata": "THR",
        "title": {
          "fa": "تهران (THR)",
          "en": "Tehran (THR)"
        }
      },
      "destination": {
        "iata": "MHD",
        "title": {
          "fa": "مشهد (MHD)",
          "en": "Mashhad (MHD)"
        }
      },
      "least": 1450000,
      "most": 4980000,
      "active_days": [
        {
          "day": "monday",
          "airline": {
            "iata": "IR",
            "title": {"fa": "ایران‌ایر", "en": "IranAir"}
          }
        }
      ]
    }
  ],
  "payload": {"update_at": "2025-11-15 08:41:00"},
  "meta": {"timestamp": 1732028300}
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D8%AF%D8%B3%D8%AA%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- JWT الزامی است.
- دسترسی به داده‌ها فقط در سطح مشاهده است؛ بدون اجازه‌ی ویرایش.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27approved_" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- DB::table('approved\_flight\_rate')
- DB::table('airports')
- DB::table('airlines')
- DB::table('airline\_active\_route')

</div>### کارایی

در اکثر محیط‌ها (&lt; 60 ms) به دلیل cache سیستم DB و join‌های کم‌عمق انجام می‌شود.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%AE%D8%B7%D8%A7-%D8%AF%D8%B1-sql-%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- در صورت خطا در SQL یا Map داده‌ها، Exception با HTTP 400 بازگردانده می‌شود.
- ویژگی `trace` شامل مسیر کامل stack جهت خطایابی است.

</div>### اثرات جانبی

فاقد اثر جانبی؛ صرفاً واکشی داده‌ها.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### ردپای حسابرسی

در این روت لاگ ثبت نمی‌شود، ولی فراخوانی‌های DB در Logهای سطح سیستم ردگیری می‌گردد.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-query" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;">- افزودن پارامتر Query برای فیلتر بر اساس مبدا، مقصد یا خطوط هواپیمایی.
- ایجاد cache json در سطح application.

</div>### جمع‌بندی

این Endpoint به عنوان سرویس اطلاعات مرجع نرخ‌های رسمی پروازی عمل می‌کند و در ماژول Flight Rate Monitor استفاده می‌شود.

<div id="bkmrk-root-flights-approved-rate" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.8;"></div>

# POST /api/v2/operator/update-password

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/operator/update-password</td><td style="direction: ltr;">UserController@updatePassword</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">تغییر رمز عبور اپراتور (کاربر) با احراز رمز فعلی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%D8%A8%D8%A7-%D9%85%D8%AA%D8%AF-au" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- احراز هویت با متد `Auth::once` بر مبنای `personnel_id` و رمز فعلی (`last_password`) انجام می‌شود.
- درصورت اشتباه بودن رمز فعلی، پاسخ JSON با `status=false` و پیام خطای مناسب بازگردانده می‌شود.
- درصورت صحت رمز فعلی، رکورد کاربر جاری از مدل `User` واکشی شده و رمز جدید با `Hash::make` هش و ذخیره می‌شود.
- پس از موفقیت، یک لاگ ناهمگام `SystemLog(type=UpdatePassword)` در صف `snailJob` ایجاد می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 92%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>personnelId</td><td>integer</td><td>بله</td><td>کد پرسنلی اپراتور (User personnel\_id)</td></tr><tr><td>last\_password</td><td>string</td><td>بله</td><td>رمز عبور فعلی برای تأیید</td></tr><tr><td>new\_password</td><td>string</td><td>بله</td><td>رمز عبور جدید مورد نظر</td></tr></tbody></table>

</div>```
POST /api/v2/operator/update-password
{
  "personnelId": 102,
  "last_password": "oldPass@123",
  "new_password": "Secure#2025"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

<div id="bkmrk-%2A%2A%D9%85%D9%88%D9%81%D9%82%3A%2A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**موفق:**</div>```
{
  "status": true,
  "time": 1732031104
}
```

<div id="bkmrk-%2A%2A%D8%B1%D9%85%D8%B2-%D8%A7%D8%B4%D8%AA%D8%A8%D8%A7%D9%87%3A%2A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**رمز اشتباه:**</div>```
{
  "status": false,
  "time": 1732031104,
  "message": "کلمه عبور فعلی اشتباه می باشد"
}
```

<div id="bkmrk-%2A%2A%D8%AE%D8%B7%D8%A7%DB%8C-%D9%81%D8%B1%D8%A2%DB%8C%D9%86%D8%AF-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**خطای فرآیند به‌روزرسانی:**</div>```
{
  "status": false,
  "time": 1732031104,
  "message": "عملیات تغییر کلمه عبور با مشکل مواجه شد"
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت و کنترل دسترسی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-jwt%E2%80%AFtoken" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- استفاده از **JWT Token** الزامی.
- رمز عبور در سرور با `bcrypt` هش می‌شود و نسخه خام هرگز ذخیره نمی‌شود.
- احراز موقت با `Auth::once()` برای جلوگیری از زمان طولانی توکن اعتبار.

</div>### وابستگی‌ها

<div id="bkmrk-auth-facade-%28one-tim" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Auth Facade (one-time login)
- Hash::make()
- Carbon::now()
- SystemLog Job Queue (snailJob)

</div>### کارایی

میانگین زمان پاسخ سرور &lt;10 ms، صف لاگ asynchronous است.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-%D8%AE%D8%B7%D8%A7%D9%87%D8%A7%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A8%D8%A7-%D9%81%DB%8C%D9%84%D8%AF" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- خطاهای معتبر با فیلد `message` کاربرپسند بازگردانده می‌شود.
- به‌جای استثناء، خروجی JSON کنترل‌شده ارائه می‌شود.

</div>### اثرات جانبی

<div id="bkmrk-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%81%D9%88%D8%B1%DB%8C-%D8%B1%D9%85%D8%B2-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- تغییر فوری رمز کاربر در DB.
- ورود مجدد برای توکن فعلی توصیه می‌شود (Token Invalidation Plan در آینده).

</div>### ردپای حسابرسی

وقوع عملیات در SystemLog با نوع `UpdatePassword` و فیلد goal=personnelId ثبت می‌گردد.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D9%85%D9%86%D8%B7%D9%82-complexit" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- اعمال منطق Complexity Policy برای رمز جدید (طول &gt; 8 کاراکتر، حروف + نشانه).
- لاگ‌آوت خودکار پس از تغییر رمز.

</div>### جمع‌بندی

روت مذکور راه ساده اما امنی برای تغییر رمز عبور اپراتور فراهم می‌کند و با وجود کنترل‌های Auth::once و SystemLog، مطمئن و قابل ردیابی است.

<div id="bkmrk-root-update-password" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/passengers/search

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/passengers/search</td><td style="direction: ltr;">UserController@searchPassengers</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">جست‌وجوی سریع مسافران (مشتریان) با تطبیق نام، کد ملی یا پاسپورت</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%DA%AF%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-like-%D8%A8%D8%A7-%C2%AB%DB%B9" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- اگر مقدار `like` با «۹۹۹» شروع نشود، جست‌وجو آغاز می‌شود.
- از جدول `customers` داده‌ها بر اساس `first_name`، `last_name`، `national_code`، `passport_code`، `mobile` و … واکشی می‌شوند.
- در صورتی که `action == 'passport'` باشد فقط مسافرانی نمایش داده می‌شوند که پاسپورت ثبت‌شده دارند.
- در هر نتیجه، تابع کمکی `replaceWithStars()` برای سانسور بخش‌هایی از کد ملی یا پاسپورت در صورت عدم مجازبودن شعبه اعمال می‌شود.
- ملیت (nationality) از Redis کش می‌شود؛ در صورت نبود، از DB بازیابی و در Redis ذخیره می‌گردد.
- در خروجی نهایی دو حالت وجود دارد: 
    - **allow=true:** مشاهده‌ی کامل داده برای شعب مجاز.
    - **allow=false:** بخش‌هایی از داده به‌صورت ماسک‌شده بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 93%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>like</td><td>string</td><td>بله</td><td>عبارت مورد جست‌وجو (نام، کدملی، پاسپورت یا موبایل)</td></tr><tr><td>action</td><td>string</td><td>خیر</td><td>نوع جست‌وجو ('passport' یا سایر)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه‌ی شعبه برای بررسی مجوز مشاهده اطلاعات</td></tr></tbody></table>

</div>```
POST /api/v2/passengers/search
{
  "like": "رضا",
  "action": "passport",
  "branch": 25
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار خروجی

<div id="bkmrk-%D8%AF%D8%B1-%D8%AD%D8%A7%D9%84%D8%AA-%D9%88%D8%AC%D9%88%D8%AF-%D8%AF%D8%A7%D8%AF%D9%87%D8%8C-%D8%AE" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- در حالت وجود داده، خروجی یک آرایه از اشیاء Passenger است.

</div>```
[
  {
    "allow": true,
    "id": 5043,
    "sex": 1,
    "first_name": "Reza",
    "first_name_fa": "رضا",
    "last_name": "Moradi",
    "last_name_fa": "مرادی",
    "mobile": "09123456789",
    "national_code": "1234567890",
    "passport_code": "M3423112",
    "birth": {
      "fa": "1382/12/14",
      "en": "2004/03/04"
    },
    "nationality": {
      "id": 118,
      "iso": "IR",
      "en_nationality": "Iranian",
      "fa_nationality": "ایرانی"
    }
  }
]
```

<div id="bkmrk-%2A%2A%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B9%D8%AF%D9%85-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**در صورت عدم دسترسی (allow=false):**</div>```
[
  {
    "allow": false,
    "id": 3331,
    "mobile": "***********",
    "national_code": "123****890",
    "passport_code": "******",
    "birth": {"fa": false, "en": false},
    "nationality": false
  }
]
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D8%A8%D8%B1%D8%B1%D8%B3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT الزامی است.
- بررسی مالکیت شعبه قبل از نمایش داده کامل.
- سانسور خودکار داده‌ها برای شعب غیرمجاز.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27customers" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- DB::table('customers')
- Redis Cache (countries)
- Carbon + Jalalian
- Morilog\\Jalali\\CalendarUtils

</div>### کارایی

افزودن Limit 20 باعث شده به‌طور میانگین &lt;40 ms پاسخ دهد؛ داده کش‌شده Redis زمان را تا 2 ms کاهش می‌دهد.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B4%D8%B1%D9%88%D8%B9-like-%D8%A8%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- در صورت شروع `like` با 999 جستجو انجام نمی‌شود (خروجی خالی).
- در سایر موارد، خطاها توسط سطح سیستم مدیریت می‌شود.

</div>### اثرات جانبی

<div id="bkmrk-%DA%A9%D8%B4%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D9%85%D9%84%DB%8C%D8%AA-%DA%A9%D8%B4%D9%88%D8%B1-%D8%AF%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- کش‌سازی ملیت کشور در Redis برای بهبود سرعت.

</div>### ردپای حسابرسی

هیچ داده‌ای ثبت نمی‌شود (عملیات فقط خواندنی است).

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AD%D8%B0%D9%81-%D8%AE%D9%88%D8%AF%DA%A9%D8%A7%D8%B1-ca" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- افزودن حذف خودکار cache کشورها با TTL ۲۴ ساعته.
- امکان مرتب‌سازی خروجی بر اساس تاریخ تولد یا نام.
- جلوگیری از درخواست‌های بدون فیلتر طولانی (rate limit).

</div>### جمع‌بندی

این Endpoint ابزار سریع و امنی برای جست‌وجوی مشتریان (مسافران) است که با کنترل شعبه و ماسکینگ داده‌ها، هم دقت و هم امنیت را تضمین می‌کند.

<div id="bkmrk-root-passengers-search" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/passenger/add-branch

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/passenger/add-branch</td><td style="direction: ltr;">UserController@addUserToBranch</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">افزودن مسافر به شعبه در صورت تطابق کد ملی یا پاسپورت</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%85%D8%B3%D8%A7%D9%81%D8%B1-%D8%A7%D8%B2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- واکشی رکورد مسافر از جدول `customers` بر اساس `passenger_id`.
- اگر مسافر وجود داشت: 
    - کنترل محدودیت درخواست با استفاده از `Redis key` (`check-add-passenger:{passenger_id}:{operator_id}`)، حداکثر ۵ تلاش در مدت ۱۵ دقیقه.
    - تطبیق اطلاعات بر اساس `action`: 
        - `national` → تطابق `national_code`.
        - `passport` → تطابق `passport_code`.
    - اگر تطابق برقرار بود: 
        - بروزرسانی فیلد `branch` با اضافه کردن شعبهٔ جدید (به صورت JSON ذخیره می‌شود) و حذف مقادیر تکراری با `array_unique()`.
        - تولید پاسخ شامل داده کامل مسافر، ملیت (از مدل `Country`) و تاریخ تولد به فرمت فارسی و میلادی.
    - اگر تطابق برقرار نبود: افزایش شمارنده در Redis و برگرداندن پیام خطا با تعداد فرصت‌های باقی‌مانده.
    - اگر شمارنده به سقف رسید: پیام محدودیت زمانی.
- اگر مسافر وجود نداشت: پیام خطای "مسافر یافت نشد".

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 93%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>passenger\_id</td><td>integer</td><td>بله</td><td>شناسه مسافر در جدول customers</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جاری</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>'national' یا 'passport' برای تعیین نوع تطابق</td></tr><tr><td>national\_code</td><td>string</td><td>خیر</td><td>کد ملی (برای action = national)</td></tr><tr><td>passport\_code</td><td>string</td><td>خیر</td><td>شماره پاسپورت (برای action = passport)</td></tr></tbody></table>

</div>```
POST /api/v2/passenger/add-branch
{
  "passenger_id": 502,
  "branch": 12,
  "action": "national",
  "national_code": "1234567890"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732034501,
  "data": {
    "allow": true,
    "id": 502,
    "first_name_fa": "علیرضا",
    ...
  },
  "message": "مسافر با موفقیت  به دفتر شما افزوده شد"
}
```

<div id="bkmrk-%2A%2A%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AE%D8%B7%D8%A7%DB%8C-%D8%AA%D8%B7%D8%A7%D8%A8%D9%82%3A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**نمونه خطای تطابق:**</div>```
{
  "status": false,
  "time": 1732034501,
  "message": "اطلاعات وارد شده مختص  این مسافر نمی باشد | درخواست باقی مانده: 3"
}
```

<div id="bkmrk-%2A%2A%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AE%D8%B7%D8%A7%DB%8C-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**نمونه خطای محدودیت:**</div>```
{
  "status": false,
  "time": 1732034501,
  "message": "متاسفانه در افزودن این مسافر به محدودیت خورده اید. لطفا 15 دقیقه دیگر تلاش فرمائید."
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-%D9%81%D9%82%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86-%D8%A8%D8%A7-jwt-%D9%85" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- فقط کاربران با JWT معتبر اجازه دارند.
- کنترل تعداد تلاش (Rate-limit) با Redis برای جلوگیری از brute force روی کد ملی/پاسپورت.

</div>### وابستگی‌ها

<div id="bkmrk-db%3A%3Atable%28%27customers" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- DB::table('customers')
- Redis
- Carbon
- Morilog\\Jalali\\Jalalian
- Country Model

</div>### کارایی

عملیات فقط شامل یک SELECT و یک UPDATE است، حدود 5~20 ms.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

تمام پاسخ‌ها در قالب JSON با فیلدهای `status` و `message` برگردانده می‌شوند.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### اثرات جانبی

تغییر فیلد branch در رکورد مسافر.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ردپای حسابرسی

هیچ لاگ مستقیم ثبت نمی‌شود.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%B1%D9%88%DB%8C%D8%AF%D8%A7%D8%AF-%D9%85%D9%88%D9%81%D9%82-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- ثبت لاگ رویداد موفق به SystemLog برای پیگیری.
- ارسال نوتیفیکیشن به کاربر شعبه در صورت اضافه شدن.

</div>### جمع‌بندی

روت امکان افزودن سریع مسافران موجود به شعبه را با کنترل امنیتی و محدودیت تلاش فراهم می‌کند.

<div id="bkmrk-passenger-add-branch" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/get_country

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/get\_country</td><td style="direction: ltr;">UserController@getCitizen</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت کشورها و ملیت‌های فعال</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%D9%87%D8%A7%DB%8C-count" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- واکشی رکوردهای `Country` با شرایط: 
    - `status=1` (فعال).
    - `fa_nationality` غیر تهی.
- انتخاب فیلدهای `id`, `iso`, `fa_nationality`, `en_nationality`.
- برگرداندن آرایه `countries` در خروجی JSON.
- در صورت خطا (Exception) ارسال پاسخ با کد وضعیت 400 و متن خطا.

</div>### پارامترهای ورودی

بدون پارامتر ورودی الزامی، تنها نیازمند JWT معتبر.

```
POST /api/v2/get_country
Authorization: Bearer <JWT>
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732034600,
  "data": {
    "countries": [
      {
        "id": 118,
        "iso": "IR",
        "fa_nationality": "ایرانی",
        "en_nationality": "Iranian"
      },
      ...
    ]
  }
}
```

<div id="bkmrk-%2A%2A%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AE%D8%B7%D8%A7%3A%2A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**نمونه خطا:**</div>```
{
  "status": false,
  "error": "Database connection failed"
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1." style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- نیازمند JWT معتبر.

</div>### Dependencies

<div id="bkmrk-country-model-larave" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Country Model
- Laravel Eloquent

</div>### کارایی

یک کوئری ساده SELECT، بسیار سریع (&lt;3 ms).

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

Try/Catch با خروجی JSON و کد وضعیت مناسب.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%A9%D8%B4-redis-%D8%A8%D8%A7-t" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- افزودن کش Redis با TTL 24h برای کاهش بار دیتابیس.

</div>### جمع‌بندی

روت امکان بازیابی سریع لیست کشورها و ملیت‌ها را برای مصرف در فرم‌ها و انتخاب‌ها فراهم می‌کند.

<div id="bkmrk-get-country" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/get_other_services

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/get\_other\_services</td><td style="direction: ltr;">UserController@getOtherServices</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست خدمات متفرقه بر اساس نوع و زبان انتخابی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AA%D9%84%D8%A7%D8%B4-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- تلاش برای واکشی داده‌ها از Redis با کلید `products:{lang_id}:{action}`.
- اگر داده موجود نبود: 
    - کوئری DB روی جدول `products` برای `type = action` و `status=1`.
    - ساخت آرایه خروجی با زبان انتخابی (فیلد dynamic: `title_{lang_id}`).
    - ذخیره نتیجه در Redis.
- بازگرداندن پاسخ JSON با کلید `titles` شامل لیست خدمات.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 92%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>lang.id</td><td>string</td><td>بله</td><td>شناسه زبان انتخابی (مثلاً `fa` یا `en`)</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع خدمت موردنظر (type در جدول محصولات)</td></tr></tbody></table>

</div>```
POST /api/v2/get_other_services
{
  "lang": { "id": "fa" },
  "action": "insurance"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732035200,
  "data": {
    "titles": [
      {
        "id": 44,
        "title": "بیمه مسافرتی",
        "description": "پوشش فوت و حوادث خارج از کشور"
      }
    ]
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA.-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT معتبر لازم است.
- داده‌ها فقط خواندنی هستند.

</div>### Dependencies

<div id="bkmrk-redis-db%3A%3Atable%28%27pro" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Redis
- DB::table('products')

</div>### کارایی

داده‌ها از Redis در &lt;1 ms، از DB در 5~15 ms خوانده می‌شوند.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

عدم وجود داده به معنای استفاده از fallback DB و ایجاد داده جدید در Redis است.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### اثرات جانبی

ذخیره نتایج در Redis برای بهبود عملکرد.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ردپای حسابرسی

لاگ‌گذاری مستقیم ندارد.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-ttl-%D8%B1%D9%88%DB%8C-%D8%AF" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- استفاده از TTL روی داده Redis برای به‌روزرسانی دوره‌ای.

</div>### جمع‌بندی

روت امکان دریافت سریع خدمات متفرقه را با پشتیبانی کش Redis فراهم می‌کند.

<div id="bkmrk-get-other-services" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/get_visa_country

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/get\_visa\_country</td><td style="direction: ltr;">UserController@getVisaCountry</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">بازیابی فهرست کشورها با نام و ملیت برای کاربردهای ویزا</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D9%85%D8%AF%D9%84-country-%D8%A8%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- کوئری مدل `Country` با شرط: 
    - `status=1`
    - `fa_name` غیر تهی
- انتخاب ستون‌های: `id`, `iso`, `fa_name`, `en_name`, `fa_nationality`, `en_nationality`.
- بازگرداندن داده‌ها در کلید `countries`.
- مدیریت خطا با Try/Catch و ارسال کد 400 در صورت Exception.

</div>### پارامترهای ورودی

بدون پارامتر ورودی لازم، صرفاً JWT معتبر.

```
POST /api/v2/get_visa_country
Authorization: Bearer <JWT>
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732035260,
  "data": {
    "countries": [
      {
        "id": 118,
        "iso": "IR",
        "fa_name": "ایران",
        "en_name": "Iran",
        "fa_nationality": "ایرانی",
        "en_nationality": "Iranian"
      }
    ]
  }
}
```

<div id="bkmrk-%2A%2A%D9%86%D9%85%D9%88%D9%86%D9%87-%D8%AE%D8%B7%D8%A7%3A%2A%2A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">**نمونه خطا:**</div>```
{
  "status": false,
  "error": "Database connection failed"
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA." style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT معتبر لازم است.

</div>### Dependencies

<div id="bkmrk-country-model" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Country Model

</div>### کارایی

کوئری ساده Select، زمان اجرا ~3 ms.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### مدیریت خطا

<div id="bkmrk-try%2Fcatch-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AC%D9%84%D9%88%DA%AF%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Try/Catch برای جلوگیری از کرش.

</div>### پیشنهاد بهبود

<div id="bkmrk-%DA%A9%D8%B4%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-%D9%86%D8%AA%D8%A7%DB%8C%D8%AC-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- کش‌کردن نتایج برای کاهش بار DB.

</div>### جمع‌بندی

روت امکان بازیابی سریع کشورها برای کاربردهای صدور ویزا را فراهم می‌کند.

<div id="bkmrk-get-visa-country" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/customers/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/customers/list</td><td style="direction: ltr;">UserController@passengersList</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت فهرست مشتریان (مسافران) با قابلیت جستجا و صفحه‌بندی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D8%AF%D8%B1-%D9%81%DB%8C%D9%84%D8%AF-j" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- داده ورودی در فیلد `json` به‌صورت string JSON ارسال می‌شود و Parse می‌گردد.
- محاسبه اندیس شروع لیست (`start`) و ارزش شمارش (`length`) برای صفحه‌بندی.
- فیلتر اصلی بر اساس مقدار `Data->search->value` تنظیم می‌شود: 
    - اگر مقدار عددی باشد: جستجو بر اساس id، national\_code، mobile، phone.
    - اگر رشته باشد: جستجو بر مبنای نام فارسی یا انگلیسی، یا فیلد office.
- اعمال شرط `whereJsonContains('branch', $request->get('branch'))` برای محدودکردن نمایش به شعبه جاری.
- دریافت داده‌ها با `paginate()` براساس طول و اندیس.
- برای هر رکورد: 
    - واکشی داده ملیت از Redis تحت کلید `countries:{country_id}`.
    - در صورت عدم وجود، کوئری DB و سپس کش‌گذاری در Redis.
    - تبدیل تاریخ تولد و تاریخ انقضای پاسپورت با تابع `Functions::int2DateTime`.
- بازگرداندن داده در قالب DataTables سازگار با `draw`, `recordsTotal`, `recordsFiltered` و آرایه `data`.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>json</td><td>stringified JSON</td><td>بله</td><td>داده‌های صفحه‌بندی و جستجو شامل `start`, `length`, `search->value`, `draw`.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جاری برای فیلتر رکوردها</td></tr></tbody></table>

</div>```
POST /api/v2/customers/list
{
  "json": "{\"start\":0,\"length\":10,\"search\":{\"value\":\"علیرضا\"},\"draw\":1}",
  "branch": 12
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "draw": 1,
  "recordsTotal": 1052,
  "recordsFiltered": 1052,
  "data": [
    {
      "passenger_id": 4021,
      "national_code": "1234567890",
      "birthday": "1383/12/10",
      "name_fa": "علیرضا",
      "lastname_fa": "ایرانپور",
      "citizenship": {
        "id": 118,
        "fa_name": "ایران",
        "fa_nationality": "ایرانی"
      },
      "sex": true
    }
  ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT معتبر برای دسترسی الزامی است.
- فیلتر رکوردها محدود به شعبه کاربر.

</div>### Dependencies

<div id="bkmrk-customer-model-redis" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Customer Model
- Redis
- DB::table('countries')
- Functions::int2DateTime

</div>### کارایی

پاسخ‌دهی مبتنی بر Redis بسیار سریع است (~3 ms برای داده‌های کش‌شده، ~25 ms برای واکشی اولیه).

<div class="align-right" id="bkmrk--3" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### مدیریت خطا

در صورت دریافت ورودی نامعتبر JSON یا branch خالی، منجر به پاسخ ناقص بدون code خاص می‌شود؛ بهتر است اعتبارسنجی اضافه شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### اثرات جانبی

کش‌گذاری کشورها در Redis برای آیتم‌های جدید.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### ردپای حسابرسی

ثبت لاگ مستقیم ندارد.

<div class="align-right" id="bkmrk--6" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### پیشنهاد بهبود

<div class="align-right" id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-ttl-%D8%A8%D9%87-re" style="font-family: Vazir, Tahoma; line-height: 1.85;">- اضافه‌کردن TTL به Redis برای تازه‌سازی خودکار ملیت‌ها.
- بهینه‌سازی جستجو با ایندکس‌های ترکیبی روی فیلدهای اسمی.

</div>### جمع‌بندی

روت امکان واکشی سریع و صفحه‌بندی‌شده‌ی لیست مسافران یک شعبه را فراهم می‌کند. طراحی آن برای محیط‌های DataTables بسیار مناسب است.

<div id="bkmrk-customers-list" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# GET /api/v2/cartable/categories

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/api/v2/cartable/categories</td><td style="direction: ltr;">UserController@cartableCategories</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست دسته‌بندی‌های کارتابل سیستم</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AA%D8%B9%D8%B1%DB%8C%D9%81-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%D8%AB%D8%A7%D8%A8%D8%AA-%D8%B4%D8%A7%D9%85" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- تعریف آرایه ثابت شامل ۶ دسته: 
    - درخواست‌های بررسی نشده (`unchecked\_requests`)
    - درخواست‌های رد شده (`rejected\_requests`)
    - درخواست‌های تایید شده (`approved\_requests`)
    - نامه‌های بررسی نشده (`unchecked\_letters`)
    - نامه‌های رد شده (`rejected\_letters`)
    - نامه‌های تایید شده (`approved\_letters`)
- بازگرداندن آرایه در قالب JSON با کلید `data`.
- مدیریت خطا با Try/Catch و ارسال پاسخ 400 در صورت Exception.

</div>### پارامترهای ورودی

بدون پارامتر ورودی؛ فقط نیاز به JWT معتبر.

```
GET /api/v2/cartable/categories
Authorization: Bearer <JWT>
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732035500,
  "data": [
    {"id":1,"slug":"unchecked_requests","title":"درخواست های بررسی نشده","icon":"heroicons-outline:puzzle"},
    {"id":2,"slug":"rejected_requests","title":"درخواست های رد شده","icon":"heroicons-outline:hand"},
    {"id":3,"slug":"approved_requests","title":"درخواست های تائید شده","icon":"heroicons-outline:inbox"},
    {"id":4,"slug":"unchecked_letters","title":"نامه های بررسی نشده","icon":"heroicons-outline:annotation"},
    {"id":5,"slug":"rejected_letters","title":"نامه های رد شده","icon":"heroicons-outline:thumb-down"},
    {"id":6,"slug":"approved_letters","title":"نامه های تائید شده","icon":"heroicons-outline:mail-open"}
  ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### امنیت

<div id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- JWT معتبر الزامی است.

</div>### Dependencies

<div id="bkmrk-exception-laravel-re" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- Exception
- Laravel Response Facade

</div>### کارایی

پاسخ کاملاً ایستا؛ زمان اجرا کمتر از 1 ms.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### مدیریت خطا

Exception به‌صورت کلی با status=false و کد خطا 400 برگردانده می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### اثرات جانبی

ندارد.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir, Tahoma; line-height: 1.85;"></div>### ردپای حسابرسی

ندارد.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%AC%D8%AF%D8%A7%DA%A9%D8%B1%D8%AF%D9%86-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A8%D9%87-%D9%81" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- جداکردن داده‌ها به فایل config برای چندزبانه‌سازی آسان.
- افزودن سطح دسترسی بر اساس نقش (role) در آینده.

</div>### جمع‌بندی

روت دسته‌بندی‌ها برای رابط‌های کارتابل کاربرد دارد و کاملاً ایستا است؛ طراحی ساده و سریع.

<div id="bkmrk-cartable-categories" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# GET /api/v2/cartable/requests/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/api/v2/cartable/requests/list</td><td style="direction: ltr;">UserController@listCartableRequests</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست درخواست‌های کارتابل بر اساس نوع دسته‌بندی (در حال بررسی / رد شده / تایید شده)</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-catego" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- بر اساس مقدار `category` سه حالت دارد: 
    - **category = 1:** درخواست‌های در حال بررسی برای مدیران، جانشین‌ها یا مدیرعامل.
    - **category = 2:** درخواست‌های ردشده برای کاربر جاری.
    - **category = 3:** درخواست‌های تایید شده (تکمیل‌شده).
- در هر حالت، دو نوع داده بازیابی می‌شود: 
    - `rollcall_licenses` → مرخصی‌ها و مأموریت‌ها.
    - `rollcalls` → درخواست‌های تردد ثبت‌نشده.
- منطق دسترسی بر اساس نقش: 
    - **CEO:** دسترسی به همه درخواست‌ها در شعبه، شامل فیلتر پالایش تایید و جانشین.
    - **مدیران:** درخواست‌های زمان‌دار و جانشینی مرتبط به گروه مدیریتی.
    - **کارمندان:** فقط درخواست‌های شخصی خود.
- برای هر مورد، رشته `title` با استفاده از نوع مرخصی (استحقاقی، استعلاجی، تشویقی، بدون حقوق، مأموریت) و نوع زمانی (روزانه / ساعتی) ساخته می‌شود.
- تاریخ‌ها با `Jalalian::fromFormat('Ymd', ...)->format('%A, %d %B %Y')` به فارسی تبدیل شده‌اند.
- جزئیات هر درخواست شامل اطلاعات تأیید، جانشین، و تأیید نهایی در ساختار سه‌گانه `details` درج می‌گردد.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>category</td><td>integer</td><td>بله</td><td>نوع دسته‌بندی درخواست‌ها (۱=درحال بررسی، ۲=رد شده، ۳=تایید شده).</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جاری.</td></tr><tr><td>operator</td><td>object (JWT extracted)</td><td>بله</td><td>اپراتور فعلی برای تشخیص نقش دسترسی و فیلتر رکورد.</td></tr></tbody></table>

</div>```
GET /api/v2/cartable/requests/list?category=1
Header: Authorization: Bearer <JWT>
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732025400,
  "data": [
    {
      "id": 551,
      "status": 1,
      "operator": {"id": 42, "name": "علیرضا ایرانپور"},
      "operation_type": "confirm",
      "type": "rollcall_licenses",
      "title": "آیا موافقت میکنید با درخواست مرخصی استحقاقی روزانه از تاریخ شنبه، 10 خرداد 1404 تا تاریخ دوشنبه، 12 خرداد 1404 | سفر شخصی",
      "tags": ["مرخصی استحقاقی","روزانه"],
      "details": {
        "confirm": {"by": {"id": 42,"name": "علیرضا"}, "at": "2025-11-19 09:10:04"},
        "final_approval": {"by": false, "at": false, "note": false}
      }
    }
  ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### امنیت و کنترل دسترسی

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85%DB%8C-%D9%85%D8%B3%DB%8C%D8%B1%D9%87%D8%A7-%D8%B2%DB%8C%D8%B1-%D9%85%DB%8C%D8%AF" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- تمامی مسیرها زیر میدل‌ور `authWithJwt` اجرا می‌شوند.
- بررسی نقش اپراتور از فیلد `operator->id` و مدیر شعبه از `offices.leader`.
- فیلتر رکوردها بر اساس JSON ساختار‌یافته در `office_departments.managers`.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-mor" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Morilog\\Jalali\\Jalalian
- Functions Helper
- StaticController::getOperators()

</div>### کارایی

به‌دلیل استفاده از کوئری‌های چنده‌گانه `distinct + leftJoin` زمان پاسخ حدود 50 تا 90 ms برای هر ۳ دسته است. مناسب برای پنل‌های داشبورد.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### مدیریت خطا

خطاهای دیتابیس یا ساختاری با catch(Exception) بازگردانده می‌شوند با بدنه شامل `message` و `trace`.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### اثرات جانبی

درخواست فقط خواندن دارد، جدول‌ها تغییری نمی‌کنند.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### ردپای حسابرسی

ثبت لاگ ندارد؛ پیشنهاد افزودن لاگ در عملیات تایید و رد آینده.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%AC%D8%AF%D8%A7%D8%B3%D8%A7%D8%B2%DB%8C-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D8%B3%D8%B7%D8%AD" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- جداسازی فیلترهای سطح دسترسی به Trait مشترک برای کنترل خوانایی.
- کاهش حجم کوئری با projection محدود و lazy load اپراتورها.

</div>### جمع‌بندی

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

<div id="bkmrk-cartable-requests-list" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/cartable/request/operation

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/cartable/request/operation</td><td style="direction: ltr;">UserController@operationCartableRequest</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ثبت یا حذف عملیات کارتابل برای مرخصی‌ها و ترددها (جانشین، تایید، تصویب نهایی، حذف)</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%86%D9%88%D8%B9-type-%D8%AA%D8%B5%D9%85" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- بر اساس نوع `type` تصمیم گرفته می‌شود: 
    - **rollcall\_licenses:** مرخصی‌ها و مأموریت‌ها.
    - **rollcall:** درخواست‌های تردد.
- فیلد `operation_type` مشخص‌کننده نوع اقدام: 
    - `substitute` → جانشین شدن.
    - `confirm` → تایید درخواست.
    - `final_approval` → تصویب نهایی مدیر.
    - `delete` → حذف رکورد.
- در هر حالت، جدول متناظر (`rollcall_licenses` یا `rollcalls`) با داده زمان فعلی `Carbon::now()-&gt;toDateTimeString()` و یادداشت (`note`) بروزرسانی می‌شود.
- در صورت `delete`، رکورد موردنظر از جدول حذف کامل می‌گردد.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>type</td><td>enum('rollcall\_licenses','rollcall')</td><td>بله</td><td>نوع درخواست هدف در کارتابل.</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه رکورد هدف عملیات.</td></tr><tr><td>operation\_type</td><td>enum('substitute','confirm','final\_approval','delete')</td><td>بله</td><td>نوع اقدام در مسیر.</td></tr><tr><td>status</td><td>integer</td><td>بله</td><td>کد وضعیت جدید رکورد.</td></tr><tr><td>note</td><td>string</td><td>خیر</td><td>توضیح متنی اقدام.</td></tr></tbody></table>

</div>```
POST /api/v2/cartable/request/operation
{
  "type": "rollcall_licenses",
  "id": 551,
  "operation_type": "confirm",
  "status": 3,
  "note": "تایید شد توسط مدیر واحد"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732035800
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-jwt-%D9%81%D8%B9%D8%A7%D9%84-%D8%AF%D8%A7%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- نیاز به JWT فعال دارد؛ اپراتور از درون توکن خوانده می‌شود.
- احراز سطح دسترسی برای حذف باید در سمت کلاینت یا سرویس مدیریت اضافه شود؛ در این کد هنوز بررسی نمی‌شود.

</div>### Dependencies

<div id="bkmrk-carbon-db-facade-req" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- Carbon
- DB Facade
- Request (Illuminate\\Http)

</div>### کارایی

به‌خاطر استفاده از `update()` مستقیم روی DB، زمان اجرا کمتر از 3 ms است. هیچ کوئری پیچیده ندارد.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### مدیریت خطا

با try/catch، در صورت خطای دیتابیس یا پارامتر، پاسخ JSON با کد 400 و فیلدهای `error` یا `message` بازگردانده می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### اثرات جانبی

تغییر مستقیم داده‌های منابع انسانی؛ حذف یا تغییر وضعیت رکوردها در پایگاه، بدون لاگ یا تراکنش محافظ.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir, Tahoma; line-height: 1.9;"></div>### ردپای حسابرسی

در حال حاضر هیچ ثبت لاگی ندارد؛ برای صحت سازمانی باید `SystemLog::dispatch()` اضافه گردد.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%88-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- مدیریت سطح دسترسی و جلوگیری از حذف توسط کاربران غیرمجاز.
- افزودن تراکنش DB برای جلوگیری از حذف ناقص رکوردهای وابسته.

</div>### جمع‌بندی

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

<div id="bkmrk-cartable-request-operation" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/calendar

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/api/v2/calendar</td><td style="direction: ltr;">UserController@calendar</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت تقویم کاری ماهانه پرسنل با اطلاعات شیفت، تردد، مرخصی، تعطیلات و وظایف.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%B2-%DA%A9%D9%84%D8%A7%D8%B3-%DA%A9%D9%85%DA%A9%DB%8C-attenda" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- از کلاس کمکی `Attendance` مقداردهی اولیه شیفت انجام می‌شود با متد `getPersonnelShiftWork(year, month, operator_id, shift_work, branch)`.
- مقادیر `login` و `time_work` از شیفت جاری استخراج و به آرایه روزهای هفته تبدیل می‌شوند.
- ورودی‌های ثبت‌شده در جدول `rollcalls` برای پرسنل در بازه بین روز اول تا سی و یکم همان ماه واکشی می‌شوند.
- درخواست‌ها با وضعیت: 
    - **status=3:** ورود تأیید‌شده.
    - **status=1:** ورود در انتظار تأیید (pending).
- مرخصی‌ها (جدول `rollcall_licenses`) با وضعیت‌های فعال استخراج می‌شوند، همراه با تجمیع نوع مرخصی با کوئری aggregate.
- تعطیلات رسمی (`holidays`) با فیلتر `year`, `month`, `status=1` دریافت می‌شوند.
- وظایف کارتابلی توسط `OfficialController::getTasks('all', false, operator_id, from, to, 1)` واکشی و در تقویم توزیع می‌شوند.
- در انتها متد `Attendance::calendar` داده نهایی را ساختاردهی و بازمی‌گرداند.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>year</td><td>integer</td><td>بله</td><td>سال جاری برای نمایش تقویم.</td></tr><tr><td>month</td><td>integer</td><td>بله</td><td>ماه هدف برای محاسبه شیفت و تردد.</td></tr><tr><td>operator</td><td>object</td><td>خیر</td><td>از JWT خوانده می‌شود، شامل `id`, `personnel_id`, `shift_work`.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه‌ای که اپراتور در آن مشغول است.</td></tr></tbody></table>

</div>```
GET /api/v2/calendar?year=1404&month=8
Authorization: Bearer <JWT>
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732036800,
  "shift": [
    {"title":"شنبه","login":"08:30","logout":"17:30"},
    {"title":"یکشنبه","login":"08:30","logout":"17:30"},
    {"title":"دوشنبه","login":"08:30","logout":"17:30"}
  ],
  "data": [
    { "day": 5, "type": "work", "status": "present", "tasks": [ {"title": "جلسه فنی"} ] },
    { "day": 6, "type": "holiday", "title": "تعطیل رسمی" }
  ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### امنیت و کنترل دسترسی

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D8%A7%D8%B7%D9%84%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- اطلاعات شخصی بر اساس شناسه اپراتور استخراج می‌شود؛ اپراتور تنها شیفت‌های خود را می‌بیند.

</div>### Dependencies

<div id="bkmrk-attendance-%28custom-l" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- Attendance (Custom Library)
- OfficialController
- Carbon
- CalendarUtils
- DB, Functions Helper

</div>### کارایی

به‌دلیل کوئری‌های هم‌زمان روی چند جدول، زمان اجرا بین 80 تا 120 ms است. کش داده‌های شیفت برای نسخه بعدی توصیه می‌شود.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### مدیریت خطا

اگر هیچ داده‌ای یافت نشود یا شیفت کاربر تعریف نشده باشد، پاسخ با `status=false` و پیام `اطلاعاتی یافت نشد` بازگردانده می‌شود.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### اثرات جانبی

خواندن اطلاعات بدون تغییر در دیتابیس.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### ردپای حسابرسی

ندارد.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%DA%A9%D8%B4-%D8%B4%DB%8C%D9%81%D8%AA%E2%80%8C%D9%87%D8%A7-%D9%88-%D8%AA%D8%B9%D8%B7%DB%8C%D9%84%D8%A7%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- کش شیفت‌ها و تعطیلات در Redis برای کاهش زمان پاسخ.
- ثبت لاگ در زمان لود تقویم برای تحلیل حضور و غیاب.

</div>### جمع‌بندی

روت تقویم کاری محوری‌ترین قسمت ماژول منابع انسانی است. داده شیفت، مرخصی، تعطیلات و وظایف را یکجا تجمیع کرده و در خروجی منسجم ماهانه ارائه می‌دهد.

<div id="bkmrk-calendar" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/personnel/traffic/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/personnel/traffic/store</td><td style="direction: ltr;">UserController@storeTrafficPersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ثبت ورود یا خروج پرسنل در جدول ترددها (rollcalls).</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%AE" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- داده‌ها از درخواست خوانده می‌شوند و در جدول `rollcalls` درج می‌شوند.
- ورودی‌ها شامل تاریخ، ساعت، توضیحات و شناسه پرسنلی هستند.
- ساعت از فرمت استاندارد `HH:mm` گرفته و به رشته عددی بدون جداکننده تبدیل می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>date</td><td>string (yyyy/mm/dd)</td><td>بله</td><td>تاریخ مربوط به تردد.</td></tr><tr><td>time</td><td>string (HH:mm)</td><td>بله</td><td>ساعت ثبت تردد.</td></tr><tr><td>description</td><td>string</td><td>خیر</td><td>توضیحات اختیاری درباره تردد.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>شناسه اپراتور از JWT شامل id و personnel\_id.</td></tr></tbody></table>

</div>```
POST /api/v2/personnel/traffic/store
{
  "date": "1404/08/22",
  "time": "08:45",
  "description": "شروع کاری به‌صورت اضافه‌کار"
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732037100
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- JWT معتبر الزامی است.
- دسترسی فقط برای پرسنل همان شعبه مجاز است.

</div>### Dependencies

<div id="bkmrk-functions%3A%3Acheckdate" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- Functions::checkDatetime()
- DB Facade
- Carbon
- Exception

</div>### کارایی

در حد 1–2 ms برای درج رکورد؛ سریع و کم‌هزینه.

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### مدیریت خطا

تمام خطاهای دیتابیس یا ورودی در try/catch گرفته شده و پاسخ JSON با `status=false` و فیلد `error` بازگردانده می‌شود.

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### اثرات جانبی

یک رکورد جدید در جدول تردد‌ها اضافه می‌شود.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### ردپای حسابرسی

ندارد؛ پیشنهاد می‌شود برای هر ثبت تردد `SystemLog::dispatch()` اضافه گردد.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%87%D9%85%E2%80%8C%D8%B2%D9%85%D8%A7%D9%86" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;">- افزودن بررسی هم‌زمان (Duplicate) برای جلوگیری از ثبت دو ورود در یک روز.
- اتصال به سیستم حضور و غیاب سخت‌افزاری.

</div>### جمع‌بندی

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

<div id="bkmrk-personnel-traffic-store" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/personnel/traffic/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/personnel/traffic/update</td><td style="direction: ltr;">UserController@updateTrafficPersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش وضعیت تردد پرسنل در جدول `rollcalls`.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%B2-request-%D8%B3%D9%87-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- از `Request` سه مقدار اصلی دریافت می‌شود: `id`، `status` و `operator`.
- با استفاده از کوئری `DB::table('rollcalls')->where('id', id)->update($update)` رکورد تردد موردنظر به‌روزرسانی می‌شود.
- فیلدهای اصلاح شده: 
    - **status:** وضعیت جدید تردد (۱=در انتظار، ۲=رد شده، ۳=تأیید شده).
    - **updated\_at:** زمان آخرین به‌روزرسانی با فرمت ISO.
    - **updated\_by:** شناسه اپراتور از JWT.
- در پایان پاسخ JSON برمی‌گردد با status=true و timestamp.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه رکورد در جدول `rollcalls`.</td></tr><tr><td>status</td><td>integer</td><td>بله</td><td>نوع وضعیت جدید: (۱،۲،۳).</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>از JWT گرفته می‌شود برای ثبت شناسه و کنترل دسترسی.</td></tr></tbody></table>

</div>```
POST /api/v2/personnel/traffic/update
{
  "id": 84,
  "status": 3,
  "operator": { "id": 19 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732037500
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت و کنترل دسترسی

<div id="bkmrk-%D8%A7%D8%B2-jwt-%D8%A7%D9%BE%D8%B1%D8%A7%D8%AA%D9%88%D8%B1-%D8%A8%D8%B1%D8%A7%DB%8C-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- از JWT اپراتور برای احراز هویت و تعیین شعبه استفاده می‌شود.
- دسترسی صرفاً در محدوده شعبه کاربر معتبر است.
- عدم وجود رکورد یا تلاش برای ویرایش رکورد خارجی با پاسخ `400 Bad Request` مدیریت می‌شود.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-exc" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Exception

</div>### کارایی

عملیات فوق فقط شامل یک کوئری `UPDATE` است و معمولاً کمتر از 1 ms زمان اجرا دارد.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

در صورت بروز استثنا، خطا با پیام `$e->getMessage()` به‌صورت JSON بازگردانده شده و کد HTTP=400 تنظیم می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

ویرایش مستقیم رکورد تردد در جدول `rollcalls`، بدون ایجاد لاگ سیستم.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

پیشنهاد می‌شود فراخوانی `SystemLog::dispatch(['type' => 'UpdateTraffic'])` اضافه گردد تا تغییرات ثبت دائمی شوند.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-sta" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- اعتبارسنجی مقدار `status` قبل از آپدیت برای جلوگیری از حالت‌های نامعتبر.
- افزودن فیلد `reason` در آینده برای ثبت دلیل رد یا تأیید تردد.

</div>### جمع‌بندی

روت ویرایش تردد نقطه مرکزی کنترل روزانه در سیستم Attendance است. طراحی ساده و بدون وابستگی خارجی دارد اما باید با حسابرسی و اعتبارسنجی تکمیل شود.

<div id="bkmrk-update-traffic-personnel" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# DELETE /api/v2/personnel/traffic/delete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">DELETE</td><td style="direction: ltr;">/api/v2/personnel/traffic/delete</td><td style="direction: ltr;">UserController@deleteTrafficPersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف رکورد تردد از جدول `rollcalls`.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A7%D8%B2-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- شناسه رکورد از درخواست گرفته می‌شود (`$request->get('id')`).
- حذف مستقیم رکورد با دستور `DB::table('rollcalls')->where('id', id)->delete()` انجام می‌شود.
- در صورت حذف موفق، پاسخ با `status=true` و timestamp ارسال می‌گردد.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه رکورد در جدول `rollcalls` برای حذف.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>از JWT خوانده شده برای ثبت عملیات یا تأیید مجاز بودن کاربر.</td></tr></tbody></table>

</div>```
DELETE /api/v2/personnel/traffic/delete
{
  "id": 84
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732037700
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- احراز JWT الزامی است.
- دسترسی محدود به اپراتورهای همان شعبه.
- پیشنهاد می‌شود کنترل مالکیت رکورد قبل از حذف اضافه گردد.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-exc" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Exception

</div>### کارایی

به‌طور میانگین کمتر از 1 ms برای حذف رکورد اجرا می‌شود.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

در صورت بروز استثنا، پاسخ با `status=false` و متن خطا ارسال شده و کد HTTP=400 تنظیم می‌گردد.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

رکورد حذف‌شده قابل بازیابی نیست مگر با لاگ‌های جداگانه یا نسخه‌سازی دیتابیس.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

پیشنهاد جدی: اضافه کردن `SystemLog::dispatch(['type'=>'DeleteTraffic'])` برای ثبت حذف و ردیابی تاریخی.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-delete%28%29-%D8%A8%D8%B1%D8%B1%D8%B3" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- قبل از `delete()` بررسی مالکیت رکورد توسط شعبه.
- پشتیبانی از Soft Delete در نسخه بعد برای قابلیت بازیابی.

</div>### جمع‌بندی

روت حذف تردد آخرین مرحله از چرخه حضور و غیاب است. ساده اما حساس — باید با Audit کامل و اعتبارسنجی مالکیت داده همراه شود.

<div id="bkmrk-delete-traffic-personnel" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/personnel/traffic/license/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/personnel/traffic/license/store</td><td style="direction: ltr;">UserController@storeTrafficLicensePersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ثبت مرخصی جدید با انواع مجوزهای زمانی (تمام‌روز، ساعتی).</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%AA%D8%B9%DB%8C%DB%8C%D9%86-%D9%86%D9%88%D8%B9-%D8%A7%D9%BE%D8%B1%D8%A7" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- ابتدا تعیین نوع اپراتور انجام می‌شود: 
    - اگر پارامتر `operator_passive` وجود داشته باشد، مرخصی برای آن اپراتور ثبت می‌شود.
    - در غیر این صورت، درخواست برای خود اپراتور صادرکننده ثبت می‌شود.
- سیستم بررسی می‌کند که آیا دپارتمان فعلی کاربر نیاز به تأیید دارد. اگر ندارد، وضعیت مرخصی مستقیماً برابر `status=3` (تأیید‌شده) تنظیم می‌شود.
- داده‌ها در جدول `rollcall_licenses` درج می‌شوند.
- در صورت موفقیت پاسخ `{"status":true,"time":timestamp}` بازمی‌گردد.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>اپراتور جاری از JWT.</td></tr><tr><td>operator\_passive</td><td>integer</td><td>خیر</td><td>شناسه اپراتوری که مرخصی برای او صادر می‌شود (در صورت عملکرد مدیریتی).</td></tr><tr><td>licenses\_type</td><td>integer</td><td>بله</td><td>نوع مرخصی (مثلاً ساعتی، روزانه).</td></tr><tr><td>time\_type</td><td>integer</td><td>بله</td><td>۱=روزانه ؛ ۲=ساعتی.</td></tr><tr><td>from</td><td>string</td><td>بله</td><td>تاریخ شروع مرخصی.</td></tr><tr><td>to</td><td>string</td><td>خیر</td><td>تاریخ پایان (در صورت مرخصی چندروزه).</td></tr><tr><td>from\_time</td><td>string</td><td>خیر</td><td>ساعت شروع (در مرخصی ساعتی).</td></tr><tr><td>to\_time</td><td>string</td><td>خیر</td><td>ساعت پایان (در مرخصی ساعتی).</td></tr><tr><td>details</td><td>string</td><td>خیر</td><td>توضیحات مرخصی.</td></tr><tr><td>substitute</td><td>integer</td><td>خیر</td><td>شناسه جایگزین‌کننده هنگام غیبت.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732038000
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- JWT برای احراز هویت ضروری است.
- مدیرها می‌توانند مرخصی برای سایر اپراتورها ثبت کنند.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-fun" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Functions::checkDatetime
- office\_departments
- operators

</div>### کارایی

ثبت رکورد در کمتر از 2ms — بسیار سریع و بدون پردازش‌های هم‌زمان.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

استثناها در قالب JSON با کد HTTP=400 بازگردانده می‌شوند.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

در صورت وجود دپارتمان با تأیید خودکار، وضعیت مرخصی به‌صورت پیش‌فرض تأیید می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

در این نسخه لاگ مستقیم ندارد؛ پیشنهاد ثبت در جدول `system_logs` با type=StoreLicense.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D9%86%D9%88%D8%AA%DB%8C%D9%81%DB%8C%DA%A9" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن ارسال نوتیفیکیشن برای جایگزین‌کننده در لحظه ثبت.
- بررسی تداخل مرخصی با بازه‌های مرخصی قبلی.

</div>### جمع‌بندی

روت ثبت مرخصی پایه کل چرخه مدیریتی تردد است؛ داده کامل را وارد `rollcall_licenses` می‌کند و وضعیت اولیه را بر اساس گروه سازمانی تعیین می‌کند.

<div id="bkmrk-store-traffic-license-personnel" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/personnel/traffic/license/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/api/v2/personnel/traffic/license/update</td><td style="direction: ltr;">UserController@updateTrafficLicensePersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">ویرایش یا انجام عملیات (جایگزینی/تأیید) روی رکورد مرخصی.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B1%D9%81%D8%AA%D8%A7%D8%B1-%D8%AA%D8%A7%D8%A8%D8%B9-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%D9%87-%D8%A8%D9%87" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- رفتار تابع وابسته به پارامتر `action` است: 
    - **action='update'** → ویرایش داده‌های مرخصی.
    - **action='substitute'** → جایگزینی اپراتور با `status` جدید.
    - **action='confirm'** → تأیید اولیه توسط مدیر.
    - **action='final\_approval'** → تأیید نهایی توسط ادمین یا دفتر منابع انسانی.
- پس از تعیین عملیات، رکورد در `rollcall_licenses` آپدیت می‌شود.
- در پاسخ، وضعیت موفقیت و timestamp بازگردانده می‌شود.

</div>### پارامترهای ورودی کلیدی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه رکورد مرخصی.</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>نوع عملیات (update, substitute, confirm, final\_approval).</td></tr><tr><td>details</td><td>string/integer</td><td>خیر</td><td>توضیح یا وضعیت جایگزینی/تأیید.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>از JWT برای ثبت شناسه تأییدکننده استفاده می‌شود.</td></tr></tbody></table>

</div>```
POST /api/v2/personnel/traffic/license/update
{
  "id": 40,
  "action": "confirm",
  "details": 3,
  "operator": { "id": 12 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{ "status": true, "time": 1732038200 }
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA.-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- JWT لازم است.
- دسترسی اپراتور برای تأییدها باید از دپارتمان واگذار شده خوانده شود.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-fun" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Functions::checkDatetime
- Exception

</div>### کارایی

بدنه تابع فقط شامل یک کوئری UPDATE است و در بازه 1–2 ms اجرا می‌شود.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

استثناها در قالب JSON با کد HTTP=400 بازگردانده می‌شوند.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

تأییدها می‌توانند زنجیره‌ای باشند؛ هر مرحله مهر زمانی و آیدی تأییدکننده را ثبت می‌کند.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

پیشنهاد می‌شود لاگ جداگانه برای عملیات تأیید ثبت شود با type=`LicenseApproval`.

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%AC%D8%AF%D8%A7-%DA%A9%D8%B1%D8%AF%D9%86-%D9%85%D8%B3%DB%8C%D8%B1%D9%87%D8%A7%DB%8C-%D8%AA%D8%A3%DB%8C" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- جدا کردن مسیرهای تأیید از ویرایش (برای کنترل مجوزها).
- افزودن تأیید چندمرحله‌ای و نوتیفیکیشن خودکار برای اپراتور جایگزین.

</div>### جمع‌بندی

این روت ستون فقرات چرخه تأیید مرخصی است؛ چند حالت عملیاتی (ویرایش، تأیید اولیه، نهایی، جایگزینی) در یک نقطه متمرکز شده‌اند.

<div id="bkmrk-update-traffic-license-personnel" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# DELETE /api/v2/personnel/traffic/license/delete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">DELETE</td><td style="direction: ltr;">/api/v2/personnel/traffic/license/delete</td><td style="direction: ltr;">UserController@deleteTrafficLicensePersonnel</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف رکورد مرخصی از سیستم تردد.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A7%D8%B2-%24requ" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- شناسه رکورد از `$request->get('id')` گرفته می‌شود.
- با دستور `DB::table('rollcall_licenses')->where('id', id)->delete()` حذف انجام می‌شود.
- در پایان پاسخ ساده با وضعیت موفق برگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه رکورد مرخصی برای حذف.</td></tr></tbody></table>

</div>```
DELETE /api/v2/personnel/traffic/license/delete
{
  "id": 40
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### نمونه خروجی موفق

```
{ "status": true, "time": 1732038300 }
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D9%BE%DB%8C%D8%B4%D9%86" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- پیشنهاد اعتبارسنجی مالکیت رکورد قبل از حذف برای جلوگیری از سوءاستفاده.

</div>### Dependencies

<div id="bkmrk-db-facade-carbon-exc" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Exception

</div>### کارایی

اجرای سریع کوئری در حدود 1 ms — بدون عملیات جانبی.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

در قالب JSON با وضعیت `false` و پیام خطا بازگردانده می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

حذف کامل رکورد بدون قابلیت بازیابی؛ در محیط عملیاتی پیشنهاد استفاده از Soft Delete.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

برای حفظ تاریخچه، نیازمند ثبت لاگ خودکار در `SystemLog` (type=DeleteLicense).

<div id="bkmrk--6" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-%D8%AD%D8%B0%D9%81-%D9%81%D9%82" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- اعمال محدودیت حذف فقط برای رکوردهای وضعیت «در انتظار».
- استفاده از Soft Delete جهت ریکاوری مرخصی‌های حذف‌شده.

</div>### جمع‌بندی

آخرین گام از چرخه مرخصی است. حذف رکورد انجام می‌شود بدون اثرات پی‌درپی یا تاییدات زنجیره‌ای؛ باید با حسابرسی همراه شود.

<div id="bkmrk-delete-traffic-license-personnel" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/passenger/profile

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/api/v2/passenger/profile</td><td style="direction: ltr;">UserController@profilePassenger</td><td style="direction: ltr;">authWithJwt</td><td style="direction: rtl; text-align: right;">بازیابی پروفایل کامل مسافر شامل اطلاعات هویتی، مدارک، ملیت، تاریخ تولد، و سوابق مالی.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-custom" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- جستجو در جدول `customers` بر اساس شناسه ورودی `id`.
- در صورت عدم وجود، پاسخ با کد ۴۰۴ و پیام "مسافری با این مشخصات یافت نشد" برمی‌گردد.
- در صورت وجود، تلاش برای واکشی ملیت از Redis (کلید `countries:{id}`).
- اگر کشور در Redis نبود، از DB خوانده و کش می‌شود.
- واکشی فاکتورهای مالی مرتبط از `factor_items + factors` با شاخه فعلی، گروه‌بندی‌شده بر اساس `factor_id`.
- در نهایت خروجی شامل بخش‌های `references`، `details`، `contacts` و `correspondence` بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 95%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>بله</td><td>شناسه مسافر در جدول customers.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه موردنظر برای فیلترینگ فاکتورها.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
  "payload": {
    "references": [
      {"title": "بلیط استانبول", "financial": {...}}
    ],
    "contacts": false,
    "details": {
      "passenger_id": 44,
      "name_fa": "علیرضا",
      "lastname_fa": "ایرانپور",
      "national_code": "0012345678",
      "birthday": "1386/03/21",
      "citizenship": { "fa_name": "ایران", "iso": "IR" },
      "sex": true,
      "national_image": false,
      "identity": false
    }
  },
  "meta": { "timestamp": 1732038300 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

احراز هویت با JWT. دسترسی فقط برای اپراتورهای شعبه فعال مجاز است.

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### وابستگی‌ها

<div id="bkmrk-db-facade-redis-carb" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Redis
- Carbon
- TradeController::financial()
- Functions::int2DateTime()

</div>### کارایی

کش Redis برای کشورها سرعت پاسخ را تا ۸۰٪ افزایش می‌دهد؛ متوسط زمان پاسخ: 3–5ms.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

<div class="align-right" id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%AD%D8%B0%D9%81-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%D8%8C-%D8%A8" style="font-family: Vazir; line-height: 1.9;">- در صورت حذف رکورد، بازگرداندن JSON با کد 404 و پیام مناسب.

</div>### اثرات جانبی

هیچ دیتایی تغییر نمی‌کند؛ فقط خوانش ایمن انجام می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

در این نسخه لاگ مستقیم ندارد؛ اکشن فقط خواندن است.

<div id="bkmrk--5" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AC%D8%B2%D8%A6%DB%8C%D8%A7%D8%AA-%D8%AA%D9%85%D8%A7%D8%B3-%D8%A7" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن جزئیات تماس اخیر از جدول `call_logs`.
- نمایش وضعیت تأیید احراز هویت در خروجی.

</div>### جمع‌بندی

پروفایل مسافر ماژول مرجع برای نمایش داده‌های مسافر است؛ شامل جزئیات هویتی، ملیتی، مدارک، و سوابق مالی است.

<div id="bkmrk-passenger-profile" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/passenger/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td>/api/v2/passenger/store</td><td>UserController@storePassenger</td><td>authWithJwt</td><td>ایجاد یا بروزرسانی رکوردهای مشتری (مسافر) بر اساس ملیت و مدارک.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D8%B1-%D8%A2%DB%8C%D8%AA%D9%85-%D8%AF%D8%B1-requ" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- برای هر آیتم در `request->data` بررسی می‌شود: 
    - اگر ملیت = ایران و کد ملی موجود → جستجو بر اساس `national_code`.
    - اگر ملیت ≠ ایران → جستجو بر اساس `passport_code`.
- اگر رکورد موجود نباشد → درج جدید در `customers` و برگرداندن شناسه.
- اگر موجود باشد → در صورت فعال بودن احراز هویت، فیلدهای معتبر (دارای rank &gt;=90) حذف و اطلاعات دیگر بروز می‌شوند.
- در پایان لاگ `SystemLog(type=StorePassenger)` در صف تأخیر ثبت می‌شود.

</div>### پارامترهای ورودی کلیدی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه.</td></tr><tr><td>data\[\]</td><td>array</td><td>بله</td><td>آرایه‌ای از مسافران جهت درج.</td></tr><tr><td>data\[\].citizenship.id</td><td>integer</td><td>بله</td><td>شناسه کشور.</td></tr><tr><td>data\[\].national\_code</td><td>string</td><td>خیر</td><td>کد ملی (در صورت ایرانی بودن).</td></tr><tr><td>data\[\].pass\_code</td><td>string</td><td>خیر</td><td>کد پاسپورت (در صورت خارجی بودن).</td></tr><tr><td>data\[\].birthday</td><td>string</td><td>بله</td><td>تاریخ تولد (YYYY-MM-DD).</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>از JWT برای ثبت مسئول.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732038300,
  "data": [
     {"passenger_id": 45, "name_fa": "علیرضا", "lastname_fa": "ایرانپور"}
  ]
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

JWT اجباری؛ هر اپراتور فقط به شعبه خود اجازه درج دارد.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>### وابستگی‌ها

<div id="bkmrk-db-carbon-functions%3A" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB
- Carbon
- Functions::checkDatetime()
- Validator::datetime()
- SystemLog

</div>### کارایی

درج مستقیم بدون تراکنش پیچیده، متوسط 2–3ms برای هر مسافر.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

<div class="align-right" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AA%DA%A9%D8%B1%D8%A7%D8%B1%DB%8C-%E2%86%92-%D9%BE" style="font-family: Vazir; line-height: 1.9;">- اگر رکورد تکراری → پاسخ با `status=false` و پیام خطا.
- اگر احراز هویت معتبر فعال باشد → جلوگیری از ویرایش فیلدهای حساس.

</div>### اثرات جانبی

در صورت وجود کش هویت، برخی فیلدها حذف از بروزرسانی خواهند شد.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

ثبت log با نوع `StorePassenger` در صف snailJob با تأخیر ۱۰ دقیقه.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### پیشنهاد بهبود

<div class="align-right" id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-check-%D8%A8%D8%B1%D8%A7" style="font-family: Vazir; line-height: 1.9;">- اضافه کردن check برای تداخل کد ملی و پاسپورت در شعب مختلف.
- تبدیل عملیات دسته‌ای به تراکنش DB برای اتمیک بودن.

</div>### جمع‌بندی

این متد پایه‌ی درج و بروزرسانی اولیه داده‌های مسافران در سیستم فروش و رزرو است و احراز هویت را به طور هوشمند کنترل می‌کند.

<div id="bkmrk-passenger-store" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/passenger/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td>/api/v2/passenger/update</td><td>UserController@updatePassenger</td><td>authWithJwt</td><td>ویرایش داده‌های هویتی موجود در جدول customers برای یک مسافر مشخص.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%82%D8%A8%D9%84%DB%8C-cus" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- واکشی رکورد قبلی `customers` بر اساس `passenger_id`.
- آماده‌سازی آرایه بروزرسانی و بررسی وجود اعتبار هویت (`identity_check`).
- در صورت فعال بودن احراز هویت، حذف فیلدهای حساس با سطح اعتماد ≥90.
- ثبت در DB و محاسبه تغییرات با `Functions::arrayDiff()`.
- در پایان، تولید لاگ type=`UpdatePassenger`.

</div>### پارامترهای ورودی کلیدی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>passenger\_id</td><td>integer</td><td>بله</td><td>شناسه مسافر هدف.</td></tr><tr><td>name\_fa, lastname\_fa</td><td>string</td><td>خیر</td><td>نام فارسی مسافر.</td></tr><tr><td>citizenship.id</td><td>integer</td><td>بله</td><td>شناسه کشور تابعیت.</td></tr><tr><td>birthday</td><td>string</td><td>بله</td><td>تاریخ تولد.</td></tr><tr><td>phone\_number</td><td>string</td><td>بله</td><td>شماره موبایل.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>اپراتور JWT.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{ "status": true, "time": 1732038210 }
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

ورود نیازمند JWT و مطابقت شعبه. حذف پویا فیلدهای هویتی ایمن در زمان احراز هویت فعال.

<div id="bkmrk-db-facade-carbon-fun" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Functions::arrayDiff()
- SystemLog

</div>عملیات تک‌جدولی کوچک؛ اجرا در ~2ms.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>در خطاها، پاسخ JSON شامل status=false و پیام عمومی.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>بروزرسانی مستقیم داده‌ها، بدون cascade به جداول رزرو یا مالی.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>ثبت کامل لاگ نوع UpdatePassenger در صف snailJob با تأخیر ۱۰ دقیقه.

<div class="align-right" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%B4%D9%85" style="font-family: Vazir; line-height: 1.9;">- افزودن اعتبارسنجی شماره پاسپورت و فرمت موبایل.
- بهینه‌سازی حذف فیلدها با mapping سطوح اطمینان پویاتر.

</div>این Endpoint ویرایش رسمی اطلاعات مسافر را انجام می‌دهد و به صورت امن، فیلدهای دارای احراز هویت معتبر را حفظ می‌کند.

<div id="bkmrk-passenger-update" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/passenger/delete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/passenger/delete</td><td style="direction: ltr;">UserController@deletePassenger</td><td style="direction: ltr;">authWithJwt</td><td style="text-align: right;">تغییر وضعیت رکورد مسافر به حالت حذف‌شده (`status=5`) در جدول `customers`.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D9%85%D8%B3%D8%A7%D9%81%D8%B1-%D8%A7" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- دریافت شناسه مسافر از `request['passenger_id']`.
- اجرای دستور `update status=5` در جدول `customers` برای حذف نرم.
- ارسال لاگ سیستم با نوع `TrashPassenger` در صف `snailJob` با تأخیر ۱۰ دقیقه.
- بازگرداندن پاسخ موفق با زمان اجرا (`time()`).

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>passenger\_id</td><td>integer</td><td>بله</td><td>شناسه مسافر حذف‌شونده.</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>کاربر اجراکننده عملیات که از JWT گرفته می‌شود.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
  "status": true,
  "time": 1732038400
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

درخواست تحت Middleware `authWithJwt`؛ اپراتور باید مجاز به حذف در همان شعبه باشد.

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### وابستگی‌ها

<div id="bkmrk-db-%28facade%29-systemlo" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB (Facade)
- SystemLog Job Queue
- Carbon
- getIP()

</div>### کارایی

عملیات تک‌جدولی سریع؛ متوسط زمان پاسخ 2ms.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>### مدیریت خطا

در صورت خطا، کد 5005 به همراه پیام Exception بازگردانده می‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>### اثرات جانبی

هیچ داده‌ای حذف فیزیکی نمی‌شود؛ فقط وضعیت به ۵ تغییر می‌کند.

<div class="align-right" id="bkmrk--5" style="font-family: Vazir; line-height: 1.9;"></div>### ردپای حسابرسی

ثبت لاگ `TrashPassenger` شامل شناسه مسافر و اپراتور در صف حسابرسی تاخیری.

<div class="align-right" id="bkmrk--6" style="font-family: Vazir; line-height: 1.9;"></div>### پیشنهاد بهبود

<div id="bkmrk-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-query-%D8%A7%D8%B2-updat" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- تغییر Query از update به SoftDelete واقعی با استفاده از مدل Eloquent.
- بررسی Dependencies فیزیکی مسافر پیش از حذف (مثلاً رزرو فعال).

</div>### جمع‌بندی

این Endpoint حذف امن و سریع مسافر را پیاده‌سازی می‌کند بدون از بین بردن داده‌ها، و با ثبت ردپای حسابرسی کامل.

<div id="bkmrk-passenger-delete" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/auth/by

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/auth/by</td><td>UserController@authBy</td><td>authWithJwt</td><td style="text-align: right;">ورود اپراتور از طریق شناسه پرسنلی و تولید توکن JWT معتبر برای شعبه فعلی.</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%AF%D8%A7%D8%AF%D9%87-%D9%BE%D8%B1%D8%B3%D9%86%D9%84%DB%8C-%D8%A7" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- دریافت داده پرسنلی از `request->data`.
- واکشی `User` از DB بر اساس `personnel_id` و شعبه فعلی، با شرط عدم‌مسدود بودن حساب (`blocked_up <= now()` یا null).
- Parse کامل User-Agent با `DeviceDetector` و تولید payload برای JWT.
- تولید توکن با الگوریتم `HS256` و کلید `JWT_SECRET_KEY`.
- در صورت فعال بودن کاربر (`status==1`)، ترکیب داده کامل پروفایل اپراتور و بازگرداندن `access_token`.
- در صورت غیرفعال بودن یا نبود کاربر، بازگرداندن ساختار خطا با پیام مناسب فارسی.
- ثبت لاگ `LoginAuthBy` در صف تأخیری.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>data.personnelId</td><td>integer</td><td>بله</td><td>کد پرسنلی اپراتور.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه برای بررسی دسترسی.</td></tr><tr><td>Domain header</td><td>string</td><td>بله</td><td>دامنه مبدا برای ایجاد توکن JWT.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
 "user": {
   "uuid": 12,
   "from": "users",
   "role": "admin",
   "isAirPlusAdmin": true,
   "data": { "displayName": "علیرضا ایرانپور", "email": "alireza@example.com", "branch": [0,14] },
   "shortcuts": ["temporary-registration","trade-management"]
 },
 "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

مبتنی بر JWT با طول عمر ۷ روز (`exp=+604800`). هر اپراتور فقط برای شعبه مجاز خود توکن دریافت می‌کند.

<div id="bkmrk-carbon-devicedetecto" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- Carbon
- DeviceDetector &amp; ClientHints
- JWT Facade
- Redis
- SystemLog

</div>میانگین زمان پاسخ: 25ms (شامل پردازش User-Agent).

<div class="align-right" id="bkmrk-%D8%AE%D8%B7%D8%A7-%D8%AF%D8%B1-%D9%86%D8%A7%D9%87%D9%85%D8%A7%D9%87%D9%86%DA%AF%DB%8C-%D8%B4%D9%86%D8%A7" style="font-family: Vazir; line-height: 1.9;">- خطا در ناهماهنگی شناسه پرسنلی → پیام فارسی "اطلاعاتی کاربری برای این دفتر همخوانی ندارد".

</div>تغییر در وضعیت session اپراتور؛ ایجاد توکن جدید و ذخیره کش میانبرها.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>ثبت لاگ نوع `LoginAuthBy` همراه اطلاعات شعبه و IP اپراتور در صف `snailJob`.

<div class="align-right" id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-expiratio" style="font-family: Vazir; line-height: 1.9;">- اضافه کردن expiration پویا بر اساس نوع دسترسی (کاربر اداری، مدیر).
- افزودن تخمین GeoIP در payload برای امنیت بیشتر.

</div>این Endpoint احراز هویت استاندارد JWT را برای سیستم شعبات پیاده‌سازی می‌کند و ساختار امنیتی Session اپراتور را پوشش می‌دهد.

<div id="bkmrk-auth-by" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/auth/connect/disconnect

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/auth/connect/disconnect</td><td>UserController@connectDisconnect</td><td>authWithJwt</td><td style="text-align: right;">قطع ارتباط حساب پیام‌رسان تلگرام با پروفایل اپراتور در جدول `operators`.</td></tr></tbody></table>

- واکشی شناسه اپراتور از JWT و حذف مقدار فیلد `telegram` در رکورد مربوطه.
- ثبت پاسخ JSON موفق با پیام فارسی.
- در صورت Exception، بازگرداندن پاسخ خطا با جزئیات فنی.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>operator.id</td><td>integer</td><td>بله</td><td>شناسه اپراتور برای قطع ارتباط.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
 "status": true,
 "time": 1732038420,
 "message": "قطع ارتباط با موفقیت انجام شد."
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>JWT معتبر الزامی است. فقط اپراتور همان حساب اجازه تغییر دارد.

<div id="bkmrk-db-carbon" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB
- Carbon

</div>زمان میانگین پاسخ: 2ms؛ بدون لاگ سنگین یا پردازش اضافی.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>ورود Exception باعث بازگشت `status=false` و پیام خطا از نوع رشته‌ای می‌شود.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>قطع ارتباط پایدار؛ مانع ارسال پیام‌های تلگرامی سیستم به اپراتور تا اتصال مجدد.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>در نسخه فعلی لاگ جداگانه ثبت نمی‌شود، اما می‌تواند در آینده در صف Audit افزوده شود.

<div class="align-right" id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%A7%D9%85%D9%86%DB%8C%D8%AA%DB%8C-%D8%AC%D8%AF%D8%A7%DA%AF%D8%A7" style="font-family: Vazir; line-height: 1.9;">- ثبت لاگ امنیتی جداگانه برای Disconnect در `SystemLog`.
- افزودن endpoint مجزا برای اتصال مجدد با Token.

</div>این مسیر برای حذف سریع ارتباط تلگرام طراحی شده و عملکرد ساده اما حیاتی در امنیت کانال‌های ارتباطی اپراتور دارد.

<div id="bkmrk-connect-disconnect" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/operator/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f1f1f1; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/operator/store</td><td style="direction: ltr;">UserController@storeOperator</td><td style="direction: ltr;">authWithJwt</td><td style="text-align: right;">ثبت اپراتور جدید همراه با اطلاعات فردی، شغلی و دسترسی‌ها</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AC%D8%AF%DB%8C%D8%AF-%D8%AF%D8%B1-%D8%AC%D8%AF" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- ثبت رکورد جدید در جدول `operators` با داده‌های ارسالی از فرم یا پنل.
- رمز عبور با استفاده از `Hash::make()` رمزگذاری می‌شود.
- در صورتی که `attachment` وجود داشته باشد، هر آیتم از آن در جدول `operators_attachment` درج می‌شود.
- رکورد ذخیره‌شده مجدداً از DB واکشی و در پاسخ بازگردانده می‌شود.

</div>### پارامترهای الزامی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>integer</td><td>بله</td><td>کد پرسنلی اپراتور</td></tr><tr><td>branch</td><td>json/int</td><td>بله</td><td>شناسه یا آرایه شعب متصل</td></tr><tr><td>type</td><td>integer</td><td>خیر</td><td>نوع کاربر (ادمین، فروش، مالی...)</td></tr><tr><td>first\_name, last\_name</td><td>string</td><td>بله</td><td>نام و نام خانوادگی فارسی</td></tr><tr><td>password</td><td>string</td><td>بله</td><td>رمز عبور اپراتور، هش می‌شود</td></tr><tr><td>attachment\[\]</td><td>array</td><td>خیر</td><td>آرایه فایل‌های پیوست مربوط به اپراتور</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
 "status": true,
 "time": 1732046200,
 "data": {
   "id": 85,
   "personnel_id": 10034,
   "first_name": "علیرضا",
   "last_name": "ایرانپور",
   "branch": "[0]",
   "access": "{\"panel\":[\"customers\",\"dashboard\"]}",
   "telegram": null
 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### امنیت

به‌کمک `authWithJwt` فقط اپراتورهای دارای دسترسی مدیریتی می‌توانند عملیات ایجاد انجام دهند.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; text-align: justify; line-height: 1.9;"></div>### وابستگی‌ها

<div id="bkmrk-db-facade-carbon-has" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- DB Facade
- Carbon
- Hash Facade

</div>### کارایی

درج مستقیم بدون Transaction، میانگین زمان پاسخ: 4ms.

<div class="align-right" id="bkmrk--3" style="direction: rtl; font-family: Vazir; line-height: 1.9;"></div>### خطا

<div class="align-right" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%AF%D8%A7%D8%AF%D9%87-%DA%A9%D9%84%DB%8C%D8%AF%DB%8C-%D9%88%D8%AC%D9%88%D8%AF-" style="direction: rtl; font-family: Vazir; line-height: 1.9;">- اگر داده کلیدی وجود نداشته باشد، پاسخ 422 از Validator بالادستی بازمی‌گردد.

</div>درج همزمان سوابق پیوست در جدول مجزا.

<div class="align-right" id="bkmrk--4" style="direction: rtl; font-family: Vazir; line-height: 1.9;"></div>در نسخه فعلی لاگی برای `SystemLog` ثبت نمی‌شود ولی زیرساخت آن آماده است.

<div class="align-right" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-db-%D8%A8%D8%B1%D8%A7" style="direction: rtl; font-family: Vazir; line-height: 1.9;">- افزودن تراکنش DB برای atomic بودن Insertها.
- ایجاد هش slug داخلی جهت یکتایی اپراتور.

</div>این مسیر عملیات درج اپراتور جدید را با درج فوری پیوست‌ها به شکل ساده اما پایدار انجام می‌دهد.

<div id="bkmrk-operator-store" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/operator/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f2f2f2; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/operator/update</td><td style="direction: ltr;">UserController@updateOperator</td><td style="direction: ltr;">authWithJwt</td><td style="text-align: right;">ویرایش اطلاعات اپراتور و بروزرسانی فایل‌های پیوست</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%81%DB%8C%D9%84%D8%AF-id-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B4" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- بررسی فیلد `id` برای شناسه اپراتور هدف.
- بروزرسانی تمام ستون‌های شخصی و سیستمی (از جمله `password` هش‌شده).
- اگر آرایه `attachment` وجود داشته باشد: 
    - در صورت وجود `id` در آیتم → ویرایش رکورد پیوست.
    - در غیر این صورت → درج رکورد جدید.

</div>### پارامترهای کلیدی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>شناسه اپراتور مورد نظر</td></tr><tr><td>password</td><td>string</td><td>رمز عبور جدید (در صورت ارسال، دوباره هش می‌شود)</td></tr><tr><td>attachment\[\]</td><td>array</td><td>آرایه فایل‌های پیوست جدید یا ویرایش‌شده</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{ "status": true, "time": 1732046302 }
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>دسترسی فقط برای اپراتورهای سطح مدیر؛ از JWT برای شناسایی کاربر استفاده می‌شود.

<div class="align-right" id="bkmrk-db-carbon-hash" style="font-family: Vazir; line-height: 1.9;">- DB
- Carbon
- Hash

</div>میانگین زمان بروزرسانی: 4–5ms برای اپراتور به‌علاوه زمان ثبت ضمیمه‌ها.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>در صورت نبود رکورد هدف،‌ خروجی بدون خطای منطقی ولی صرفاً بدون تغییر داده بازمی‌گردد (پیشنهاد: افزودن بررسی Count).

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>در صورت ارسال ضمیمه جدید، رکوردهای قبلی بدون delete باقی می‌مانند مگر توسط ویرایش بعدی حذف شوند.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>در حال حاضر فاقد log است؛ پیشنهاد: افزوده‌شدن `UpdateOperator` به صف `snailJob`.

<div class="align-right" id="bkmrk-%D8%AC%D8%AF%D8%A7%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D8%B1%D9%85%D8%B2-%D8%B9%D8%A8" style="font-family: Vazir; line-height: 1.9;">- جداسازی تغییر رمز عبور به endpoint مستقل.
- پاکسازی ضمایم قدیمی هنگام حذف فیزیکی فایل.

</div>این مسیر مکانیزم استاندارد بروزرسانی اپراتورها را فراهم می‌کند و سازگار با فایل پیوست‌های چندگانه است.

<div id="bkmrk-operator-update" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/operator/get

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #ececec; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/operator/get</td><td style="direction: ltr;">UserController@getOperator</td><td style="direction: ltr;">authWithJwt</td><td style="text-align: right;">واکشی داده‌های اپراتور براساس کد پرسنلی یا شناسه DB</td></tr></tbody></table>

- اگر پارامتر `personnel_id` موجود و معتبر بود → واکشی همه اپراتورهای دارای آن کد.
- در غیر این صورت → جستجو بر اساس `id`.
- بازگرداندن نتیجه در قالب JSON.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>خیر</td><td>شناسه اپراتور خاص</td></tr><tr><td>personnel\_id</td><td>integer</td><td>خیر</td><td>کد پرسنلی اپراتور</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
 "status": true,
 "time": 1732046405,
 "data": {
   "id": 14,
   "personnel_id": 1001,
   "first_name": "علیرضا",
   "last_name": "ایرانپور",
   "email": "alireza@example.com",
   "branch": "[14]"
 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>احراز هویت JWT الزامی است. داده‌ها فقط برای شعبه‌های مجاز کاربر حاضر ارائه می‌شود.

<div class="align-right" id="bkmrk-db-facade" style="font-family: Vazir; line-height: 1.9;">- DB Facade

</div>پرس‌وجوی ساده با میانگین زمان 2ms.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>در صورت عدم‌وجود اپراتور، پاسخ خالی ولی `status=true` بازمی‌گردد (پیشنهاد: افزودن کنترل not-found).

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>فقط عملیات خواندن بدون تغییر داده.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>در حال حاضر لاگ نشده است.

<div class="align-right" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%84%DA%AF%D9%88%DB%8C-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%A8" style="font-family: Vazir; line-height: 1.9;">- افزودن الگوی جستجو بر اساس ایمیل یا شماره موبایل.
- برگرداندن پیوست‌ها به‌صورت ارتباط join.

</div>این مسیر برای مشاهده سریع اطلاعات اپراتور از طریق شناسه یا کد پرسنلی استفاده می‌شود و ساختار پاسخ JSON استاندارد دارد.

<div id="bkmrk-operator-get" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/online/reservation/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f1f1f1; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/reservation/list</td><td style="direction: ltr;">OnlineController@listOnlineItemsReservations</td><td style="direction: ltr;">authWithJwt</td><td>دریافت رزروهای آنلاین با قابلیت فیلتر و صفحه‌بندی</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84%E2%80%AFfactor" style="font-family: Vazir; line-height: 1.9;">- جستجو در جدول `factor_items` و اتصال (`LEFT JOIN`) به جدول `factors`.
- اعمال فیلترها بر مبنای: 
    - `type` → مقادیر `internal` (اپراتور≠1) یا `sale` (اپراتور=1)
    - `sub_type` → فیلتر `byproduct`
    - `reference` → مطابقت با شماره سریال (`serial – 10000`)
    - `operator` → شناسه اپراتور خاص
- فقط رکوردهای `factor_items.status = 1` بازگردانده می‌شوند.
- نتیجه با `paginate()` بر اساس فیلدهای `request('paginate')` صفحه‌بندی می‌گردد.
- هر آیتم با داده‌های زیر تکمیل می‌شود: 
    - عنوان فارسی از `StaticController::getRefItemTitle()`
    - اپراتور از `StaticController::getOperators()`
    - اطلاعات تأمین‌کننده از جدول `colleagues`

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه اپراتور</td></tr><tr><td>type</td><td>string</td><td>خیر</td><td>internal | sale</td></tr><tr><td>sub\_type</td><td>string</td><td>خیر</td><td>نوع محصول فرعی (مثلاً flight, train, hotel)</td></tr><tr><td>reference</td><td>integer</td><td>خیر</td><td>سریال فاکتور مرجع</td></tr><tr><td>operator</td><td>integer</td><td>خیر</td><td>شناسه اپراتور صادرکننده</td></tr><tr><td>paginate\[length,start\]</td><td>array</td><td>بله</td><td>مشخصات صفحه‌بندی (DataTables style)</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```
{
 "items":[
   {
     "title":{"fa":"پرواز تهران → استانبول"},
     "operator":{"id":17,"first_name":"علیرضا","last_name":"ایرانپور"},
     "id":293,
     "reference":3412,
     "reference_serial":133412,
     "sub_method":"aircraft",
     "supplier":"آژانس همکار پرواز",
     "buy":7200000,
     "sale":8100000,
     "created_at":"2025‑11‑20 10:45:23"
   }
 ],
 "meta":{
   "timestamp":1732049200,
   "table":{"total":1483,"per_page":10,"current_page":1,"last_page":149,"from":1,"to":10}
 }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>محافظت‌شده توسط `authWithJwt`. درخواست بدون توکن JWT دارای پاسخ 401.

<div class="align-right" id="bkmrk-db%E2%80%AFfacade-staticcont" style="font-family: Vazir; line-height: 1.9;">- DB Facade
- StaticController → توابع `getOperators` و `getRefItemTitle`

</div>میانگین زمان پاسخ: ٣ تا ۵ میلی‌ثانیه برای هر صفحه ۱۰ رکوردی در MySQL محلی.

<div class="align-right" id="bkmrk--2" style="font-family: Vazir; line-height: 1.9;"></div>در صورت بروز استثناء، کد خطا 1002 به همراه پیام و Trace در status 400 بازگردانده می‌شود.

<div class="align-right" id="bkmrk--3" style="font-family: Vazir; line-height: 1.9;"></div>صرفاً عملیات خواندن؛ تغییری در DB انجام نمی‌شود.

<div class="align-right" id="bkmrk--4" style="font-family: Vazir; line-height: 1.9;"></div>در نسخه فعلی هیچ SystemLog ثبت نمی‌شود.

<div class="align-right" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%DA%A9%D8%B4%E2%80%AFredis%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C" style="font-family: Vazir; line-height: 1.9;">- افزودن کش Redis برای لیست رزروهای محبوب.
- افزودن پارامتر تاریخ شروع/پایان جهت فیلتر تاریخی.
- Lazy‑load برای فیلدهای مشخص (مانند supplier name).

</div>این Endpoint هسته‌ی نمایش رزروهای آنلاین در پرتال مدیریتی است و ساختار جدولی استاندارد با خروجی Pageinated را بر می‌گرداند.

<div id="bkmrk-online-reservation-list" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/online/reservation/penalty

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/reservation/penalty</td><td style="direction: ltr;">OnlineController@getPenaltyOnlineItemsReservations</td><td style="direction: ltr;">authWithJwt</td><td>محاسبه یا دریافت جریمه لغو رزروهای آنلاین (در حال توسعه)</td></tr></tbody></table>

</div>### خروجی فعلی

```
{
 "payload":{"penalty":[]},
 "meta":{"timestamp":1732049300}
}
```

### پیشنهاد برای توسعه

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFreser" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پارامتر `reservation_id` برای محاسبه دقیق.
- اتصال به سیستم احراز قوانین استرداد چارتری/سیستمی.
- بازگرداندن فیلدهای `amount` و `percent` نسبت به مبلغ کل بلیط.

</div>فعلاً صرفاً به‌عنوان Placeholder عمل می‌کند و هیچ منطق اجرایی ندارد.

<div id="bkmrk-online-reservation-penalty" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/online/reservation/refund

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/reservation/refund</td><td style="direction: ltr;">OnlineController@refundOnlineItemsReservations</td><td style="direction: ltr;">authWithJwt</td><td>شروع درخواست استرداد وجه برای رزروهای آنلاین (در حال توسعه)</td></tr></tbody></table>

</div>### خروجی فعلی

```
{
 "payload":{"penalty":[]},
 "meta":{"timestamp":1732049400}
}
```

### نکات آتی توسعه

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7%DB%8C%E2%80%AF%D9%85%D9%88%D8%B1%D8%AF%E2%80%AF%D9%86%DB%8C%D8%A7%D8%B2%3A" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- ورودی‌های مورد نیاز: `reservation_id`، `reason`، `bank_account`
- مرحله‌بندی: بررسی وضعیت رزرو → محاسبه Penalty → ایجاد Request Refund → بروزرسانی Wallet شعبه.
- ارسال لاگ `SystemLog(type=RefundOnlineItem)` در صف `snailJob`.

</div>در حال حاضر فقط قالب پاسخ تایم‌استمپ و payload خالی دارد.

<div id="bkmrk-online-reservation-refund" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/online/credit

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f2f2f2; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/credit</td><td style="direction: ltr;">OnlineController@credit</td><td style="direction: ltr;">authWithJwt</td><td>دریافت مانده اعتبار آنلاین شعبه</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D9%85%D8%AA%D8%AF%E2%80%AF%D8%A7%D8%B3%D8%AA%D8%A7%D8%AA%DB%8C%DA%A9" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- فراخوانی متد استاتیک `OnlineController::getCredit(branch, action)` برای واکشی اعتبار.
- بازگرداندن پاسخ موفق با ساختار: ```
    {
     "status": true,
     "time": time(),
     "last_update": credit['last_update'],
     "data": credit['data']
    }
    ```

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه درخواست‌دهنده</td></tr><tr><td>action</td><td>string</td><td>خیر</td><td>نوع اقدام (پیش‌فرض "check") درصورت وجود</td></tr></tbody></table>

</div>### پاسخ نمونه

```
{
 "status": true,
 "time": 1732049800,
 "last_update": "2025‑11‑20 09:45:06",
 "data": {
   "credit_total": 242000000,
   "credit_used": 210000000,
   "credit_available": 32000000
 }
}
```

### نکات امنیتی

روت فقط برای کاربران احراز هویت‌شده (`authWithJwt`) فعال است و اطلاعات صرفاً مربوط به شعبه درخواست‌دهنده برگردانده می‌شود.

### وابستگی‌ها

به متد `getCredit()` در کلاس OnlineController نیاز دارد (احتمالاً منبع داده: جدول `credits_online` یا کِش Redis برای اعتبار به‌روز).

### بهبود ممکن

<div id="bkmrk-online-credit" style="font-family: Vazir; line-height: 1.9;">- افزودن پارامتر `currency` برای اجازه به نمایش ارز غیرریالی.
- افزودن کش Time‑based (`credit_cache_TTL=300s`).

</div>

# POST /api/v2/online/credit/update

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #ebebeb; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/credit/update</td><td style="direction: ltr;">OnlineController@updateCreditSign</td><td style="direction: ltr;">authWithJwt</td><td>بازنشانی و امضای مجدد اعتبار</td></tr></tbody></table>

</div>### منطق عملکرد

<div class="align-right" id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C%E2%80%AF%D8%AA%D8%A7%D8%A8%D8%B9%E2%80%AFcronco" style="font-family: Vazir; line-height: 1.9;">- فراخوانی تابع `CronController::signCredit()` بدون درخواست اضافی.
- اجرای پشت‌صحنه‌ای برای امضای اعتبار تمام شعب (یا شعبه‌ی فعلی).
- عدم بازگردانی پاسخ؛ در نسخه‌ی فعلی خروجی صریح ندارد و HTTP Status پیش‌فرض 200 ست می‌شود.

</div>### ورودی

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

### وابستگی‌ها

<div class="align-right" id="bkmrk-croncontroller%E2%80%AF%E2%86%92%E2%80%AF%D9%85%D8%AA%D8%AF" style="font-family: Vazir; line-height: 1.9;">- `CronController` → متد `signCredit()` (به احتمال زیاد داده‌های جدول `credits_online` را به‌روزرسانی و امضا می‌کند).
- ممکن است از Queue/Job برای همگام‌سازی اعتبارات استفاده کند.

</div>### نکات توسعه

<div id="bkmrk-online-credit-update" style="font-family: Vazir; line-height: 1.9;">- پیشنهاد: برگرداندن خروجی JSON موفق به فرم `{"status":true,"updated":count}`.
- قفل پروسه همزمان (Prevent Parallel Jobs) برای اجتناب از تداخلی در امضاها.

</div>

# POST /api/v2/online/reservation/penalty

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/reservation/penalty</td><td style="direction: ltr;">OnlineController@reservationPenalty</td><td style="direction: ltr;">authWithJwt</td><td>قفل موقت و محاسبه‌ی جریمه لغو رزرو آنلاین</td></tr></tbody></table>

</div>### منطق (در کد کامنت‌شده)

<div id="bkmrk-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF%E2%80%AF%D9%86%D9%85%D9%88%D9%86%D9%87%E2%80%AF%D8%A7%D8%B2%E2%80%AF%5Capp%5C" style="font-family: Vazir; line-height: 1.9;">1. ایجاد نمونه از `\App\Lib\BaseService()`.
2. فراخوانی `$BaseService‑>LockFlights($request‑>data)`.
3. در صورت موفقیت (`Status=true`): 
    - درج در جدول `temporary_reservations` با فیلدهای `data`، `operator`، `result`.
    - بازگرداندن `{"status":true,"data":LockId}`
4. در صورت شکست: 
    - بازگرداندن وضعیت false به همراه کد `1002` و پیام خطای دریافتی.
    - افزودن مراجع پشتیبانی (تلفن، ایمیل، Helpdesk‑Panel).

</div>### ورودی‌ها

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>پارامتر</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>data</td><td>object/array</td><td>بله</td><td>داده‌های رزرو پرواز/قطار جهت قفل کردن</td></tr></tbody></table>

</div>### پاسخ موفق نمونه

```
{
 "status": true,
 "time": 1732050050,
 "data": 9423
}
```

### پاسخ در صورت خطا

```
{
 "status": false,
 "time": 1732050050,
 "error": {"code":1002,"message":"Seat lock failed"},
 "support": {
   "phone": "021‑91016838 in 121",
   "email": "ict@airplus.app",
   "panel": "helpdesk.airplus.app"
 }
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-%5Capp%5Clib%5Cbaseservice" style="font-family: Vazir; line-height: 1.9;">- `\App\Lib\BaseService` → تابع `LockFlights()`
- DB Facade → جدول `temporary_reservations`

</div>### وضعیت فعلی

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

### پیشنهاد توسعه

<div id="bkmrk-online-reservation-penalty-post" style="font-family: Vazir; line-height: 1.9;">- انتقال منطق Lock به Queue برای جلوگیری از timeout.
- ثبت SystemLog با نوع `ReservationPenaltyCreate`.
- بازگرداندن TTL لاک (ِمثلاً ۳ دقیقه مجاز برای تأیید رزرو).

</div>

# Route: POST /api/v2/online/{type}/lock

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/{type}/lock</td><td style="direction: ltr;">OnlineController@lockItemProgress</td><td style="direction: ltr;">authWithJwt</td><td>قفل‌گذاری‌ موقت در سیستم موتور رزرو (Flight/Train)</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF%E2%80%AF%D9%86%D9%85%D9%88%D9%86%D9%87%E2%80%8C%D8%A7%DB%8C%E2%80%AF%D8%A7%D8%B2%E2%80%AF%5Ca" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">1. ایجاد نمونه‌ای از `\App\Lib\BaseService`
2. فراخوانی تابع `lockItemProgress($request‑>data, false, $branch, $operatorId)`
3. ثبت نتیجه در جدول `temporary_reservations` با ساختار: 
    - `data` → درخواست ارسالی
    - `operator` → شناسه اپراتور
    - `result` → پاسخ دریافتی از BaseService
    - timestamps → `now()`
4. بازگرداندن `lock_id` و نتیجه در خروجی JSON
5. در صورت استثناء: ارسال وضعیت false با کد خطای `1002` و اطلاعات پشتیبانی

</div>### نمونه پاسخ موفق

```
{
 "status": true,
 "time": 1732050123,
 "lock_id": 9321,
 "data": {
   "status": true,
   "token": "a342ff‑lock‑hash‑string",
   "expire": 180
 }
}
```

### ورودی‌ها

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>پارامتر</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>نوع سرویس (`flight` | `train`)</td></tr><tr><td>data</td><td>object</td><td>بله</td><td>اطلاعات کامل آیتم مورد قفل</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه</td></tr><tr><td>operator</td><td>object</td><td>بله</td><td>اپراتور فعلی (از JWT)</td></tr></tbody></table>

</div>### پاسخ در صورت خطا

```
{
 "status": false,
 "time": 1732050123,
 "error": {
   "code": 1002,
   "message": "Seats not available",
   "trace": [...]
 },
 "support": {
   "phone": "021‑91016838 in 121",
   "email": "ict@airplus.app",
   "panel": "helpdesk.airplus.app"
 }
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-baseservice%3A%3Alockite" style="font-family: Vazir; line-height: 1.9;">- `BaseService::lockItemProgress()`
- DB Facade روی `temporary_reservations`

</div>### نکات توسعه

<div id="bkmrk-online-lock-item" style="font-family: Vazir; line-height: 1.9;">- در نسخه‌های بعدی TTL لاک باید برگردانده شود.
- ثبت لاگ `SystemLog(type="LockOnlineItem")` جهت ردیابی.

</div>

# Route: POST /api/v2/online/search/{type}

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/search/{type}</td><td style="direction: ltr;">OnlineController@routeSearch</td><td style="direction: ltr;">authWithJwt</td><td>جستجوی مسیر رفت و برگشت بر اساس IATA یا ID شهر</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C%E2%80%AF%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE%E2%80%8C%D9%87%D8%A7%DB%8C" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- اعتبارسنجی تاریخ‌های `departure` و `returning` با `Validator::datetime()`
- فراخوانی `BaseService::combineRoute(branch, type, ...)` برای دریافت پروازها.
- واکشی اطلاعات مبدا/مقصد از Redis (کلید تکی مثل `airports:IATA`).
- در صورت ناکامی در Redis → دریافت از DB به همراه کش مجدد.
- تجمیع داده در `tempSearch` و بازگردانی به کلاینت.

</div>### پارامترها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>flight | train</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه</td></tr><tr><td>origin</td><td>string | int</td><td>بله</td><td>کد IATA یا شناسه شهر مبدأ</td></tr><tr><td>destination</td><td>string | int</td><td>بله</td><td>کد IATA یا شناسه شهر مقصد</td></tr><tr><td>departure</td><td>datetime</td><td>بله</td><td>تاریخ حرکت</td></tr><tr><td>returning</td><td>datetime | null</td><td>خیر</td><td>تاریخ بازگشت، اختیاری</td></tr></tbody></table>

</div>### پاسخ موفق نمونه

```
{
 "payload": {
   "departure":[{"flight":"IR432","price":2480000}],
   "returning":[{"flight":"IR433","price":2490000}]
 },
 "meta": {
   "search": {
     "origin": {"iata":"IKA","title_fa":"تهران"},
     "destination":{"iata":"IST","title_fa":"استانبول"},
     "departure":"2025‑11‑25",
     "returning":"2025‑11‑30"
   },
   "timestamp":1732050160
 }
}
```

### پاسخ خطا

```
{
 "error": {
   "code": 1000,
   "message": "تاریخ شروع جستجو معتبر نمی باشد"
 },
 "meta": {"timestamp":1732050160}
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-baseservice%3A%3Acombine" style="direction: rtl; font-family: Vazir; line-height: 1.9;">- `BaseService::combineRoute()`
- `Redis` → کش فرودگاه‌ها
- `DB` → جداول `airports`, `countries`, `states`, `cities`

</div>### بهبود پیشنهادی

<div id="bkmrk-online-route-search" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از نوع `bus`
- پویش همزمان در providers ( پارالل requests )

</div>

# Route: POST /api/v2/online/flight/list/date

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #ececec; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/flight/list/date</td><td style="direction: ltr;">OnlineController@flightListDate</td><td style="direction: ltr;">authWithJwt</td><td>دریافت لیست پروازها بر اساس IATA و تاریخ</td></tr></tbody></table>

</div>### منطق عملکرد

<div class="align-right" id="bkmrk-%D8%B3%D8%A7%D8%AE%D8%AA%E2%80%AF%D8%A2%D8%B1%D8%A7%DB%8C%D9%87%E2%80%AF%24arrdata%E2%80%AF" style="font-family: Vazir; line-height: 1.9;">- ساخت آرایه `$ArrData` با `origin, destination, departure_date, returning_date`.
- فراخوانی `BaseService‑>combineFlight($ArrData, true, branch)`.
- واکشی داده‌های فرودگاه‌ها (مبدأ و مقصد) از Redis و درصورت نبود از DB.
- بازگردانی نتیجه با `search` (جزئیات جستجو) و `data` (پاسخ سرویس).

</div>### پارامترهای درخواستی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>نام</td><td>نوع</td><td>ضروری</td><td>توضیح</td></tr><tr><td>origin</td><td>string</td><td>بله</td><td>کد IATA مبدأ</td></tr><tr><td>destination</td><td>string</td><td>بله</td><td>کد IATA مقصد</td></tr><tr><td>departure\_date</td><td>date</td><td>بله</td><td>تاریخ حرکت</td></tr><tr><td>returning\_date</td><td>date</td><td>خیر</td><td>تاریخ بازگشت</td></tr><tr><td>only\_charters</td><td>boolean</td><td>خیر</td><td>نمایش فقط چارتری‌ها (پیش‌فرض false)</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه</td></tr></tbody></table>

</div>### پاسخ موفق نمونه

```
{
 "search": {
   "origin": {"iata":"MHD","title_fa":"مشهد"},
   "destination":{"iata":"THR","title_fa":"تهران"},
   "departure_date":"2025‑11‑22"
 },
 "data":[
   {"flight":"W567","airline":"Mahan","fare":1480000,"type":"charter"},
   {"flight":"IR345","airline":"IranAir","fare":1580000,"type":"system"}
 ]
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-functions%3A%3Acheckdate" style="direction: rtl; font-family: Vazir; line-height: 1.9;">- `Functions::checkDatetime()`
- `Redis` و جداول `airports`، `countries`، `states`، `cities`
- `BaseService::combineFlight()`

</div>### نکات توسعه

<div id="bkmrk-online-flight-list-date" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پارامتر `filter_price_range`
- اضافه نمودن کش داینامیک برای نتایج Flight List

</div>

# Route: POST /api/v2/online/{type}/unlock

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/{type}/unlock</td><td style="direction: ltr;">OnlineController@unlockItemProgress</td><td style="direction: ltr;">authWithJwt</td><td>رفع قفل آیتم (Flight/Train) در Temporary Reservation</td></tr></tbody></table>

</div>### منطق عملکرد

<div class="align-right" id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%AF%28%D8%AA%D9%85%D8%A7%D9%85%E2%80%AF%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%DB%8C" style="font-family: Vazir; line-height: 1.9;">1. ورودی (تمام داده‌های درخواست) از $request دریافت می‌شود.
2. نمونه‌ای از `\App\Lib\BaseService` ایجاد می‌شود.
3. فراخوانی تابع `BaseService::unlockItemProgress($request‑>all(), $request‑>get('branch'))`.
4. پاسخ دریافتی در `data` به صورت JSON بازگردانده می‌شود.
5. در صورت خطا (Code 1002)، جزئیات trace و مشخصات پشتیبانی برمی‌گردد.

</div>### ورودی‌ها

داده‌ها بر اساس نوع سرویس (`{type}`) تغییر می‌کنند، اما حداقل پارامترها عبارتند از:

<div class="align-right" id="bkmrk-branch%E2%80%AF%3A%E2%80%AF%D8%B4%D9%86%D8%A7%D8%B3%D9%87%D9%94%E2%80%AF%D8%B4%D8%B9%D8%A8%D9%87" style="font-family: Vazir; line-height: 1.9;">- `branch` : شناسهٔ شعبه
- `lock_id` : شناسه قفل موقت در جدول `temporary_reservations`
- `token` | `progress_id` : شناسه داخلی لاک

</div>### پاسخ نمونه موفق

```
{
 "status": true,
 "time": 1732050510,
 "data": {
   "unlocked": true,
   "id": 9372,
   "message": "Lock released successfully"
 }
}
```

### پاسخ خطا

```
{
 "status": false,
 "time": 1732050510,
 "error": {
   "code": 1002,
   "message": "Lock not found or already expired",
   "trace": [...]
 },
 "support": {
   "phone": "021‑91016838 in 121",
   "email": "ict@airplus.app",
   "panel": "helpdesk.airplus.app"
 }
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-baseservice%3A%3Aunlocki" style="font-family: Vazir; line-height: 1.9;">- `BaseService::unlockItemProgress()`
- مدیریت Table `temporary_reservations`

</div>### نکات توسعه

<div id="bkmrk-online-unlock-item" style="font-family: Vazir; line-height: 1.9;">- بهتر است در نسخه بعدی، تاریخ Expiry لاک در خروجی نمایش داده شود.

</div>

# Route: POST /api/v2/online/{type}/status

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>POST</td><td style="direction: ltr;">/api/v2/online/{type}/status</td><td style="direction: ltr;">OnlineController@statusItemProgress</td><td style="direction: ltr;">authWithJwt</td><td>دریافت وضعیت فعلی قفل یا رزرو (Flight/Train)</td></tr></tbody></table>

</div>### منطق عملکرد

<div class="align-right" id="bkmrk-%D8%B3%D8%A7%D8%AE%D8%AA%E2%80%AFbaseservice%E2%80%AF%D9%88%E2%80%AF%D9%81" style="font-family: Vazir; line-height: 1.9;">1. ساخت `BaseService` و فراخوانی `statusItemProgress($request‑>all(), $request‑>get('branch'))`
2. مقدار خروجی در متغیر `$DataBaseService`
3. بازگرداندن پاسخ در دو کلید اصلی: 
    - `changed` → آیا تغییری در وضعیت اتفاق افتاده؟
    - `data` → جزئیات وضعیت فعلی آیتم
4. مدیریت خطاها مشابه unlock با code 1002

</div>### پاسخ نمونه موفق

```
{
 "status": true,
 "time": 1732050560,
 "changed": false,
 "data": {
   "lock_id": 9372,
   "progress": "locked",
   "expires_in": 120,
   "payload": {
     "service": "flight",
     "origin": "IKA",
     "destination": "IST"
   }
 }
}
```

### پاسخ خطا

```
{
 "status": false,
 "time": 1732050560,
 "error": {
   "code": 1002,
   "message": "Invalid lock identifier",
   "trace": [...]
 }
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-baseservice%3A%3Astatusi" style="font-family: Vazir; line-height: 1.9;">- `BaseService::statusItemProgress()`
- جداول کنترل لاک موقت و Redis (در صورت فعال)

</div>### یادداشت توسعه

<div id="bkmrk-online-status-item" style="font-family: Vazir; line-height: 1.9;">- اضافه کردن «progress\_history» به پاسخ برای ردیابی رویدادهای لاک.

</div>

# Route: GET /api/v2/online/accommodation/list

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 96%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #e9e9e9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/accommodation/list</td><td style="direction: ltr;">OnlineController@listAccommodation</td><td style="direction: ltr;">authWithJwt</td><td>لیست اقامتگاه‌های قابل نمایش در سیستم Online Booking</td></tr></tbody></table>

</div>### منطق عملکرد

<div id="bkmrk-%D8%A8%D9%87%E2%80%AF%D8%B5%D9%88%D8%B1%D8%AA%E2%80%AF%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85%E2%80%AF%D9%81%D8%B1%D8%A7%D8%AE%D9%88" style="font-family: Vazir; line-height: 1.9;">1. به صورت مستقیم فراخوانی `BaseService::listAccommodation($request)`.
2. مدیریت فیلترها (شهر، تاریخ، ظرفیت، قیمت و …) در لایه سرویس.
3. بازگردانی ساختار اطلاعات در قالب JSON از پاسخ سرویس داخلی.

</div>### ورودی‌ها

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-ci" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 94%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #fafafa; font-weight: bold;"><td>پارامتر</td><td>نوع</td><td>توضیح</td></tr><tr><td>city</td><td>string</td><td>شهر اقامتگاه (مثلاً SHZ یا 'شیراز')</td></tr><tr><td>check\_in</td><td>date</td><td>تاریخ ورود</td></tr><tr><td>check\_out</td><td>date</td><td>تاریخ خروج</td></tr><tr><td>guest\_count</td><td>integer</td><td>تعداد مسافران</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه ارسال‌کننده</td></tr></tbody></table>

</div>### پاسخ نمونه موفق

```
{
 "status": true,
 "timestamp": 1732050600,
 "payload": [
   {"hotel":"Homa Hotel","city":"Shiraz","stars":5,"rooms":[...]},
   {"hotel":"Persepolis","city":"Shiraz","stars":4}
 ]
}
```

### وابستگی‌ها

<div class="align-right" id="bkmrk-baseservice%3A%3Alistacc" style="font-family: Vazir; line-height: 1.9;">- `BaseService::listAccommodation()`
- منابع داخلی اقامتگاه‌ها (جدول `accommodations` یا API third‑party)

</div>### یادداشت توسعه

<div id="bkmrk-online-accommodation-list" style="font-family: Vazir; line-height: 1.9;">- افزودن پارامتر `sort_by` برای مرتب‌سازی بر اساس قیمت یا ستاره.
- در نسخه بعد، کش نتایج به مدت ۶۰ ثانیه پیشنهاد می‌شود.

</div>

# GET /api/v2/online/accommodation/get_min_prices

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 98%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f3f3f3; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Logic Source</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/accommodation/get\_min\_prices</td><td style="direction: ltr;">OnlineController@getAccommodationMinPrices</td><td style="direction: ltr;">authWithJwt</td><td style="direction: ltr;">BaseService::getAccommodationMinPrices()</td></tr></tbody></table>

</div>## Logic

یک Wrapper ساده برای فراخوانی `BaseService::getAccommodationMinPrices()` است که با ورودی‌هایی مثل شهر، تاریخ ورود/خروج و میهمانان، لیست اقامتگاه‌ها را از سرویس‌های Tport، Sepehr، AirPlus و SnappTrip گرفته و کمترین قیمت ممکن را برمی‌گرداند. داده‌ها از Redis کش می‌شوند.

## Input

<div id="bkmrk-city%3A%E2%80%AF%DA%A9%D8%AF%E2%80%AF%DB%8C%D8%A7%E2%80%AF%D9%86%D8%A7%D9%85%E2%80%AF%D8%B4%D9%87%D8%B1%E2%80%AF" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- **city**: کد یا نام شهر (اجباری)
- **check\_in / check\_out**: تاریخ ورود و خروج
- **guests**: تعداد میهمان‌ها
- **branch**: شناسه شعبه

</div>## Response

```
{
 "status": true,
 "payload": [
   {"hotel_id": 331, "name": "Parsian Kowsar", "min_price": 3850000},
   {"hotel_id": 127, "name": "Hotel Homa", "min_price": 4520000}
 ],
 "meta": {"timestamp": 1732050810}
}
```

## Security

احراز هویت از طریق JWT. سطح دسترسی اپراتور باید دارای دسترسی به ماژول Online باشد.

## Dependencies

<div id="bkmrk-baseservice%3A%3Agetacco" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- `BaseService::getAccommodationMinPrices`
- Redis Cache (city → min prices)
- DB: `accommodations`, `rooms`

</div>## Performance

زمان میانگین پاسخ ≤ 400 ms از کش، یا ≤ 2 s در صورت فراخوانی API خارجی.

## Error Handling

```
{
 "status": false,
 "code": 1002,
 "message": "No available accommodation found"
}
```

## Side Effects

داده‌ای تغییر نمیکند؛ فقط Read از DB یا Cache.

## Audit Trail

در this route لاگ نویسی پیش‌فرض فعال نیست؛ ممکن است در مدیر سطح Enterprise مشاهده شود.

## Improvement

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFsorti" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پارامتر sorting بر اساس price یا star rating.
- امکان cache بیشتر بر اساس city+checkin.

</div>## Conclusion

بخش MinPrices اولین مرحله جریان "Accommodation Search" است و پایه‌ی lock/checkStatus می‌باشد.

<div id="bkmrk-online-accommodation-min-prices" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/online/accommodation/get_room_type_prices

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 98%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #efefef; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/accommodation/get\_room\_type\_prices</td><td style="direction: ltr;">OnlineController@getRoomTypePrices</td><td style="direction: ltr;">authWithJwt</td><td>دریافت قیمت، ظرفیت و رفتاری اتاق‌ها برای یک اقامتگاه خاص</td></tr></tbody></table>

</div>## Logic

تابع به‌ترتیب، اقامتگاه را در جدول `mapping_accommodations` پیدا می‌کند و پس‌ از گرفتن اتاق‌ها از `accommodation_roomtypes`، چهار Provider (سرویس‌دهنده) فعال را تشخیص می‌دهد. برای هر Provider فعال (**Tport**، **Sepehr**، **AirPlus**، **SnappTrip**)، API اختصاصی فراخوانی شده و قیمت، ظرفیت و board typeها برگشت داده می‌شوند. تمام قیمت‌ها توسط `BaseService::getRoomTypeMarkups()` براساس branch/group/level محاسبه می‌گردند.

## Input

<div id="bkmrk-accommodation_id%E2%80%AF%28in" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- **accommodation\_id** (Integer) – شناسه اقامتگاه
- **checkin\_date / checkout\_date** (String) – تاریخ ورود و خروج
- **adults\_count** (Integer)
- **children\_count** (Integer)
- **branch, group, level** (درون JWT)

</div>## Response

```
{
  "Status": true,
  "Time": 1732050987,
  "Data": [
    {
      "id": 502,
      "name": "Double Standard",
      "board_type_list": [
        {
          "service": "tport",
          "DisplayableService": "airplusHub",
          "supplier": {"title_fa":"TPort Supplier 1"},
          "financial_total":{"net_price":3980000,"markup":198000}
        }
      ],
      "purchase_capacity": 3
    }
  ]
}
```

## Security

JWT الزامی است؛ هر درخواست باید شامل branch/group/level برای markup دقیق باشد.

## Dependencies

<div id="bkmrk-tportapi%3A%3Asendreques" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- `TportApi::sendRequestAccommodation()`
- `SepehrApi::sendRequestAccommodation()`
- `AirPlusApi::sendRequest()`
- `SnappTripApi::sendRequestAccommodation()`
- `BaseService::getRoomTypeMarkups()`
- `Redis` برای کش حداقل‌قیمت هر اقامتگاه → کلید `accommodations:min_price:{id}`

</div>## Performance

تعداد فراخوانی API تا ۴ عدد ممکن است. بهتر است در نسخه بعد با async موارد تجمیع (Parallel) شوند. پاسخ میانگین : ۲ – ۴ ثانیه

## Error Handling

```
{
 "Status": false,
 "Code": 1002,
 "Message": "No active provider data"
}
```

## Side Effects

هیچ تغییر داده‌ای در DB ندارد؛ صرفاً Read با write در cache Redis.

## Audit Trail

هر فراخوانی API در جدول `system_logs` با نوع "AccommodationRoomTypeRequest" ثبت می‌گردد.

## Improvement

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFcurre" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پارامتر `currency` برای تبدیل قیمت.
- ایجاد threaded request برای سرویس‌های خارجی.
- cache تفکیکی براساس (اقامتگاه+تاریخ+branch).

</div>## Conclusion

هسته‌ی Versatile Pricing Engine اقامتگاه‌هاست و مستقیماً به مرحله Lock/CheckStatus منتهی می‌شود.

<div id="bkmrk-online-accommodation-roomtype-prices" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# GET /api/v2/online/accommodation/get_details

## Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"><table border="1" style="width: 98%; margin: auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f7f7f7; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td>GET</td><td style="direction: ltr;">/api/v2/online/accommodation/get\_details</td><td style="direction: ltr;">OnlineController@getAccommodationDetails</td><td style="direction: ltr;">authWithJwt</td><td>دریافت اطلاعات کامل اقامتگاه (رسانه، امکانات، قوانین)</td></tr></tbody></table>

</div>## Logic

تابع ابتدا رکورد اقامتگاه را از `hotels` می‌خواند. سپس رسانه‌های پیوسته (cover / image / video / logo) در Media خوانده می‌شوند. امکانات و قوانین (‌Facilities, Policies, Rules‌) به صورت گروه‌بندی‌شده برگشت داده می‌شوند. اگر اقامتگاه charter اکتیو داشت، جزئیات transfer از `charter_items` به policies اضافه می‌شود.

## Input

<div id="bkmrk-accommodation_id%3A%E2%80%AF%D8%B4%D9%86" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- **accommodation\_id**: شناسه اقامتگاه (الزامی)

</div>## Response

```
{
 "Status": true,
 "Data": {
   "id":331,
   "name":"Parsian Kowsar",
   "logo":"/uploads/hotels/logo331.png",
   "media":{"images":[{}],"videos":false},
   "facility_categories":[{"title":"امکانات عمومی","facilities":[...]}],
   "rules":{"policies":{...},"cancellation":[...],"public":[...]}
 }
}
```

## Security

JWT مورد نیاز. دسترسی فقط برای اپراتورهای دارای module:Online.

## Dependencies

<div id="bkmrk-media%E2%80%AF%28model%29-facili" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- `Media` (Model)
- `facilities` و `accommodation_facilities_mapping`
- `facilities_categories`
- `charters`، `charter_items`
- `accommodation_policies`، `rules`

</div>## Performance

پاسخ DB تک مرحله‌ای ≤ ۲۵۰ ms در میانگین.

## Error Handling

```
{"Status":false,"Message":"Accommodation not found."}
```

## Side Effects

Read‑only operation، بدون نوشتن در DB.

## Audit Trail

درخواست‌ها با برچسب "AccommodationDetailsRequest" در system\_logs ثبت می‌شوند.

## Improvement

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFlangu" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;">- افزودن پارامتر `language` برای multi‑locale.
- کش JSON در Redis برای media+facility به‌صورت ۲۴ ساعته.

</div>## Conclusion

بخش GetDetails پل میان Search و نمایش کارت اقامتگاه در UI است و اطلاعات هویتی اقامتگاه را تکمیل می‌کند.

<div id="bkmrk-online-accommodation-details" style="direction: rtl; font-family: Vazir; text-align: justify; line-height: 1.9;"></div>

# * POST /api/v2/online/accommodation/check_status

## Accommodation Check Status (V2)

<div class="api-docs" id="bkmrk-endpoint%3A-post-%2Fv2%2Fo"><div class="endpoint-info"><div>**Endpoint:** `POST /v2/online/accommodation/check_status`</div><div>**Controller:** `OnlineController@checkAccommodationStatus`</div><div>**Delegates to:** `BaseService::checkAccommodationStatus(type, accommodation, agentDetails)`</div><div>**Auth:** `authWithJwt`</div></div></div>### Description

این اندپوینت جهت بررسی وضعیت رزرو اقامتگاه در نسخه دوم سیستم آنلاین طراحی شده است. مسیر پردازش بسته به نوع سرویس (`type`) متفاوت بوده و نتایج نهایی – شامل وضعیت، تغییر قیمت، کد لاک موقت یا پیام خطا – در جدول `temporary_reservations` ثبت می‌گردند.

### Request Example

```

POST /v2/online/accommodation/check_status
{
  "type": "airplus",
  "accommodation": {
    "id": 1549,
    "checkin": "2025-01-15",
    "checkout": "2025-01-20",
    "rooms": 2,
    "adults": 4
  }
}
  
```

### Response Example

```

{
  "status": "success",
  "changed": false,
  "lock_id": "TMP-7634",
  "payable": 5840000,
  "errors": []
}
  
```

### Technical Logic

<div class="api-docs" id="bkmrk-client-%E2%86%92-send-reques"><div class="flowchart"><div class="flow-item flow-item-process">Client → send request `/v2/online/accommodation/check_status`</div><div class="flow-arrow">↓</div><div class="flow-item flow-item-process">Parse Request → extract `type` و `accommodation`</div><div class="flow-arrow">↓</div><div class="flow-item flow-item-decision">Branch by `type` <small>(Tport / Airplus / Sepehr / Snapptrip)</small></div><div class="flow-arrow">↙ ↓ ↘</div><div class="flow-item" style="background-color: #e8f3fe;">**Tport** → call `calculate_price()` → `save_normal_reserve()` → return `lock_id`</div><div class="flow-item" style="background-color: #fef6e4;">**Airplus** → validate passengers → check charter capacity handle codes `1005–1026`</div><div class="flow-item" style="background-color: #fafafa;">**Sepehr** → use saved temporary lock if exists</div><div class="flow-item" style="background-color: #eafdea;">**Snapptrip** → call `create_book()` → return `reservation_code`</div><div class="flow-arrow">↓</div><div class="flow-item flow-item-process">Save result snapshot → `temporary_reservations`</div><div class="flow-arrow">↓</div><div class="flow-item flow-item-success">Return response: `{status, changed, lock_id, payable, errors[]}`</div></div></div>### Error Conditions

<div class="api-docs" id="bkmrk-1005-%E2%80%93-%D8%B8%D8%B1%D9%81%DB%8C%D8%AA-%D9%BE%D8%B1-%D8%B4%D8%AF%D9%87-">- `1005` – ظرفیت پر شده است
- `1010` – اتاق در تاریخ انتخابی یافت نشد
- `1026` – اطلاعات ناقص مسافران
- در سایر موارد پاسخ شامل `errors[]` با جزئیات دقیق می‌شود.

</div>### Notes

در صورت فعال بودن ماژول `temporary_reservations`، هر درخواست تکراری (با داده‌ی یکسان) پیش از تریگر مجدد منطق بررسی از کش Redis مورد بررسی قرار می‌گیرد.

# GET /api/v2/base/data

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95.9524%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/base/data</td><td style="direction: ltr; text-align: left;">V2BaseController@automationBaseData</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">واکشی داده‌های پایهٔ سیستم اتوماسیون مثل نوع اقامتگاه، کشورها، ایستگاه‌ها، یا تیپ اتاق‌ها</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-automationbasedata" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">تابع **automationBaseData** وظیفه دارد بر اساس پارامترهای `subject` یا `table` و `action`، داده‌های ثابت مورد نیاز بخش‌هایی مثل رزرو اقامتگاه، نوع قطار، یا نرخ اتاق را واکشی کند. روند اجرای کلی: - بررسی نوع جدول درخواست‌شده (مثلاً `hotel_title`، `countries`، `train_types`، `train_stations`).
- واچش داده‌ها از دیتابیس یا Redis در صورت وجود Cache کلیدشده.
- در صورت نبود داده در Redis، فراخوانی مستقیم از جدول یا مدل مربوطه انجام می‌شود (مثلاً `HotelTitle`).
- تبدیل نتیجه به آرایهٔ استاندارد شامل شناسه، عنوان فارسی و انگلیسی، وضعیت و ویژگی.
- برگرداندن خروجی در قالب JSON همراه با زمان UNIX و وضعیت.

</div>### ورودی‌ها (Request Fields)

<div id="bkmrk-input-table" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>subject</td><td>string</td><td>بله</td><td>دسته داده مورد نیاز (مثلاً `train_types` یا `room_rate`)</td></tr><tr><td>table</td><td>string</td><td>خیر</td><td>نام جدول اصلی در دیتابیس (مثلاً `hotel_title`)</td></tr><tr><td>action</td><td>string</td><td>خیر</td><td>زیرعملیات مربوط به table در صورت وجود (مثلاً `room_type`)</td></tr><tr><td>search</td><td>string</td><td>خیر</td><td>فیلتر جستجو برای محدودسازی نتایج</td></tr><tr><td>state</td><td>int</td><td>خیر</td><td>شناسهٔ استان مورد استفاده برای فیلتر ایستگاه‌ها</td></tr></tbody></table>

</div>### خروجی (Response)

<div id="bkmrk-output-structure" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت کلی درخواست (true/false)</td></tr><tr><td>time</td><td>int (Unix Timestamp)</td><td>زمان واکنش سرور</td></tr><tr><td>data</td><td>array</td><td>لیست آیتم‌های جدول مورد نظر با title‌ها و شناسه‌ها</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
GET /api/v2/base/data?subject=train_types
Authorization: Bearer {JWT_TOKEN}
```

#### نمونه پاسخ:

```
{
  "status": true,
  "time": 1732289102,
  "data": {
    "titles": [
      {"title_fa": "چهارنفره لوکس", "title_en": "Luxury 4", "status": true},
      {"title_fa": "دو نفره عادی", "title_en": "Standard Couple", "status": true}
    ]
  }
}
```

### امنیت و کنترل دسترسی

- احراز هویت با `AuthWithJWT` و بررسی جدول کاربر بر اساس نوع (`operators`, `customers`, `colleague_auth`).
- توکن منقضی یا غیرمعتبر → بازگشت کدهای خطا 1006 یا 1001.
- هیچ سطح دسترسی Role-Based روی نوع داده‌ها وجود ندارد؛ صرفاً مجوز کلی برای شعبه فعلی.
- عدم اعتبارسنجی ورودی‌های search و action امکان تزریق param دارد (پیشنهاد به Validation).

### نکات کارایی و پیاده‌سازی

- هر بار درخواست مستقیماً از DB انجام می‌شود اگر Redis Cache قبلاً موجود نباشد.
- محدودسازی نتایج با `limit(30)` جهت کنترل بار سرور.
- در حالت جستجو، چندین شرط LIKE بدون ایندکس باعث کاهش سرعت Query می‌شود.
- پیشنهاد به ایندکس ترکیبی روی فیلدهای `country` و `state` در جداول ایستگاه‌ها.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\HotelTitle;
- use App\\Helpers\\Functions;

### کدهای خطا و خروجی‌های Exception

<table border="1" cellpadding="6" cellspacing="0" id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>Exception عمومی هنگام Query یا Decode</td><td>automationBaseData</td></tr><tr><td>1006</td><td>JWT منقضی یا نامعتبر</td><td>AuthWithJWT</td></tr><tr><td>500</td><td>Database connection/Redis read failed</td><td>V2BaseController</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اعتبارسنجی فیلدهای ورودی به وسیلهٔ Request Validator داخلی.
- اضافه‌کردن TTL به Cache‌های Redis برای جلوگیری از حافظهٔ بی‌انتها.
- محدودسازی دسترسی به جدول‌های پایه فقط برای کاربران نوع base/operator.

### پیشنهادهای بهبود

- استخراج Service Layer مجزا برای هر نوع دادهٔ پایه (BaseServiceSplit).
- افزودن pagination واقعی با پارامتر `page` و `size`.
- اعمال Cache invalidation زمانی با تغییر در جداول.
- بهینه‌سازی کوئری LIKE با FullTextIndex برای جستجوی طبیعی‌تر.

### ممیزی دسترسی و عدم قطعیت

- هیچ log برای مشاهدهٔ این Route وجود ندارد.
- پیشنهاد: افزودن audit طبقه‌بندی شده برای هر subject/table فراخوانی‌شده.
- افزودن ردپا (traceId) در پاسخ JSON جهت تطبیق درخواست‌های داخلی.

### جمع‌بندی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%DB%8C%DA%A9%DB%8C-%D8%A7%D8%B2-" style="direction: rtl; text-align: justify;">این endpoint یکی از بخش‌های زیرساختی برای بارگذاری داده‌های ثابت در پانل‌های اتوماسیون است. با حذف cache و اعتبارسنجی ناکافی ورودی، مستعد کندی و SQL Injection خفیف است. نرمال‌سازی منطق به سرویس مستقل و محدودسازی سطح دسترسی، پیش‌نیاز تبدیل آن به نسخهٔ enterprise-grade محسوب می‌شود.</div>

# POST /api/v2/titles/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/titles/list</td><td style="direction: ltr; text-align: left;">V2BaseController@baseAutomationData</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست عنوان‌های ثابت یا داده‌های ساختاری جدول‌های پایه مانند room\_type، room\_rate و room\_view</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-function-logic-content" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify;">تابع **baseAutomationData** بر اساس `table` و `action` پارامتر دریافت‌شده از درخواست، داده‌های عنوانی مورد نیاز را واکشی و پردازش می‌کند: - اگر `table` برابر با `hotel_title` باشد، بر اساس مقدار `action` یکی از سه حالت `room_type`، `room_rate` یا `room_view` اجرا می‌شود.
- دریافت محتوا از جدول `hotel_titles` با انتخاب فیلد `data` و دیکد آن به JSON.
- ساخت آرایه نهایی `fixedTitles` با فیلدهای `title_fa`، `title_en`، `property`، `status`، و `selected`.
- ارسال خروجی استاندارد به صورت JSON به همراه زمان UNIX در فیلد `time`.

</div>### ورودی‌ها (Request Fields)

<div id="bkmrk-input-table" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>table</td><td>string</td><td>بله</td><td>نام جدول در دیتابیس (فقط `hotel_title` در حال حاضر)</td></tr><tr><td>action</td><td>string</td><td>بله</td><td>زیرعملیات مربوط به جدول (مقادیر: `room_type`، `room_rate`، `room_view`)</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
POST /api/v2/titles/list
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "table": "hotel_title",
  "action": "room_type"
}
```

### خروجی (Response)

<table border="1" cellpadding="6" cellspacing="0" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت کلی درخواست</td></tr><tr><td>time</td><td>integer</td><td>زمان واکنش سرور</td></tr><tr><td>data.titles\[\]</td><td>array</td><td>لیست عنوان‌های ثابت با فیلدهای زبان، وضعیت و ویژگی‌ها</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "time": 1732289234,
  "data": {
    "titles": [
      {"title_fa": "سوئیت دونفره", "title_en": "Double Suite", "property": "2 beds", "status": true, "selected": false},
      {"title_fa": "اتاق رو به دریا", "title_en": "Sea View Room", "status": true, "selected": true}
    ]
  }
}
```

### امنیت و کنترل دسترسی

- تابع دارای لایهٔ امنیتی `AuthWithJWT` برای تأیید هویت کاربر است.
- عدم وجود محدودسازی Role-Based بر اساس نوع داده‌ها؛ صرفاً مجوز عمومی کاربر معتبر بررسی می‌شود.
- فاقد validation داخلی برای فیلدهای ورودی؛ تزریق دادهٔ جعلی روی `action` محتمل است.

### نکات کارایی و پیاده‌سازی

- داده مستقیماً از جدول `hotel\_titles` واکشی می‌شود؛ بدون cache یا pagination.
- هر بار دیکد JSON کامل در سمت سرور انجام می‌شود؛ در صورت حجم زیاد منجر به افزایش load CPU خواهد شد.
- نیاز به ایندکس مجزا برای فیلد `title` در جدول `hotel_titles` جهت بهینه‌شدن Queryها.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use App\\Models\\HotelTitle;
- use Illuminate\\Http\\Request;
- use App\\Helpers\\Functions;

### کدهای خطا و خروجی‌های Exception

<table border="1" cellpadding="6" cellspacing="0" id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>Database Exception → Trace بازگردانده می‌شود.</td><td>baseAutomationData</td></tr><tr><td>1006</td><td>JWT token منقضی یا نامعتبر.</td><td>AuthWithJWT</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اعمال `Request Validation` برای ورودی‌های `table` و `action`.
- پنهان‌سازی Trace کامل در خروجی خطا.
- محدودسازی دسترسی فقط برای کاربران نوع operator.

### پیشنهادهای بهبود

- افزودن مکانیزم caching با TTL جهت پاسخ سریع‌تر در تکرار درخواست.
- جداسازی منطق دریافت داده به کلاس Service مستقل (مثلاً TitleService).
- افزودن فیلد زبان به عنوان پارامتر اختیاری جهت انتخاب زبان خروجی.

### ممیزی دسترسی و لاگ‌ها

- هیچ لاگ سروری برای ثبت تعداد فراخوانی‌ها وجود ندارد.
- پیشنهاد ثبت رویداد با event `SystemLog::dispatch()` جهت ردیابی درخواست‌های غیرمجاز.

### جمع‌بندی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-endpoint-%D9%85%D8%B3%DB%8C%D8%B1-%D8%A7%D8%B5" style="direction: rtl; text-align: justify;">این Endpoint مسیر اصلی دریافت عنوان‌های پارامتری در سیستم اتوماسیون است. ساختار ساده و خروجی یکسان باعث سهولت مصرف در فرانت‌اند می‌شود، اما عدم وجود validation و cache منجر به ضعف امنیت و Performance در حجم بالا خواهد شد. توصیه می‌شود Refactor در لایهٔ Service و اعمال TTL روی داده‌های ثابت انجام گیرد.</div>

# POST /api/v2/notif/simple/list

<div id="bkmrk-notif-simple-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/notif/simple/list</td><td style="direction: ltr; text-align: left;">V2BaseController@notifySimpleList</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">واچش اعلان‌های ساده (Simple Notification) کاربر برای نمایش فوری در داشبورد یا موبایل</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-notifysimplelis" style="direction: rtl; text-align: justify;">تابع **notifySimpleList** با دریافت شناسهٔ شعبه یا اپراتور کاربر، لیست اعلان‌های فعال را از جدول `notifications` واکشی می‌کند و آن‌را به ترتیب نزولی تاریخ مرتب می‌نماید. روند اجرایی اصلی: - دریافت پارامتر `branch` از JWT یا درخواست.
- فیلتر اعلان‌هایی که `status = 1` و نوعشان `simple` است.
- مرتب‌سازی با `orderBy('id','DESC')` برای آخرین اعلان‌ها.
- بازگرداندان خروجی در قالب JSON شامل متادیتا (timestamp).

</div>### ورودی‌ها (Request Fields)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="direction: rtl; text-align: justify;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبهٔ کاربر برای فیلتر اعلان‌ها</td></tr><tr><td>limit</td><td>integer</td><td>خیر</td><td>تعداد رکوردهای قابل نمایش در پاسخ (پیش‌فرض ۱۰)</td></tr><tr><td>search</td><td>string</td><td>خیر</td><td>عبارت جستجوی جزئی در عنوان یا متن اعلان</td></tr></tbody></table>

</div>### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت نهایی پاسخ</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان واکنش سرور (Unix)</td></tr><tr><td>data\[\]</td><td>array</td><td>آرایه‌ای از اعلان‌ها</td></tr><tr><td>data\[\].title</td><td>string</td><td>عنوان اعلان</td></tr><tr><td>data\[\].content</td><td>string</td><td>متن کامل اعلان</td></tr><tr><td>data\[\].created\_at</td><td>datetime</td><td>تاریخ ایجاد اعلان</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "meta": {"timestamp": 1732289310},
  "data": [
    {"title": "به‌روزرسانی سامانه", "content": "نسخه جدید سیستم مدیریت کاربران فعال شد.", "created_at": "2025-11-22 09:34:16"},
    {"title": "اتمام اعتبار", "content": "اعتبار کیف پول به زیر حد مجاز رسیده است.", "created_at": "2025-11-21 19:48:00"}
  ]
}
```

### امنیت و کنترل دسترسی

- فقط کاربران معتبر JWT می‌توانند اعلان‌های شعبهٔ خود را ببینند.
- عدم وجود محدودسازی بر اساس role؛ کافی‌ست توکن معتبر باشد.
- جستجوی رشته‌ای بدون escaping می‌تواند زمینهٔ تزریق در LIKE را فراهم کند.

### نکات کارایی و سرعت

- عدم استفاده از Redis باعث واکشی مستقیم از DB در هر بار درخواست می‌شود.
- پیشنهاد افزودن Cache کوتاه مدت (TTL 60s) برای کاهش Queryهای تکراری.
- در صورت زیاد بودن اعلان‌ها، از pagination یا LIMIT استفاده شود.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use App\\Models\\Notification;
- use App\\Helpers\\Functions;

### کدهای خطا و خروجی‌های Exception

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>Database یا Query Exception</td><td>notifySimpleList</td></tr><tr><td>1006</td><td>JWT نامعتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>404</td><td>عدم یافتن اعلان برای branch</td><td>Query Result</td></tr></tbody></table>

### پیشنهادهای امنیتی

- افزودن escaping برای پارامتر `search`.
- اعمال محدودیت max-limit برای جلوگیری از overload.
- جداسازی نقش‌ها برای اعلان‌های اختصاصی شعبه و مدیر کل.

### پیشنهادهای بهبود

- افزودن سیستم دسته‌بندی اعلان‌ها بر اساس نوع (سیستمی، مالی، پشتیبانی).
- امکان مارک خوانده‌شدن توسط کاربر (read/unread flag).
- فعال‌سازی لاگ برای ثبت آخرین مشاهدهٔ اعلان توسط هر اپراتور.

### ممیزی و لاگ‌ها

- در حال حاضر هیچ لاگی برای دسترسی به اعلان‌ها ثبت نمی‌شود.
- پیشنهاد ثبت با `SystemLog::dispatch("notifListAccess")` برای رصد خطاها.

### جمع‌بندی

<div id="bkmrk-endpoint-post-%2Fnotif" style="direction: rtl; text-align: justify;">Endpoint **POST /notif/simple/list** یکی از مسیرهای کلیدی مدیریتی برای رساندن اعلان‌های سریع به کاربران است. ساختار ساده و بدون Cache باعث افزایش بار در تکرار درخواست‌ها شده و نبود Audit باعث فقدان ردپای ثبت‌شدهٔ امنیتی است. توصیه می‌شود Redis و صفحه‌بندی برحسب زمان آخرین مشاهده در نسخهٔ بعدی اضافه گردد.</div>

# POST /api/v2/notif/send

<div id="bkmrk-notif-send" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify;"><table border="1" cellpadding="6" cellspacing="0" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/notif/send</td><td style="direction: ltr; text-align: left;">V2BaseController@notifySend</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ارسال اعلان جدید به کاربران یا اپراتورهای هدف در سیستم Base</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-notifysend-%D9%88%D8%B1%D9%88%D8%AF" style="direction: rtl; text-align: justify;">تابع **notifySend** ورودی‌های اعلان را از درخواست دریافت کرده و با استفاده از مدل `notifications` در بانک اطلاعاتی ثبت می‌کند. منطق تابع به صورت خلاصه: - دریافت و اعتبارسنجی فیلدهای اجباری (نوع، عنوان، متن، گیرنده).
- ساخت رکورد جدید در جدول `notifications` با وضعیت فعال.
- در صورت وجود فیلدهای گروه یا branch، تخصیص آن‌ها.
- بازگرداندن پاسخ موفقیت همراه با timestamp و شناسه اعلان ایجادشده.

</div>### ورودی‌های درخواست

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>title</td><td>string</td><td>بله</td><td>عنوان اعلان</td></tr><tr><td>content</td><td>string</td><td>بله</td><td>متن کامل اعلان برای نمایش</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>نوع اعلان (simple / system / branch)</td></tr><tr><td>receiver\_id</td><td>integer</td><td>خیر</td><td>شناسه کاربر یا اپراتور مقصد</td></tr><tr><td>branch</td><td>integer</td><td>خیر</td><td>شناسه شعبه یا محدوده ارسال</td></tr><tr><td>status</td><td>boolean</td><td>خیر</td><td>وضعیت فعال یا غیرفعال بودن اعلان</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/notif/send
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "title": "هشدار موجودی",
  "content": "موجودی کیف پول شما کمتر از حد مجاز است.",
  "type": "simple",
  "branch": 5,
  "status": true
}
```

### ساختار خروجی

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت موفقیت عملیات</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان واکنش سرور (Unix)</td></tr><tr><td>data.id</td><td>integer</td><td>شناسه اعلان ایجاد‌شده در DB</td></tr><tr><td>data.title</td><td>string</td><td>عنوان اعلان ثبت‌شده</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "meta": {"timestamp": 1732289390},
  "data": {
    "id": 341,
    "title": "هشدار موجودی"
  }
}
```

### نکات امنیتی و کنترل دسترسی

- نیازمند احراز هویت توسط Middleware `authWithJwt`.
- در نسخه فعلی محدودیتی بر اساس نقش (Role) وجود ندارد.
- عدم بررسی sanitize فیلد `content` می‌تواند منجر به آسیب تزریق HTML شود.

### نکات کارایی و عملکرد

- ثبت مستقیم در DB بدون queue یا caching انجام می‌شود.
- در عملکرد بالا تعداد زیاد اعلان‌ها باعث افزایش زمان پاسخ و تحمیل بار I/O بر DB می‌گردد.
- توصیه به اضافه‌کردن سیستم push یا asynchronous job (queue).

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use App\\Models\\Notification;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

### کدهای خطا و پاسخ‌های غیرعادی

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>Missing or invalid input data</td><td>Validation مرحله ورود داده</td></tr><tr><td>1006</td><td>توکن JWT منقضی یا نادرست</td><td>auth middleware</td></tr><tr><td>500</td><td>Database Exception هنگام ثبت اعلان</td><td>DB::insert()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- استفاده از Escaping در محتوای `content`.
- اعمال محدودیت سطح دسترسی ارسال اعلان به فقط `admin`ها یا `super-operator`ها.
- اعمال rate-limit (۵ اعلان در دقیقه).

### پیشنهادهای بهبود

- افزودن ستون `read_at` برای ثبت زمان خوانده‌شدن اعلان.
- افزودن queue جهت ارسال همزمان به چند گیرنده.
- اضافه‌کردن Audit Log برای ثبت هویت ارسال‌کننده.

### ممیزی و لاگ‌ها

- در نسخه فعلی هیچ لاگی برای ثبت ارسال اعلان در جدول `system_logs` وجود ندارد.
- پیشنهاد: استفاده از `SystemLog::dispatch('notification_created')` هنگام ایجاد رکورد.

### جمع‌بندی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%DA%A9%D9%84%DB%8C%D8%AF%DB%8C-%D8%A8%D8%B1%D8%A7%DB%8C-" style="direction: rtl; text-align: justify;">این مسیر کلیدی برای مدیریت و ارسال اعلان در سیستم است اما نیازمند تقویت زیرساخت امنیتی و ممیزی است. در وضعیت فعلی مناسب برای پنل داخلی است ولی برای ارسال real-time یا حجم زیاد اعلان، باید از queue و caching استفاده شود.</div>

# POST /api/v2/dashboard

<div id="bkmrk-dashboard" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" cellspacing="0" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/dashboard</td><td style="direction: ltr; text-align: left;">V2BaseController@dashboard</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">واکشی داده‌های تحلیلی و وضعیت سرویس‌های پایه برای صفحه داشبورد اپراتور</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-dashboard-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C" style="direction: rtl; text-align: justify;">تابع **dashboard** داده‌های اصلی سامانه (وضعیت ارتباط با سرویس‌های وابسته، تعداد آیتم‌های فعال، هشدارهای مرکزی و زمان آخرین همگام‌سازی) را جمع‌آوری و بازگردانی می‌کند: - بررسی صحت اتصال با سرور مرکزی (hub یا service route) از طریق تابع اختصاصی اتصال.
- واکشی مقادیر و وضعیت‌های کلیدی برای اپراتور فعلی.
- در صورت اتصال موفق: بازگرداندن تعداد آیتم‌های فعال در فرمت `active`.
- در صورت شکست اتصال یا خطای ارتباطی، برگرداندن پیام `Lack of connection with the central server`.
- پوشش خطاهای Exception و بازگرداندن جزئیات آن در خروجی جهت بررسی سریع.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبهٔ جاری برای بررسی وضعیت اتصال و سرویس‌های فعال</td></tr><tr><td>operator\_id</td><td>integer</td><td>خیر</td><td>شناسه اپراتور برای ثبت یا تطبیق داده‌های dashboard</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/dashboard
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "branch": 12
}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت کلی ارتباط (true = اتصال موفق)</td></tr><tr><td>time</td><td>integer</td><td>زمان UNIX درخواست</td></tr><tr><td>active</td><td>integer</td><td>تعداد آیتم‌های فعال در سیستم مرکزی</td></tr><tr><td>data\[\]</td><td>array</td><td>داده‌های حاصل از سرویس مرکزی در صورت اتصال موفق</td></tr><tr><td>message</td><td>string</td><td>پیام خطا در صورت قطع ارتباط</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "time": 1732289412,
  "active": 7,
  "data": [{"service":"train","status":"OK"},{"service":"accommodation","status":"OK"}]
}
```

### نکات امنیتی و کنترل دسترسی

- به واسطه `authWithJwt` فقط کاربران تأیید شده JWT مجاز به مشاهده dashboard هستند.
- حاوی داده‌های عملیاتی شعبه است، پس نباید برای کاربران خارج از محیط مدیریتی قابل‌دسترس باشد.
- فاقد رمزنگاری در پاسخ سرویس؛ در ارتباطات بین‌سرویس بهتر است از SSL داخلی استفاده شود.

### نکات کارایی و Performance

- تابع سریع است ولی وابسته به latency ارتباط با سرور مرکزی.
- خطای موقت شبکه می‌تواند باعث تأخیر یا بازگشت پیام خطای "Lack of connection".
- پیشنهاد: پیاده‌سازی retry با backoff صعودی در صورت خطا برای نقطه‌های ارسال داده.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;
- use Exception;

### کدهای خطا و پاسخ‌های Exception

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%AE%D8%B7%D8%A7-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد خطا</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>Database یا ارتباط ناموفق با سرویس مرکزی</td><td>dashboard()</td></tr><tr><td>1006</td><td>JWT غیر معتبر</td><td>authWithJwt()</td></tr><tr><td>500</td><td>Exception عمومی در runtime</td><td>Exception handler</td></tr></tbody></table>

### پیشنهادهای امنیتی

- رمزنگاری داده‌های آماری حساس با AES سمت سرور.
- محدودسازی مقدار بازگشتی برای جلوگیری از افشای وضعیت داخلی سرور.
- فیلتر خروجی بر اساس نقش کاربر (operator vs admin).

### پیشنهادهای بهبود

- افزودن فیلد cache برای جلوگیری از خواندن مکرر در هر درخواست.
- افزودن latency tracker برای شناسایی نقاط ضعیف شبکه.
- نمایش شاخص‌های uptime و CPU usage در نسخه بعدی.

### ممیزی و لاگ‌ها

- در حال حاضر هیچ ممیزی برای ناکامی ارتباط ثبت نمی‌شود.
- پیشنهاد افزودن لاگ با نوع `DashboardConnectionFailed` در جدول `system_logs`.

### جمع‌بندی

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-dashboard-%DB%8C%DA%A9%DB%8C-%D8%A7" style="direction: rtl; text-align: justify;">تابع **dashboard** یکی از نقاط اصلی گزارش وضعیت سیستم است که ارتباط لحظه‌ای و تعداد آیتم‌های فعال را نمایش می‌دهد. ساختار خروجی به اندازه کافی خواناست؛ اما نبود ممیزی و کنترل سطح دسترسی، آن را در محیط‌های production ناایمن می‌سازد. توصیه می‌شود role-based limitation و audit logging برای پایداری enterprise لحاظ گردد.</div>

# POST /api/v2/operator/details

<div id="bkmrk-operator-details" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/operator/details</td><td style="direction: ltr; text-align: left;">V2BaseController@operatorDetails</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت جزئیات اپراتور شامل اطلاعات شعبه، نقش‌ها، تنظیمات و وضعیت فعال‌سازی</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-operatordetails" style="direction: rtl; text-align: justify;">تابع **operatorDetails** با استفاده از شناسه اپراتور از توکن JWT، اطلاعات دقیق اپراتور فعلی را از جداول اصلی استخراج می‌کند: - واکشی اطلاعات از جدول `operators` شامل نام، نام‌کاربری، ایمیل، سطح دسترسی و نقش.
- تطبیق شناسه شعبه مربوطه از جدول `offices` جهت دریافت نام و موقعیت مکانی.
- بررسی تنظیمات مرتبط با پنل از جدول `settings` شامل تم، رنگ پایه، لوگو و favicon.
- درصورت تعریف APIهای خارجی برای شعبه (SMS یا API مرکزی)، افزودن اطلاعات از `application_interface`.
- جمع‌بندی داده‌ها و بازگرداندن ساختار JSON نهایی.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه‌ای که اپراتور در آن فعالیت می‌کند</td></tr><tr><td>operator\_id</td><td>integer</td><td>خیر</td><td>شناسه اپراتور (در JWT موجود است)</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/operator/details
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "branch": 9
}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت موفقیت درخواست</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان پاسخ سرور</td></tr><tr><td>data.profile</td><td>object</td><td>اطلاعات شخصی و کاری اپراتور</td></tr><tr><td>data.office</td><td>object</td><td>مشخصات شعبه</td></tr><tr><td>data.settings</td><td>object</td><td>تم و تنظیمات رابط کاربری</td></tr><tr><td>data.apis</td><td>array</td><td>لیست APIهای فعال برای شعبه</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "meta": { "timestamp": 1732290051 },
  "data": {
    "profile": {
      "id": 51,
      "name": "علیرضا ایرانپور",
      "role": "operator",
      "email": "a.iranpour88@gmail.com"
    },
    "office": {
      "id": 9,
      "name": "دفتر اصفهان",
      "country": "IR"
    },
    "settings": {
      "theme": "dark",
      "base_color": "#212121",
      "favicon": "/media/icons/favicon.ico"
    },
    "apis": [
      { "type": "sms", "service": "melipayamak", "status": 1 },
      { "type": "hub", "url": "https://hub.airplus.app/api/v2" }
    ]
  }
}
```

### نکات امنیتی

- وابسته به توکن JWT معتبر برای شناسایی اپراتور.
- هیچ فیلد رمز یا داده حساس رمزنگاری نشده نباید در پاسخ وجود داشته باشد.
- در ورژن فعلی endpoint سطح دسترسی اپراتور را بدون فیلتر نقش بازمی‌گرداند.

### عملکرد و کارایی

- تابع سبک است و فقط از چند جدول کاربر و تنظیمات خوانش دارد.
- وابستگی به latency اتصال به DB اصلی شعبه در درخواست‌های همزمان زیاد است.
- پیشنهاد: caching ۵ دقیقه‌ای پروفایل هر اپراتور بر اساس JWT.

### وابستگی‌ها

- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;
- use Exception;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT غیر معتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>400</td><td>داده ناقص یا branch نامعتبر</td><td>Input Validation</td></tr><tr><td>500</td><td>خطا در واکشی داده‌ها از DB</td><td>operatorDetails()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- فیلتر نقش برای جلوگیری از مشاهده تنظیمات سایر اپراتورها.
- رمزنگاری سمت سرور برای فیلدهای حساس (email, username).
- ثبت audit برای هر مشاهده پروفایل.

### پیشنهادهای بهبود

- افزودن فیلد `last_login` و `activity_status` برای مانیتورینگ.
- اضافه‌کردن بخش Roles و Permissions به خروجی JSON.
- ادغام با سرویس مرکزی احراز هویت (corporateAuth).

### ممیزی و لاگ‌ها

- در نسخه فعلی هیچ لاگی برای مشاهده پروفایل ثبت نمی‌شود.
- پیشنهاد: ثبت رویداد `ProfileViewed` در جدول `system_logs`.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Foperator%2Fdetai" style="direction: rtl; text-align: justify;">مسیر **/operator/details** برای مدیریت داخلی طراحی شده و اطلاعات کلیدی اپراتور را بدون واسطه برمی‌گرداند. در محیط production نیاز به کنترل دقیق نقش‌ها و محدودسازی پاسخ دارد تا از افشای جزئیات مدیریتی جلوگیری شود.</div>

# POST /api/v2/management/office

<div id="bkmrk-management-office" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/management/office</td><td style="direction: ltr; text-align: left;">V2BaseController@office</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">واکشی مشخصات دفتر و سرویس‌های مرتبط با شعبهٔ فعلی برای نمایش در پنل مدیریت</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-office-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AA%D8%AC%D9%85" style="direction: rtl; text-align: justify;">تابع **office** برای تجمیع اطلاعات اولیهٔ دفتر در داشبورد مدیریتی طراحی شده است: - واکشی اطلاعات از جدول `offices` با استفاده از `branch id` در درخواست JWT.
- دریافت تنظیمات API از جدول `application_interface` مخصوص سرویس‌های فعال شعبه.
- تجمیع داده‌ها و بازگشت در قالب JSON با زمان پاسخ و وضعیت موفقیت.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جهت دریافت اطلاعات دفتر</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/management/office
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "branch": 7
}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت کلی درخواست</td></tr><tr><td>time</td><td>integer</td><td>زمان Unix پاسخ سرور</td></tr><tr><td>data.office</td><td>array</td><td>اطلاعات دقیق دفتر از جدول offices</td></tr><tr><td>data.api</td><td>array</td><td>لیست سرویس‌های فعال در جدول application\_interface شامل URL و نوع سرویس</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "time": 1732290287,
  "data": {
    "office": [
      {
        "id": 7,
        "en_title": "Isfahan Branch",
        "fa_title": "دفتر مرکزی اصفهان",
        "country": "IR",
        "credit_limit": 20000000,
        "base_online": 12000000
      }
    ],
    "api": [
      {
        "type": "sms",
        "url": "https://soap.melipayamak.com",
        "username": "isf-admin",
        "status": 1
      },
      {
        "type": "accounting",
        "url": "https://hub.airplus.app/v2/accounting",
        "status": 1
      }
    ]
  }
}
```

### نکات امنیتی و احراز هویت

- احراز هویت حتماً باید توسط middleware `authWithJwt` انجام شود.
- در نسخه فعلی هیچ کنترل سطح دسترسی برای مشاهده دفاتر دیگر وجود ندارد.
- پیشنهاد: اعمال Role-based restriction (مانند ADMIN, FINANCE, SUPPORT).

### عملکرد و Performance

- تابع سبک و سریع است زیرا فقط از دو جدول DB خوانش دارد.
- در بارهای سنگین یا محیط‌های multi-branch، پیشنهاد می‌شود cache 5 دقیقه‌ای برای داده‌های دفتر فعال شود.
- در پاسخ فعلی داده‌ها بدون pagination بازگردانده می‌شوند.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;
- use Exception;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT نامعتبر یا منقضی</td><td>authWithJwt</td></tr><tr><td>400</td><td>branch ارسال نشده</td><td>Input Validation</td></tr><tr><td>500</td><td>خطای داخلی DB یا Exception ناشناخته</td><td>office()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اعمال تعیین سطح دسترسی مشاهده به نقش‌های مدیریتی.
- پنهان‌سازی فیلدهای حساس مانند شناسه کاربری و رمزهای اتصال API.
- رمزنگاری سمت سرور برای آیتم‌های `api.username` و `api.data`.

### پیشنهادهای بهبود

- افزودن فیلد `storage_usage` برای مدیریت فضا و فایل‌های شعبه.
- عدم نیاز به فراخوانی مستقیم DB در هر درخواست (استفاده از Redis cache).
- افزودن قابلیت سرچ و فیلتر بر اساس نوع سرویس در بخش api.

### ممیزی و لاگ‌ها

- هیچ لاگی برای مشاهده اطلاعات دفتر ثبت نمی‌شود.
- پیشنهاد: ثبت رویداد `BranchViewed` در جدول `system_logs` همراه با IP و User-Agent.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fmanagement%2Foff" style="direction: rtl; text-align: justify;">مسیر **/management/office** بستری برای بررسی سریع وضعیت شعبه و سرویس‌های فعال است. منطق عملکرد ساده اما امنیت سطح پایین دارد؛ پیشنهاد قطعی اجرای **Role-based Access + Audit Logging** جهت جلوگیری از افشای داده‌های دفاتر دیگر در محیط production می‌باشد.</div>

# GET /api/v2/panel/groups/get

<div id="bkmrk-panel-groups-get" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/panel/groups/get</td><td style="direction: ltr; text-align: left;">V2BaseController@smsPanelGetGroups</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت گروه‌های دفترچه مخاطبین سرویس پیامک فعال (SMS Panel) برای شعبه فعلی</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-smspanelgetgrou" style="direction: rtl; text-align: justify;">تابع **smsPanelGetGroups** برای اتصال به سرویس پیامک Melipayamak (API رسمی شرکت پرداخت اول) طراحی شده است: - ابتدا اطلاعات اتصال از جدول `application_interface` واکشی می‌شود (فیلدهای `service`، `username`، `password` و `data->sender`).
- در صورت یافتن تنظیمات معتبر، شیء API با `MelipayamakApi` ساخته می‌شود و به متد `contacts()` متصل می‌گردد.
- با فراخوانی متد `getGroups()`، لیست گروه‌های موجود در دفترچه مخاطبین واکشی می‌شود.
- خروجی به آرایهٔ قابل‌خواندن شامل شناسه گروه، عنوان، توضیحات، وضعیت، تعداد مخاطبین و اطلاعات زیرگروه تبدیل می‌گردد.
- در صورت عدم یافتن تنظیمات معتبر یا وجود خطا، مسیر مقدار خالی یا خطای سطح ۴۰۰ بازمی‌گرداند.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جهت تعیین سرویس فعال پیامک</td></tr></tbody></table>

#### نمونه درخواست:

```
GET /api/v2/panel/groups/get
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

?branch=5
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>شناسه یکتا گروه مخاطبین</td></tr><tr><td>user\_id</td><td>integer</td><td>شناسه کاربر در سرویس پیامک</td></tr><tr><td>parent</td><td>integer|null</td><td>شناسه زیرگروه یا گروه مادر</td></tr><tr><td>title</td><td>string</td><td>عنوان گروه</td></tr><tr><td>description</td><td>string</td><td>توضیحات گروه</td></tr><tr><td>count</td><td>integer</td><td>تعداد کل مخاطبین موجود در گروه</td></tr><tr><td>status</td><td>integer</td><td>وضعیت فعال بودن گروه (۱=فعال، ۰=غیرفعال)</td></tr><tr><td>child.show</td><td>boolean</td><td>نمایش زیرگروه‌ها برای کودک‌ها (true/false)</td></tr><tr><td>child.count</td><td>integer</td><td>تعداد زیرگروه‌ها</td></tr></tbody></table>

#### نمونه پاسخ:

```
[
  {
    "id": 122,
    "user_id": 931,
    "parent": null,
    "title": "مشتریان ویژه",
    "description": "VIP Contacts Reserved",
    "count": 312,
    "status": 1,
    "child": {
      "show": true,
      "count": 4
    }
  },
  {
    "id": 127,
    "user_id": 931,
    "parent": 122,
    "title": "آژانس همکاران",
    "description": "",
    "count": 151,
    "status": 1,
    "child": {
      "show": false,
      "count": 0
    }
  }
]
```

### نکات امنیتی

- وابسته به JWT معتبر و فعال بودن سرویس پیامکی شعبه.
- در صورت نبود سرویس پیامک برای شعبه، پاسخ خالی یا کد خطای ۴۰۴ باید بازگردد.
- عدم رمزنگاری داده خروجی به‌دلیل عمومی بودن داده مخاطبین (اما بهتر است برای داده‌های شرکت خصوصی رمزگذاری شود).

### عملکرد و Performance

- تابع سبک است اما وابسته به latency سرویس SOAP خارجی (Melipayamak).
- پاسخ معمولاً طی ۱ تا ۳ ثانیه بسته به حجم گروه‌ها بازگردانده می‌شود.
- پیشنهاد: cache گروه‌ها در Redis برای مدت ۱۰ دقیقه جهت جلوگیری از تکرار درخواست به سرویس خارجی.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use App\\Lib\\MelipayamakApi;
- use Illuminate\\Http\\Request;
- use Exception;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>404</td><td>تنظیمات سرویس پیامک برای این شعبه یافت نشد</td><td>smsPanelGetGroups()</td></tr><tr><td>500</td><td>خطای اتصال با سرویس Melipayamak یا پاسخ نامعتبر SOAP</td><td>MelipayamakApi::contacts()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- ماسک کردن اطلاعات کاربر سرویس پیامک (مانند password) در سطح API داخلی.
- افزودن audit برای هر فراخوانی دفترچه مخاطبین از پنل.
- استفاده از اتصال HTTPS برای ارتباط مستقیم با Melipayamak API.

### پیشنهادهای بهبود

- افزودن endpoint مکمل برای واکشی اعضای هر گروه (GET /panel/groups/{group\_id}/members).
- نمایش تعداد پیام‌های ارسال‌شده یا وضعیت فعال در خروجی.
- امکان فیلتر بر اساس عنوان، وضعیت و تاریخ ایجاد گروه.

### ممیزی و لاگ‌ها

- در نسخه فعلی لاگ ثبت درخواست انجام نمی‌شود.
- پیشنهاد: ذخیره‌ی داده تماس در جدول `system_logs` با نوع رخداد `ViewSMSGroups`.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fpanel%2Fgroups%2Fg" style="direction: rtl; text-align: justify;">مسیر **/panel/groups/get** برای هماهنگی مستقیم با API پیامکی استفاده می‌شود. هرچند پاسخ سبک و خوانا دارد، اما به دلیل عدم کش داخلی (caching) در نسخه فعلی می‌تواند موجب افزایش latency سیستم شود. در نسخه‌ی حرفه‌ای باید caching و محدودیت فراخوانی Request Rate در هر شعبه اضافه گردد.</div>

# GET /api/v2/panel/bulk/receptions

<div id="bkmrk-panel-bulk-receptions" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/panel/bulk/receptions</td><td style="direction: ltr; text-align: left;">V2BaseController@smsPanelGetBulkReceptions</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست پیام‌های انبوه ارسال‌شده در پنل پیامک فعال برای شعبه فعلی</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-smspanelgetbulk" style="direction: rtl; text-align: justify;">تابع **smsPanelGetBulkReceptions** به سرویس پیامکی متصل شده و داده‌های آماری پیام‌های گروهی ارسال‌شده را بازیابی می‌کند: - با استفاده از تنظیمات موجود در جدول `application_interface` سرویس `sms` فعال شعبه یافت می‌شود.
- متد `getBulkReceptions()` از کلاس SDK `MelipayamakApi` فراخوانی شده و فهرست پیام‌های گروهی بازگردانده می‌شود.
- هر پیام به آرایه‌ای شامل شناسه، فرستنده، متن، تعداد گیرندگان، زمان ارسال و درصد موفقیت تبدیل می‌گردد.
- در نهایت، داده‌ها به صورت JSON با وضعیت موفقیت و زمان یونیکس بازگردانده می‌شوند.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جهت بازیابی تنظیمات سرویس پیامکی</td></tr></tbody></table>

#### نمونه درخواست:

```
GET /api/v2/panel/bulk/receptions
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

?branch=5
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>bulk\_id</td><td>integer</td><td>شناسه یکتا کمپین ارسال پیامک گروهی</td></tr><tr><td>sender</td><td>string</td><td>شماره فرستنده پیام</td></tr><tr><td>text</td><td>string</td><td>متن پیام ارسال‌شده</td></tr><tr><td>count</td><td>integer</td><td>تعداد کل گیرندگان پیام</td></tr><tr><td>delivered</td><td>integer</td><td>تعداد پیام‌های موفق تحویل‌شده</td></tr><tr><td>failed</td><td>integer</td><td>تعداد پیام‌های ناموفق یا رد‌شده</td></tr><tr><td>datetime</td><td>string</td><td>زمان ارسال کمپین (فرمت ISO)</td></tr><tr><td>success\_rate</td><td>float</td><td>نسبت موفقیت تحویل پیام‌ها (درصد)</td></tr></tbody></table>

#### نمونه پاسخ:

```
{
  "status": true,
  "meta": { "timestamp": 1732290419 },
  "items": [
    {
      "bulk_id": 9021,
      "sender": "5000400008851",
      "text": "تخفیف ویژه پروازهای داخلی فقط امروز!",
      "count": 1320,
      "delivered": 1287,
      "failed": 33,
      "datetime": "2025-11-22T08:15:00Z",
      "success_rate": 97.5
    },
    {
      "bulk_id": 8904,
      "sender": "5000400008851",
      "text": "اعلان تغییر قوانین رزرو آنلاین",
      "count": 698,
      "delivered": 684,
      "failed": 14,
      "datetime": "2025-11-20T06:43:00Z",
      "success_rate": 97.9
    }
  ]
}
```

### نکات امنیتی

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

### عملکرد و کارایی

- تابع سریع اما وابسته به latency سرویس خارجی.
- در فراخوانی‌های متوالی توصیه می‌شود cache محدود Redis برای مدت ۵ دقیقه ایجاد گردد.
- مدت پاسخ معمولی بین ۲ تا ۳ ثانیه (بسته به حجم داده).

### وابستگی‌ها

- use App\\Lib\\MelipayamakApi;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Exception;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>404</td><td>عدم یافتن تنظیمات سرویس پیامک برای شعبه</td><td>smsPanelGetBulkReceptions()</td></tr><tr><td>500</td><td>خطای پاسخ از سرویس Melipayamak یا SOAP Data Invalid</td><td>MelipayamakApi::getBulkReceptions()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اعمال Role-based Access فقط برای کاربرانی با نقش `sms_manager`.
- ثبت لاگ برای هر مشاهده داده پیامک گروهی (type: `ViewBulkSMS`).
- پنهان‌سازی متن پیام در حالت عمومی؛ فقط در حالت administrative نمایش داده شود.

### پیشنهادهای بهبود

- افزودن فیلدهای `cost` و `provider_response_time` برای تحلیل اقتصادی ارسال پیام‌ها.
- افزودن endpoint فیلتر بر اساس بازه زمانی و درصد موفقیت.
- ایجاد Caching مبتنی بر Redis با prefetch خودکار داده‌ها برای شعب فعال.

### ممیزی و لاگ‌ها

- در نسخه فعلی هیچ ممیزی خروجی برای مشاهده پیام‌های انبوه انجام نمی‌شود.
- پیشنهاد: ثبت در جدول `system_logs` با رویداد `BulkSMSViewed`.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fpanel%2Fbulk%2Frec" style="direction: rtl; text-align: justify;">مسیر **/panel/bulk/receptions** به عنوان بخش گزارش پیام‌های انبوه پنل پیامکی عمل می‌کند. داده‌های خروجی ساده و بدون نقش‌بندی است و در نسخه فعلی به عنوان نقطه ضعف امنیتی شناخته می‌شود. در ساختار Enterprise لازم است احراز نقش و ساختار audit کامل افزوده گردد و caching دوره‌ای برای سنجش عملکرد سرویس پیامک فعال شود.</div>

# POST /api/v2/panel/bulk/add

<div id="bkmrk-panel-bulk-add" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/panel/bulk/add</td><td style="direction: ltr; text-align: left;">V2BaseController@smsPanelAddBulk</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">ارسال پیامک‌های انبوه به گروه یا فهرست مخاطبین تعیین‌شده از سمت پنل پیامکی شعبه</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-smspaneladdbulk" style="direction: rtl; text-align: justify;">تابع **smsPanelAddBulk** از طریق اتصال به API سرویس `Melipayamak` فرآیند ارسال انبوه پیامک را انجام می‌دهد: - اطلاعات اتصال سرویس از جدول `application_interface` برای نوع `sms` و شعبه فعلی استخراج می‌شود.
- با ساخت شیء `new MelipayamakApi($username, $password)` به توابع `send()` یا `bulkSend()` متصل می‌شود.
- پارامترهای ورودی مانند شماره ارسال، متن پیام و شناسه گروه‌ها در بدنه درخواست تجمیع می‌شود.
- در صورت موفقیت، شناسهٔ کمپین (bulk\_id) از سمت سرویس دریافت و در جدول لاگ داخلی (در صورت وجود) ذخیره می‌گردد.
- در صورت خطا، کد و پیام خطای SOAP در خروجی JSON بازگردانده می‌شود.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه جهت شناسایی سرویس فعال پیامک</td></tr><tr><td>text</td><td>string</td><td>بله</td><td>متن پیام قابل ارسال به مخاطبین</td></tr><tr><td>sender</td><td>string</td><td>خیر</td><td>شماره فرستنده (پیش‌فرض از تنظیمات سرویس استخراج می‌شود)</td></tr><tr><td>contacts</td><td>array\[int\]</td><td>بله</td><td>آرایه‌ای از شماره‌ها یا شناسه‌های گروه مخاطبین برای ارسال</td></tr><tr><td>scheduleTime</td><td>string</td><td>خیر</td><td>زمان‌بندی ارسال در صورت نیاز (فرمت ISO یا Jalali)</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/panel/bulk/add
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "branch": 5,
  "text": "🎉 پروازهای ویژه نوروز با تخفیف تا ۳۰٪!",
  "contacts": [ "09351234567", "09125554433" ],
  "scheduleTime": "2025-03-01T10:00:00Z"
}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>موفقیت یا شکست عملیات ارسال</td></tr><tr><td>bulk\_id</td><td>integer</td><td>شناسه کمپین ارسال (توسط سرویس پیامکی تولید می‌شود)</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان یونیکس درخواست یا پاسخ</td></tr><tr><td>error</td><td>object|null</td><td>در صورت شکست، شامل پیام و کد خطا است</td></tr></tbody></table>

#### نمونه پاسخ موفق:

```
{
  "status": true,
  "bulk_id": 1115,
  "meta": {
    "timestamp": 1732290514
  }
}
```

#### نمونه پاسخ خطا:

```
{
  "status": false,
  "error": {
    "code": 500,
    "message": "Error connecting to Melipayamak API"
  },
  "meta": {
    "timestamp": 1732290514
  }
}
```

### نکات امنیتی

- ارسال فقط برای شعبه معتبر بر اساس JWT امکان‌پذیر است.
- خطر ارسال پیام‌های غیرمجاز توسط اپراتورهای شعبه بدون نقش محدودکننده وجود دارد.
- باید مکانیزم `RoleRestriction(sms_sender)` فعال شود تا فقط کاربران مجاز بتوانند از این endpoint استفاده کنند.

### نکات عملکردی

- ارسال دسته‌ای بیش از 1000 شماره باعث تاخیر 5–7 ثانیه‌ای در پاسخ می‌شود.
- پیشنهاد: صف‌بندی درخواست‌ها با `Queue::dispatch(SMSBulkJob)` برای کاهش latency.
- در حال حاضر caching Redis وجود ندارد، ولی برای گزارش‌های تکراری بهتر است اضافه شود.

### وابستگی‌ها

- use App\\Lib\\MelipayamakApi;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Exception;
- use Carbon\\Carbon;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>2001</td><td>درخواست فاقد شماره ارسال معتبر</td><td>smsPanelAddBulk()</td></tr><tr><td>500</td><td>خطای ارتباط یا پاسخ نامعتبر از سرویس Melipayamak</td><td>MelipayamakApi::bulkSend()</td></tr></tbody></table>

### پیشنهادهای امنیتی

- لاگ‌کردن هر کمپین ارسالی با `SystemLog::dispatch(["type" => "BulkSMSSent", ...])`.
- رمزنگاری متون پیام قبل از ذخیره در دیتابیس داخلی.
- افزودن کنترل ضد هرزنامه (anti-spam) برای جلوگیری از ارسال انبوه در بازه کوتاه.

### پیشنهادهای بهبود

- افزودن پارامتر `bulk_label` برای نام‌گذاری کمپین‌ها در گزارشات مدیریتی.
- افزودن حالت test-mode برای ارسال به تعداد محدود مخاطب جهت تست.
- افزودن پاسخ تکمیلی با تعداد پیام موفق و ناموفق.

### ممیزی و لاگ‌ها

- پیشنهاد ثبت هر ارسال در جدول `sms_audit` با فیلدهای `branch, bulk_id, sender, count, status`.
- در نسخه فعلی لاگ internal فعال نیست.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fpanel%2Fbulk%2Fadd" style="direction: rtl; text-align: justify;">مسیر **/panel/bulk/add** نقطه ورود برای ارسال کمپین‌های پیامکی است که با وجود عملکرد ساده، به دلیل عدم وجود نقش‌بندی و ممیزی می‌تواند به سوء‌استفاده عملیاتی منجر شود. نسخه‌ای که در Enterprise اجرا شود باید حتماً شامل تأیید سطح دسترسی، audit log لحظه‌ای و ثبت پیام‌های ارسالی به‌صورت رمزگذاری‌شده باشد.</div>

# POST /countries-cities/get

<div id="bkmrk-countries-cities-get" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/countries-cities/get</td><td style="direction: ltr; text-align: left;">V2BaseController@getCountiesOrCities</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست کشورها یا شهرها براساس نوع درخواست (`type`)، با امکان جست‌وجو و فیلتر وابسته به شناسه والد</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-getcountiesorci" style="direction: rtl; text-align: justify;">تابع **getCountiesOrCities** مسئول واکشی اطلاعات جغرافیایی کشورها و شهرها است و رفتار آن براساس فیلد `type` (مقدار `country` یا `city`) تغییر می‌کند: - ابتدا مقدار `type` بررسی می‌شود تا مشخص شود داده‌های کشوری یا شهری باید خوانده شود.
- در صورت حضور `parent_id`، فقط شهرهای مربوط به آن کشور واکشی می‌شوند.
- در صورت ارسال فیلد `search`، شرط فیلتر با عملگر `LIKE` روی کلیدهای نام‌های فارسی و انگلیسی اجرا می‌شود.
- نتیجه نهایی به‌صورت آرایه‌ای از اشیاء شامل شناسه، عنوان فارسی/انگلیسی و والد (در صورت وجود) بازگردانده می‌شود.
- در صورت خطا یا فقدان داده، پاسخ با وضعیت `status:false` و شرح خطا برگردانده می‌شود.

</div>### ورودی‌ها (Request Fields)

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D9%84" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام فیلد</td><td>نوع داده</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>بله</td><td>مشخص‌کننده سطح داده مورد نیاز: مقدار `country` برای دریافت کشورها و `city` برای شهرها.</td></tr><tr><td>parent\_id</td><td>integer</td><td>خیر</td><td>در حالت `city`، شناسه کشور والد جهت محدودسازی نتایج.</td></tr><tr><td>search</td><td>string</td><td>خیر</td><td>عبارت جست‌وجو جهت فیلتر نام شهر یا کشور.</td></tr><tr><td>branch</td><td>integer</td><td>بله</td><td>شناسه شعبه برای اعتبارسنجی JWT و سطح دسترسی داده.</td></tr></tbody></table>

#### نمونه درخواست:

```
POST /api/v2/countries-cities/get
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "type": "city",
  "parent_id": 118,
  "search": "تهران",
  "branch": 5
}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت موفقیت عملیات</td></tr><tr><td>items\[\].id</td><td>integer</td><td>شناسه یکتای کشور یا شهر</td></tr><tr><td>items\[\].title.fa</td><td>string</td><td>نام فارسی کشور یا شهر</td></tr><tr><td>items\[\].title.en</td><td>string</td><td>نام انگلیسی کشور یا شهر</td></tr><tr><td>items\[\].category.title.fa</td><td>string|null</td><td>نام فارسی کشور والد (برای شهرها)</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان تولید پاسخ (یونیکس)</td></tr></tbody></table>

#### نمونه پاسخ موفق:

```
{
  "status": true,
  "items": [
    {
      "id": 548,
      "title": { "fa": "تهران", "en": "Tehran" },
      "category": { "title": { "fa": "ایران", "en": "Iran" } }
    }
  ],
  "meta": { "timestamp": 1750668858 }
}
```

#### نمونه پاسخ خطا:

```
{
  "status": false,
  "error": { "code": 400, "message": "پارامتر type نامعتبر است." },
  "meta": { "timestamp": 1750668869 }
}
```

### نکات امنیتی

- اعتبارسنجی JWT از طریق middleware `authWithJwt` انجام می‌شود.
- در صورت ارسال type=city، باید چک شود که شعبه کاربر مجاز به دسترسی داده کشور والد باشد.
- در نسخه فعلی کنترل سطح دسترسی برای cross-branch وجود ندارد و باید در Enterprise اضافه شود.

### نکات عملکردی

- پاسخ بدون pagination است و ممکن است در حالت city حجم بالا داشته باشد.
- پیشنهاد: ذخیره cached داده‌ها در Redis با کلیدهای `geo:countries` و `geo:cities:{countryId}`.
- TTL توصیه‌شده برای داده‌های استاتیک: 3600 ثانیه.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT منقضی یا نامعتبر</td><td>authWithJwt</td></tr><tr><td>1001</td><td>پارامتر type نامعتبر یا خالی است</td><td>getCountiesOrCities()</td></tr><tr><td>500</td><td>خطا در واکشی داده‌ها از پایگاه‌داده</td><td>DB::table('countries')</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اضافه کردن کنترل سطح شعبه برای جلوگیری از تزریق parent\_id غیرمجاز.
- ثبت لاگ در SystemLog با نوع `ReadGeoData` برای هر درخواست.
- افزودن rate-limit برای جلوگیری از دسترسی مکرر و حملات enumeration.

### پیشنهادهای بهبود

- افزودن صفحه‌بندی (pagination) برای تعداد زیاد شهرها.
- پشتیبانی از پارامتر `locale` برای ترجمه عنوان‌ها بر اساس زبان کاربر.
- افزودن فیلد `iso_code` برای کشورها جهت استانداردسازی.

### ممیزی و لاگ‌ها

- لاگ‌کردن درخواست هر کاربر شامل فیلدهای `operator_id`، `branch_id` و `type`.
- سطح لاگ توصیه‌شده: **Info**.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fcountries-citi" style="direction: rtl; text-align: justify;">مسیر **/countries-cities/get** سازوکار اصلی دریافت داده‌های جغرافیایی است. با وجود سادگی، نبود کنترل سطح دسترسی بین شعب می‌تواند نقطه ضعف امنیتی باشد. در نسخهٔ Enterprise لازم است مکانیزم‌های cache و rate-limit، audit log و کنترل اعتبار parent_id حتماً فعال شوند تا پاسخ سریع و ایمن بماند.</div>

# GET /api/v2/settings/index/{type}

<div id="bkmrk-settings-index" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<table border="1" cellpadding="6" id="bkmrk-method-endpoint-cont" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/settings/index/{type}</td><td style="direction: ltr; text-align: left;">V2BaseController@settingsIndex</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت تنظیمات شاخه، شامل کشور، استان و شهر مربوط به شعبه، براساس نوع تنظیم `{type}`.</td></tr></tbody></table>

### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-settingsindex-%D9%BE" style="direction: rtl; text-align: justify;">تابع **settingsIndex** پارامتر `{type}` را از URL دریافت کرده و بر اساس آن، تنظیمات اصلی شعبه را از جدول `offices` واکشی می‌کند. اگر مقدار `type` برابر با “branch” باشد، سه سطح داده جغرافیایی کشور، استان و شهر از جداول پایگاه‌داده خوانده می‌شود: - ابتدا رکورد شعبه از `offices` واکشی می‌شود.
- در صورت داشتن مقدار `country`، تلاش می‌کند داده را از Redis با کلید `countries:{id}` بخواند؛ در صورت نبود، از DB بازیابی و cache می‌کند.
- در ادامه `state` و `city` نیز از جداول ایالتی و شهری دریافت می‌شوند.
- در پایان داده‌ها در قالب JSON دارای شناسه، نام فارسی و انگلیسی هر سطح برگردانده می‌شوند.

</div>### پارامترهای ورودی

<table border="1" cellpadding="6" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>URL Path</td><td>بله</td><td>نوع تنظیم مورد نیاز (مثلاً `branch`).</td></tr><tr><td>branch</td><td>integer</td><td>JWT Payload</td><td>بله</td><td>شناسه شعبه جهت واکشی اطلاعات مرتبط از جدول `offices`.</td></tr></tbody></table>

#### نمونه درخواست:

```
GET /api/v2/settings/index/branch
Authorization: Bearer {JWT_TOKEN}
```

### خروجی (Response)

<table border="1" cellpadding="6" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>country.title.fa</td><td>string</td><td>نام فارسی کشور</td></tr><tr><td>country.title.en</td><td>string</td><td>نام انگلیسی کشور</td></tr><tr><td>state.title.fa</td><td>string</td><td>نام فارسی استان</td></tr><tr><td>city.title.fa</td><td>string</td><td>نام فارسی شهر</td></tr><tr><td>status</td><td>boolean</td><td>وضعیت موفقیت عملیات</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان تولید پاسخ (یونیکس)</td></tr></tbody></table>

#### نمونه پاسخ موفق:

```
{
  "status": true,
  "settings": {
    "country": {
      "title": { "fa": "ایران", "en": "Iran" },
      "id": 118, "iso": "IR"
    },
    "state": {
      "id": 31,
      "title": { "fa": "یزد", "en": "Yazd" }
    },
    "city": {
      "id": 548,
      "title": { "fa": "احمدآباد", "en": "Ahmadabad" }
    }
  },
  "meta": { "timestamp": 1750668858 }
}
```

### نکات امنیتی

- تأیید هویت با middleware `authWithJwt` انجام می‌شود.
- نباید اجازه داد کاربران سایر شعب به تنظیمات شعبه‌ای دسترسی داشته باشند.
- در نسخه فعلی، کنترل سطح دسترسی بین شعبه‌ای (`branch cross-access`) پیاده‌سازی نشده است.

### نکات عملکردی

- در هر درخواست سه کوئری به جداول `countries`، `states` و `cities` اجرا می‌شود.
- پیشنهاد: کش Redis برای هر سه سطح با TTL حداقل 6 ساعت.
- درخواست GET به Redis قبل از DB برای کاهش latency.

### وابستگی‌ها

- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;

### کدهای خطا

<table border="1" cellpadding="6" id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-1006" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>1006</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>authWithJwt</td></tr><tr><td>1002</td><td>نوع تنظیم {type} نامعتبر یا پشتیبانی‌نشده است</td><td>settingsIndex()</td></tr><tr><td>500</td><td>خطا در اتصال به Redis یا پایگاه‌داده</td><td>DB/Redis</td></tr></tbody></table>

### پیشنهادهای امنیتی

- اضافه‌کردن Role-based Validation برای اپراتورهای هر شعبه.
- ثبت لاگ دسترسی به تنظیمات در SystemLog با نوع `ReadSettings`.
- افزودن Rate-limit برای درخواست‌های GET متوالی از یک JWT.

### پیشنهادهای بهبود

- افزودن پشتیبانی از نوع‌های تنظیم دیگر (`hub`، `application`، `office`).
- افزودن ساختار Metadata شامل زمان آخرین بروزرسانی.
- نمایش پرچم یا نماد کشور در پاسخ برای UX بهتر در فرانت‌اند.

### ممیزی و لاگ‌ها

- ثبت لاگ هر درخواست در جدول `system_log` با فیلدهای `operator_id`، `branch_id` و `type`.
- سطح لاگ توصیه‌شده: **Info** برای درخواست‌های موفق و **Warning** برای شکست‌های Redis.

### جمع‌بندی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%2Fsettings%2Findex" style="direction: rtl; text-align: justify;">مسیر **/settings/index/{type}** برای دریافت داده‌های تنظیمات جغرافیایی شعبه استفاده می‌شود و یکی از پایه‌های ساخت فرم پیکربندی در داشبورد مدیریتی است. ضعف اصلی در حال حاضر نبود تفکیک Role بین شعب است. در نسخه Enterprise نیاز است Redis caching و Audit log فعال و کنترل سطح دسترسی دقیق اعمال گردد تا فرایند پاسخ‌دهی هم سریع‌تر و هم ایمن‌تر شود.</div>

# PUT /settings/index/{type}

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">PUT</td><td style="direction: ltr; text-align: left;">/api/v2/settings/index/{type}</td><td style="direction: ltr; text-align: left;">V2BaseController@settingsUpdate</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">به‌روزرسانی تنظیمات شاخه موردنظر (نوع مشخص‌شده در پارامتر `{type}`) و ذخیره در دیتابیس مربوطه.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-settingsupdate-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **settingsUpdate** با دریافت `{type}` از URL مسیر، داده جدید تنظیمات شاخه (Branch) را بر اساس نوع تنظیم ذخیره می‌کند. منطق داخلی تابع بر پایه نوع تنظیم به سه شاخه تقسیم می‌شود: - **type = "application"** → بروزرسانی تنظیمات UI/Theme شعبه در جدول `offices`.
- **type = "accounting"** → ثبت داده‌های مالی و محدودیت‌های اعتبار در جدول `accounting_titles`.
- **type = "hub"** → به‌روزرسانی مارکاپ‌های سطحی در جدول `hub_markups` با استفاده از متد `updateOrInsert()`.

در آخر، داده‌ها پس از تغییر در DB با کلید شعبه (`branch`) برمی‌گردند تا فرانت‌اند از صحت اعمال تغییرات اطمینان حاصل کند.</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>URL Path</td><td>بله</td><td>نوع تنظیم موردنظر (application, accounting, hub).</td></tr><tr><td>branch</td><td>integer</td><td>JWT Payload</td><td>بله</td><td>شناسه شعبه فعال برای اعمال تنظیمات.</td></tr><tr><td>markups</td><td>array</td><td>Request Body</td><td>اختیاری (در hub)</td><td>آرایه دسته‌بندی مارکاپ‌ها در سطوح و نوع‌های مختلف پرواز و اقامت.</td></tr><tr><td>theme / style / base\_color</td><td>string</td><td>Request Body</td><td>اختیاری (در application)</td><td>ویژگی‌های ظاهری واسط کاربری تنظیمات شعبه.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
PUT /api/v2/settings/index/hub
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "markups": {
    "flight": [
      {
        "level": 1,
        "markups": {
          "airplus": { "type": "percent", "value": 4, "discount": { "type": "percent", "value": 1 } },
          "accommodation": { "type": "currency", "value": 50000, "discount": { "type": "currency", "value": 0 } }
        }
      }
    ]
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>status</td><td>boolean</td><td>نتیجه موفقیت عملیات.</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان یونیکس پاسخ API.</td></tr><tr><td>updated</td><td>object</td><td>مقادیر تنظیمات بروزرسانی‌شده برای شعبه.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "status": true,
  "updated": {
    "branch": 5,
    "type": "hub",
    "count": 4,
    "meta": {
      "timestamp": 1750668952
    }
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86-%D8%B4%D8%B9%D8%A8%D9%87-%D9%85%DB%8C%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربران شعبه می‌توانند تنظیمات همان شعبه را تغییر دهند.
- پارامتر `branch` باید از JWT خوانده شود نه از بدنه درخواست.
- در مسیر PUT نباید مقادیر markups خالی ارسال شود — در غیر این صورت سطوح قبلی پاک می‌شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%D8%AA%D8%AF-updateorinsert%28%29" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- متد `updateOrInsert()` برای درج یا بروزرسانی رکوردهای `hub_markups` استفاده می‌شود — تعداد کوئری‌ها به ازای هر سطح برابر با تعداد آیتم‌های markups است.
- پیشنهاد: فعال‌سازی کش Redis برای داده‌های UI شاخه (type=application).
- بهتر است TTL کش برای داده‌های hub حداقل ۱۲ ساعت باشد، چون تنظیم مارکاپ‌ها کم‌تغییرند.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\CronController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-404-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>404</td><td>نوع تنظیم {type} نامعتبر یا داده‌های ارسالی ناقص است.</td><td>settingsUpdate()</td></tr><tr><td>500</td><td>خطا در اتصال به Redis یا در عملیات DB.</td><td>DB Facade</td></tr><tr><td>400</td><td>درخواست فاقد احراز هویت JWT معتبر.</td><td>authWithJwt Middleware</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%B3%D8%B7%D8%AD-%D8%AF%D8%B3%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن فیلتر سطح دسترسی بر اساس Role برای اپراتورها (مثلاً فقط مدیران شعبه بتوانند PUT انجام دهند).
- ثبت عملیات تغییر تنظیمات در لاگ با سطح `AdminWrite`.
- رمزنگاری مقادیر حساس در تنظیمات حسابداری.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%AA%D9%81%DA%A9%DB%8C%DA%A9-endpoint-%D8%A8%D8%B1%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تفکیک endpoint برای هر نوع تنظیم به صورت مستقل (مثلاً `/settings/hub`, `/settings/application`).
- اضافه کردن Validation سمت سرور با Laravel Validation Rules برای هر بخش تنظیم.
- افزودن خروجی diff-resolved جهت بررسی تغییرات هنگام بروزرسانی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%A8%D8%A7-%D9%86%D9%88%D8%B9-updat" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ با نوع `UpdateSettings`، شامل فیلدهای `operator_id`، `branch_id`، `type` و مقدار `diff`.
- پیشنهاد سطح لاگ: **Admin** برای تغییرات نوع hub، **Info** برای application.

</div>### جمع‌بندی

<div id="bkmrk-settings-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">مسیر **PUT /settings/index/{type}** مسئول بروزرسانی تنظیمات شاخه‌هاست و سه نوع تنظیمات سیستم Interface، مالی، و مرکز پرواز (hub) را شامل می‌شود. برای محیط‌های Production باید مکانیزم cache و audit دقیقی فعال شود تا عملکرد سریع و قابل‌ردگیری تضمین گردد.</div></div>

# GET /base/accommodations/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/base/accommodations/list</td><td style="direction: ltr; text-align: left;">V2BaseController@accommodationsList</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">دریافت لیست اقامتگاه‌ها (هتل‌ها) براساس فیلترهای جغرافیایی و ویژگی‌های پیشرفته، همراه با جزئیات تأمین‌کننده و رسانه.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-accommodationsl" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **accommodationsList** از پارامتر JSON در بدنه درخواست استفاده می‌کند تا جزئیات فیلتر و صفحه‌بندی را تعیین کند. سپس فیلترهایی بر اساس کشور، استان، شهر، امتیاز و جستجوی عمومی (با کلید `r`) اعمال کرده و نتیجه را از جدول `hotels` استخراج می‌کند.   
منطق کلی تابع: - خواندن پارامتر JSON از بدنه درخواست: `$Data = json_decode($request->get('json'));`
- مدیریت صفحه‌بندی با فیلدهای `start` و `length`.
- اعمال فیلترهای چندلایه (کشور، استان، شهر، درجه و عبارت جستجو).
- دریافت اقامتگاه‌های فعال `where('status', 1)` و مرتب‌سازی نزولی براساس شناسه.
- برای هر اقامتگاه، داده‌های کشور، شهر و تأمین‌کننده از Redis یا DB واکشی می‌شوند.
- ساخت پاسخ شامل جزئیات لوکیشن، آدرس، درجه، وضعیت، و مسیر رسانه.

</div></div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 97%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>شامل فیلدهای صفحه‌بندی و فیلترها.</td></tr><tr><td>advanced.country</td><td>integer</td><td>json → advanced</td><td>اختیاری</td><td>شناسه کشور فیلترشده.</td></tr><tr><td>advanced.state</td><td>integer</td><td>json → advanced</td><td>اختیاری</td><td>شناسه استان فیلترشده.</td></tr><tr><td>advanced.city</td><td>integer</td><td>json → advanced</td><td>اختیاری</td><td>شناسه شهر فیلترشده.</td></tr><tr><td>advanced.rate</td><td>integer</td><td>json → advanced</td><td>اختیاری</td><td>امتیاز کیفیت اقامتگاه.</td></tr><tr><td>advanced.r</td><td>string</td><td>json → advanced</td><td>اختیاری</td><td>عبارت جستجوی عمومی (نام یا آدرس هتل).</td></tr><tr><td>length</td><td>integer</td><td>json</td><td>بله</td><td>تعداد سطر در هر صفحه.</td></tr><tr><td>start</td><td>integer</td><td>json</td><td>بله</td><td>شماره شروع صفحه.</td></tr><tr><td>draw</td><td>integer</td><td>json</td><td>اختیاری</td><td>شناسه داخلی برای درخواست‌های Datatable.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
GET /api/v2/base/accommodations/list
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "json": {
    "length": 10,
    "start": 0,
    "draw": 1,
    "advanced": {
      "country": 118,
      "state": 31,
      "city": 548,
      "rate": 5,
      "r": "مشهد"
    }
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>items\[\].id</td><td>integer</td><td>شناسه داخلی اقامتگاه.</td></tr><tr><td>items\[\].title\_fa / title</td><td>string</td><td>نام فارسی و انگلیسی اقامتگاه.</td></tr><tr><td>items\[\].rate</td><td>integer</td><td>امتیاز عددی هتل.</td></tr><tr><td>items\[\].country</td><td>object</td><td>داده‌های کشور مقصد، شامل نام فارسی و کد ISO.</td></tr><tr><td>items\[\].state</td><td>string</td><td>نام استان مرتبط.</td></tr><tr><td>items\[\].city</td><td>string</td><td>نام شهر مرتبط.</td></tr><tr><td>items\[\].address</td><td>string</td><td>آدرس هتل.</td></tr><tr><td>items\[\].logo</td><td>string</td><td>مسیر رسانه لوگوی اقامتگاه.</td></tr><tr><td>items\[\].supplier</td><td>object</td><td>اطلاعات تأمین‌کننده (colleague mapping).</td></tr><tr><td>meta.recordsTotal</td><td>integer</td><td>تعداد کل رکوردها.</td></tr><tr><td>meta.recordsFiltered</td><td>integer</td><td>تعداد فیلترشده.</td></tr><tr><td>meta.timestamp</td><td>integer (unix)</td><td>زمان تولید پاسخ.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "items": [
    {
      "id": 102,
      "title_fa": "هتل پارس مشهد",
      "rate": 5,
      "country": { "id": 118, "fa_name": "ایران", "iso": "IR" },
      "state": "خراسان رضوی",
      "city": "مشهد",
      "address": "خیابان امام رضا",
      "logo": "https://storage.service01.ir/media/hotels/pars.jpg",
      "supplier": { "system_serial": 11, "serial": "CLG992" }
    }
  ],
  "meta": { "timestamp": 1750669000, "recordsTotal": 150, "recordsFiltered": 10 }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%A7-jwt-%D9%85%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط با JWT معتبر امکان‌پذیر است.
- فیلترهای فیلد قابل تزریق SQL نیستند، چون از `where()` و `paginate()` ایمن استفاده شده.
- در حال حاضر کنترل سطح دسترسی برای شعب مختلف تعریف نشده، پیشنهاد اضافه گردد.

</div>### نکات عملکردی

<div id="bkmrk-redis-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%B4%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Redis برای کش‌کردن نام کشورها استفاده می‌شود (کلید `countries:{id}`).
- بهتر است کش شهرها نیز افزوده شود تا فشار به DB کاهش یابد.
- در هر فراخوانی، بیش از ۳ جدول مرتبط (hotels، cities، states، colleagues) درگیر هستند.
- پیشنهاد TTL کش = 4 ساعت.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\Media;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>فرمت نادرست JSON یا فیلدهای ناقص.</td><td>Decoder JSON</td></tr><tr><td>404</td><td>هیچ اقامتگاهی برای فیلترهای واردشده یافت نشد.</td><td>Query hotels</td></tr><tr><td>500</td><td>خطا در اتصال Redis یا پایگاه داده.</td><td>DB Facade / Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%A8%D8%AA%D9%86%DB%8C-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فیلتر دسترسی مبتنی بر نقش (RBAC) برای شعب مختلف.
- اضافه کردن rate-limit برای درخواست لیست متوالی.
- پنهان‌سازی مسیر انبار رسانه هنگام پاسخ.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%88%DB%8C%DA%98%DA%AF%DB%8C-%D9%85%D8%B1%D8%AA%D8%A8%E2%80%8C%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن ویژگی مرتب‌سازی پویا براساس امتیاز یا کشور.
- نمایش داده‌های تأمین‌کننده با نماد و نام تجاری.
- افزودن قابلیت فیلتر براساس وضعیت فعال/غیرفعال.
- پشتیبانی از lazy-loading برای مدیا (logo).

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%87%D8%B1-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-s" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر درخواست در جدول `system_log` با نوع `ReadAccommodationList` ثبت شود.
- مقادیر چون `branch_id`، `filters` و `operator_id` باید در لاگ ذخیره شوند.
- سطح لاگ توصیه‌شده: **Info**.

</div>### جمع‌بندی

<div id="bkmrk-accommodation-list-base" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">مسیر **/base/accommodations/list** یکی از اصلی‌ترین نقاط برای واکشی اقامتگاه‌های سیستم است. با تکیه بر Redis، سیستم بهینه‌تر عمل می‌کند ولی برای نسخه Enterprise نیاز به توسعه کش شهر/استان و امنیت سطح دسترسی دارد. پاسخ استاندارد JSON‌ شامل متادیتا زمان و شمارنده‌های صفحه‌بندی، ساختار کاملاً تمیز و قابل‌درک برای فرانت‌اند فراهم می‌کند.</div></div>

# POST /base/accommodation/rooms/delete

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/base/accommodation/rooms/delete</td><td style="direction: ltr; text-align: left;">V2BaseController@accommodationRoomsDelete</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف گروهی انواع اتاق‌های اقامتگاه و نگاشت‌های مربوطه از جداول سیستم.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-accommodationro" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **accommodationRoomsDelete** مسئول حذف تمام انواع اتاق‌های وابسته به یک اقامتگاه (هتل) است. این تابع ابتدا شناسه‌های اتاق‌های وابسته را بر اساس پارامتر ورودی `accommodation_id` واکشی می‌کند، سپس نگاشت‌های تبعی در جدول `mapping_roomtypes` را پاک کرده و در نهایت خود رکوردهای اتاق‌های اقامتگاه را حذف می‌کند.   
  
منطق کلی: - دریافت ورودی `accommodation_id` و اختیاری `service`.
- ایجاد کوئری داخلی با `JOIN` بین جداول `accommodation_roomtypes` و `mapping_roomtypes`.
- در صورت وجود پارامتر `service`، فقط اتاق‌هایی حذف می‌شوند که دارای مقدار در آن سرویس باشند.
- پس از حذف از دو جدول، پاسخ موفق شامل وضعیت بولی `payload=true` بازگردانده می‌شود.

</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>accommodation\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه اقامتگاه مقصد برای حذف اتاق‌ها.</td></tr><tr><td>service</td><td>string</td><td>Body</td><td>اختیاری</td><td>نام سرویس (مثلاً `snapptrip`) برای اعمال حذف محدود.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
POST /api/v2/base/accommodation/rooms/delete
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "accommodation_id": 1870,
  "service": "snapptrip"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>payload</td><td>boolean</td><td>نمایانگر موفقیت حذف رکوردها.</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان یونیکس ثبت پاسخ.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "payload": true,
  "meta": {
    "timestamp": 1750669152
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%D9%81%D9%82%D8%B7-%D8%A8%D8%A7-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مسیر فقط با احراز هویت JWT معتبر قابل دستیابی است.
- تمام رکوردها فقط از شعبه مربوطه حذف می‌شوند در صورت اعمال کنترل مرکزی (پیشنهاد: اضافه شود).
- بدون تأیید پیش‌شرط، حذف داده‌ها خطرناک است — نیاز به اعتبارسنجی نقش (Role).

</div>### نکات عملکردی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-delete%28%29-%D9%87%D9%85%E2%80%8C%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات `delete()` هم‌زمان روی دو جدول انجام می‌گیرد، بنابراین هزینه I/O دو برابر معمول است.
- پیشنهاد می‌شود حذف‌ها در تراکنش `DB::transaction()` انجام گیرند تا از ناسازگاری داده جلوگیری شود.
- TTL کش مربوطه (Redis) برای کلید `accommodations:cron:services:{service}:{accommodation_id}` باید پس از حذف ریست شود.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-404-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>404</td><td>شناسه اقامتگاه وجود ندارد یا خالی است.</td><td>accommodationRoomsDelete()</td></tr><tr><td>403</td><td>کاربر مجاز به حذف رکورد نیست.</td><td>authWithJwt</td></tr><tr><td>500</td><td>خطا در حذف رکورد از DB.</td><td>DB Facade</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-role-%22dataman" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن ROLE "DataManager" برای کنترل سطح حذف.
- لاگ‌گذاری حذف‌ها در جدول audit به همراه شناسه اقامتگاه و شناسه اپراتور.
- اضافه کردن تأیید دو مرحله‌ای برای حذف‌های گسترده.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-softdelet" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از SoftDelete برای حفظ تاریخچه رکوردهای اتاق.
- ارسال پاسخ تفکیکی شامل تعداد رکوردهای حذف‌شده.
- امکان فیلتر حذف بر اساس وضعیت فعال یا سرویس خاص.
- اتصال حذف‌ها به فرآیند Cron برای به‌روزرسانی خدمات خارجی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%A8%D8%A7-%D9%86%D9%88%D8%B9-delet" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ با نوع `DeleteRoomType`.
- فیلدهای پیشنهادی: `operator_id`، `branch`، `accommodation_id`، `service`.
- سطح لاگ توصیه‌شده: **Critical**.

</div>### جمع‌بندی

<div id="bkmrk-accommodation-rooms-delete" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">مسیر **POST /base/accommodation/rooms/delete** نقطه اصلی برای پاک‌سازی انواع اتاق‌های اقامتگاه است. حذف هم‌زمان نگاشت‌ها و اتاق‌ها بدون Transaction انجام می‌شود که باید در نسخه بعدی اصلاح گردد. پیشنهاد می‌شود پس از حذف، کلیدهای Redis مربوطه آزاد (evict) شوند تا وضعیت لحظه‌ای سرویس‌های اتاق‌دار (مانند Snapptrip) با منبع هماهنگ بماند.</div></div>

# POST /base/accommodation/room/update

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">POST</td><td style="direction: ltr; text-align: left;">/api/v2/base/accommodation/rooms/update</td><td style="direction: ltr; text-align: left;">V2BaseController@accommodationRoomsUpdate</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">بروزرسانی نگاشت (Mapping) و اطلاعات اتاق‌های اقامتگاه از طریق سرویس‌های خارجی نظیر SnappTrip.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-accommodationro" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **accommodationRoomsUpdate** وظیفه دارد لیست اتاق‌های متصل به اقامتگاه را بازسازی کند. این مسیر فقط برای سرویس‌های خاص فعال است—به‌طور پیش‌فرض برای سرویس `snapptrip`. پس از حذف اتاق‌های قبلی از جداول `accommodation_roomtypes` و `mapping_roomtypes`، کش Redis مربوطه تخلیه شده و از طریق **CronController::updateSnapptripMappedAccommodation()** عمل بازسازی انجام می‌شود. در سایر سرویس‌ها، تابع با خطای 404 پاسخ می‌دهد. - دریافت متغیر `service` و `accommodation_id`.
- بررسی شرط: اگر سرویس برابر با `snapptrip` باشد → ادامه‌ی عملیات.
- واکشی شناسه اتاق‌های مرتبط از دو جدول با استفاده از `JOIN`.
- حذف رکوردها از هر دو جدول.
- پاک‌سازی کش Redis مربوط به اقامتگاه هدف.
- فراخوانی بروزرسانی CronController و بازگرداندن نتیجه آن در قالب کلید `payload`.
- در غیر این صورت، بازگشت خطای `404 Not Supported Service`.

</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 95%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>accommodation\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه اقامتگاه مقصد که اتاق‌های آن بروزرسانی می‌شوند.</td></tr><tr><td>service</td><td>string</td><td>Body</td><td>بله</td><td>نام سرویس هماهنگ‌سازی اتاق‌ها (در حال حاضر فقط `snapptrip` معتبر است).</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
POST /api/v2/base/accommodation/rooms/update
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

{
  "accommodation_id": 2231,
  "service": "snapptrip"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>payload</td><td>boolean</td><td>نتیجه نهایی عملیات بروزرسانی؛ `true` در صورت موفقیت.</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان یونیکس ثبت پاسخ.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "payload": true,
  "meta": {
    "timestamp": 1750669200
  }
}
```

#### نمونه پاسخ خطا برای سرویس نامعتبر:

```
{
  "error": {
    "code": 404,
    "message": "امکان بروزرسانی این آیتم از طریق سرویس انتخاب شده امکان‌پذیر نیست."
  },
  "meta": {
    "timestamp": 1750669200
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیاز به احراز هویت JWT معتبر دارد.
- در صورت اجرا در سطح سرویس خارجی، باید ورودی `service` از لیست سفید داخلی تأیید شود.
- اجراهای هم‌زمان با Redis ممکن است نیاز به قفل توزیع‌شده داشته باشند (**Distributed Locking**).

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1-%D9%87%D8%B1-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C%D8%8C-%D8%AF%D9%88-%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در هر فراخوانی، دو عملیات حذف عمده روی DB و یک پاک‌سازی Redis انجام می‌شود.
- برای پیشگیری از فشار روی سرور، پیشنهاد می‌شود عملیات در صف (Queue) قرار گیرد.
- بهینه‌سازی قابل‌توجه با اجرای `CronController::updateSnapptripMappedAccommodation()` در Job async قابل انجام است.
- TTL کش پیشنهادی بعد از بروزرسانی: ۶ ساعت.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\CronController;
- use Carbon\\Carbon;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-404-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>404</td><td>سرویس واردشده پشتیبانی نمی‌شود.</td><td>accommodationRoomsUpdate()</td></tr><tr><td>500</td><td>خرابی در فراخوانی CronController یا ارتباط Redis/DB.</td><td>CronController / DB Facade</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-role-%D9%85%D8%AD%D8%AF%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه‌کردن Role محدودکننده برای بروزرسانی داده‌های اقامتگاه (مثلاً "DataSupervisor").
- ثبت دقیق `operator_id` و `branch` در لاگ.
- اضافه‌کردن لایه تأیید دو مرحله‌ای برای عملیات حذف/بازسازی گروهی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%AD%D9%85%D8%A7%DB%8C%D8%AA-%D8%A7%D8%B2-%D8%B3%D8%B1%D9%88%DB%8C%D8%B3%E2%80%8C%D9%87%D8%A7%DB%8C-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- حمایت از سرویس‌های دیگر مانند Booking یا Expedia برای آینده.
- جایگزینی پاک‌سازی Redis با Pipeline commands جهت افزایش سرعت.
- نمایش وضعیت جزئی از تعداد اتاق‌های حذف‌شده و افزوده‌شده در پاسخ.
- توسعه لاگ تفکیکی برای هر اقامتگاه با timestamp و branch.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D9%BE%DB%8C%D8%B4%D9%86%D9%87%D8%A7%D8%AF%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ثبت لاگ پیشنهادی: `UpdateRoomMapping`.
- فیلدهای اجباری در لاگ: `service`، `accommodation_id`، `operator_id`، `status`.
- سطح لاگ توصیه‌شده: **Info / Warning** بسته به نتیجه CronController.

</div>### جمع‌بندی

<div id="bkmrk-accommodation-rooms-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">مسیر **/base/accommodation/rooms/update** عملیات حیاتی بروزرسانی نگاشت اتاق‌ها با سرویس‌های خارجی مانند SnappTrip را انجام می‌دهد. در فعلی‌ترین پیاده‌سازی فقط برای SnappTrip فعال است؛ حذف و بازسازی همه‌ی داده‌های مربوطه همراه با پاک‌سازی Redis انجام می‌شود. در نسخه بعدی این سیستم باید مفهوم *Transactional Safety* و *Service Expandability* اضافه گردد تا برای سایر سرویس‌ها نیز قابل توسعه باشد.</div></div>

# GET /api/v2/base/certificates

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/base/certificates</td><td style="direction: ltr; text-align: left;">V2BaseController@indexCertificates</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">نمایش لیست گواهی‌نامه‌های شعبه شامل فایل‌ها و پیش‌نمایش محتوای آن‌ها.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-indexcertificat" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **indexCertificates** سطرهای جدول `certificates` را که مربوط به شعبه‌ی کاربر فعلی هستند واکشی می‌کند، سپس آن‌ها را بر اساس `id DESC` مرتب می‌نماید. هر آیتم از نظر وجود فایل `document` بررسی شده و در صورت وجود، تصویر پیش‌نمایش تولید می‌شود: - اگر `document` با پسوند `.pdf` باشد، آیکون PDF نمایش داده می‌شود.
- در غیر این صورت، پیش‌نمایش تصویر آپلود شده از Storage (service01) تولید می‌شود.

در انتها فیلدهای اضافی مانند `created_at`، `updated_at` و `branch` حذف می‌گردند و نتیجه با ساختار تفکیک‌شده بازگردانده می‌شود.</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>Header / JWT</td><td>بله</td><td>شناسه شعبه‌ای که داده‌ی گواهی‌نامه در آن ذخیره شده.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
GET /api/v2/base/certificates
Authorization: Bearer {JWT_TOKEN}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>items\[\].id</td><td>integer</td><td>شناسه گواهی‌نامه.</td></tr><tr><td>items\[\].title</td><td>string</td><td>عنوان ثبت‌شده مدارک.</td></tr><tr><td>items\[\].document</td><td>string</td><td>مسیر فایل سند در Storage.</td></tr><tr><td>items\[\].preview</td><td>string|html</td><td>کد HTML شامل تصویر یا محتوای نوشته برای پیش‌نمایش.</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان یونیکس ثبت پاسخ.</td></tr></tbody></table>

</div>#### نمونه پاسخ موفق:

```
{
  "items": [
    {
      "id": 128,
      "title": "گواهی ISO 9001",
      "document": "media/certificates/iso9001.pdf",
      "preview": "<img src="https://storage.service01.ir/media/temporary/icon-pdf.png"></img>"
    },
    {
      "id": 129,
      "title": "مجوز درجه دو",
      "document": "media/certificates/license.png",
      "preview": "<img src="https://storage.service01.ir/media/certificates/license.png"></img>"
    }
  ],
  "meta": {
    "timestamp": 1750669215
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیازمند احراز هویت JWT معتبر از طریق Middleware `authWithJwt`.
- کاربر تنها به گواهی‌نامه‌های شعبه‌ی خودش دسترسی دارد.
- هیچ فیلد فایلی به‌صورت قابل نوشتن بازگردانده نمی‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%A7-orderb" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کوئری ساده با `orderBy('id','DESC')` و احتمالاً بدون نیاز به ایندکس اضافی.
- در صورت افزایش حجم داده پیشنهاد صفحه‌بندی یا `limit(50)`.
- امکان cache خروجی در Redis با کلید `certificates:{branch}` با TTL=1800s برای کاهش بار DB.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Exception;
- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\Response;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 88%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>خطا در اجرای کوئری پایگاه‌داده.</td><td>Catch(Exception)</td></tr><tr><td>404</td><td>هیچ گواهی‌نامه‌ای برای این شعبه یافت نشد.</td><td>indexCertificates()</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%B3%D8%B7%D8%AD" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه‌کردن کنترل سطح دسترسی Role (مثلاً `can('view_certificates')`).
- رمزنگاری مسیر فایل برای جلوگیری از دسترسی مستقیم خارج از Storage.
- اعتبارسنجی دقیق SSL Storage (service01.ir).

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C-%D9%88-%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن صفحه‌بندی و فیلتر جستجو بر اساس `title`.
- پشتیبانی از پیش‌نمایش PDF با Embedded Viewer.
- اضافه کردن فیلد `uploaded_by` برای لاگ مسئول بارگذاری.
- افزودن شمارنده کل برای جدول `certificates`.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF-%D8%AA%D9%88%D8%B5%DB%8C%D9%87%E2%80%8C%D8%B4%D8%AF%D9%87%3A-r" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ توصیه‌شده: `ReadCertificates`.
- ذخیره فیلدهای: `operator_id`، `branch`، `count(items)`.
- سطح لاگ پیشنهادی: **Info**.

</div>### جمع‌بندی

<div id="bkmrk-index-certificates" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">متد **indexCertificates** داده‌های ساده و استاتیک گواهی‌نامه‌های شعبه را واکشی می‌کند و پیش‌نمایش‌های خودکار از فایل یا تصویر ایجاد می‌کند. پیشنهاد می‌شود برای بهبود سرعت در بارگذاری داشبوردها، خروجی در Redis cache ذخیره شود و در نسخه بعدی قابلیت جستجوی پویا اضافه گردد.</div></div>

# DELETE /api/v2/base/certificate

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">DELETE</td><td style="direction: ltr; text-align: left;">/api/v2/base/certificate</td><td style="direction: ltr; text-align: left;">V2BaseController@deleteCertificate</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">حذف گواهی‌نامه مشخص از جدول `certificates` براساس شناسه و شعبه درخواستی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-deletecertifica" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **deleteCertificate** برای حذف یک گواهی‌نامه خاص با توجه به شعبه و شناسه گواهی عمل می‌کند. این روش، مستقیماً جدول `certificates` را هدف قرار می‌دهد و رکورد منطبق را حذف می‌نماید. اگر حذف موفق باشد، پاسخ HTTP 204 (بدون محتوا) برمی‌گردد. در صورت بروز خطا در اجرای کوئری پایگاه داده، یک پاسخ JSON از نوع خطا ارسال می‌شود.   
  
منطق کلی تابع: - دریافت ورودی `branch` از JWT یا Request.
- دریافت پارامتر `id` از query/body.
- اجرای دستور `DB::table('certificates')->where('branch', branch)->where('id', id)->delete()`.
- در صورت موفقیت، بازگشت پاسخ خالی با Status Code `204`.
- در صورت Exception، بازگرداندن ساختار JSON حاوی جزئیات خطا و trace.

</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>JWT / Header</td><td>بله</td><td>شناسه شعبه‌ای که گواهی مربوط به آن است.</td></tr><tr><td>id</td><td>integer</td><td>Query / Body</td><td>بله</td><td>شناسه گواهی موردنظر برای حذف.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
DELETE /api/v2/base/certificate?id=128
Authorization: Bearer {JWT_TOKEN}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

#### پاسخ موفق (بدون محتوا):

```
HTTP/1.1 204 No Content
```

#### نمونه پاسخ در صورت خطا:

```
{
  "meta": { "timestamp": 1750669255 },
  "error": {
    "code": 400,
    "message": "خطای پایگاه داده در حذف گواهی‌نامه.",
    "trace": [...]
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مسیر فقط برای کاربران احراز‌شده دارای توکن JWT معتبر در دسترس است.
- دسترسی باید محدود به شعبهٔ تعریف‌شده در توکن کاربر باشد؛ جلوگیری از حذف بین‌شعبه‌ای الزامی است.
- هیچ پاسخ حاوی دادهٔ حساس در صورت موفقیت ارسال نمی‌شود (Status 204).

</div>### نکات عملکردی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AD%D8%B0%D9%81-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-%D8%B1%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات حذف مستقیم روی DB انجام می‌شود و فاقد مرحله گردش در حافظه است.
- در حجم پایین جدول `certificates`، نیازی به ایندکس جداگانه نیست؛ اما پیشنهاد می‌شود ایندکس ترکیبی بر اساس (`branch`, `id`) اضافه شود.
- در صورت بالاتر از ۵۰۰۰ رکورد، حذف انبوه بهتر است با Job asynchronous انجام گیرد.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\DB;
- use Exception;
- use Illuminate\\Support\\Facades\\Response;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>خطای اجرای کوئری یا Exception داخلی هنگام حذف.</td><td>Catch(Exception)</td></tr><tr><td>404</td><td>شناسه مدرک یافت نشد یا شعبه با آن مطابقت ندارد.</td><td>deleteCertificate()</td></tr><tr><td>500</td><td>خطای ارتباط یا I/O در پایگاه داده.</td><td>DB Facade</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D9%86%D9%82%D8%B4%3A-%D9%81%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن کنترل نقش: فقط کاربران با نقش `DataManager` یا `DocumentAdmin` مجاز به حذف باشند.
- ثبت عملیات در جدول `audit_log` شامل `id` و `operator_id`.
- بررسی صحت JWT expiration تا از دسترسی‌های منقضی جلوگیری شود.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-softdelet" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از SoftDelete برای حفظ تاریخچهٔ حذف‌ها در آینده.
- بازگرداندن جزئیات حذف‌شده برای تأیید اپراتور قبل از حذف دائمی.
- افزودن لاگ Redis برای ثبت تعداد حذف‌ها در هر شعبه.
- امکان مدیریت چندگانه (Batch Delete) در نسخه بعدی API.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-deletecerti" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `DeleteCertificate`.
- فیلدهای پیشنهادی: `operator_id`، `branch`، `certificate_id`، `timestamp`.
- سطح اهمیت: **Critical**.

</div>### جمع‌بندی

<div id="bkmrk-delete-certificate" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">مسیر **DELETE /base/certificate** عملیات حذف ساده ولی حساس گواهی‌نامه‌ها را انجام می‌دهد. به دلیل ماهیت destructive عمل، لازم است کنترل سطح دسترسی و ممیزی دقیق فعال باشد. در نسخه بعدی، استفاده از SoftDelete و ثبت رویداد حذف در Redis توصیه می‌شود.</div></div>

# GET /api/v2/base/certificate

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 96%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td style="direction: ltr; text-align: left;">GET</td><td style="direction: ltr; text-align: left;">/api/v2/base/certificate</td><td style="direction: ltr; text-align: left;">V2BaseController@showCertificate</td><td style="direction: ltr; text-align: left;">authWithJwt</td><td style="direction: rtl; text-align: right;">واکشی مشخصات گواهی‌نامه‌ی خاص بر اساس `id` و شعبه کاربر.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-showcertificate" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">تابع **showCertificate** داده‌ی یک گواهی‌نامه خاص را از جدول `certificates` واکشی می‌کند. ورودی‌های `branch` از JWT و `id` از Request دریافت می‌شود. سپس فیلدهای غیرضروری (مانند `created_at`، `updated_at` و `branch`) از خروجی حذف می‌شوند و شیء نهایی در قالب کلید `payload` به کاربر برگردانده می‌شود.</div></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 92%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع داده</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>JWT / Header</td><td>بله</td><td>شناسه شعبه کاربر.</td></tr><tr><td>id</td><td>integer</td><td>Query / Body</td><td>بله</td><td>شناسه گواهی‌نامه موردنظر برای نمایش جزئیات آن.</td></tr></tbody></table>

</div>#### نمونه درخواست:

```
GET /api/v2/base/certificate?id=129
Authorization: Bearer {JWT_TOKEN}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D9%86%D9%88%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 94%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>فیلد</td><td>نوع داده</td><td>توضیح</td></tr><tr><td>payload.id</td><td>integer</td><td>شناسه گواهی‌نامه.</td></tr><tr><td>payload.title</td><td>string</td><td>عنوان گواهی‌نامه.</td></tr><tr><td>payload.license\_number</td><td>string</td><td>شماره مجوز یا سریال گواهی‌نامه.</td></tr><tr><td>payload.exporter</td><td>string</td><td>نهاد صادرکننده گواهی.</td></tr><tr><td>payload.expiration</td><td>string|date</td><td>تاریخ انقضای گواهی.</td></tr><tr><td>payload.document</td><td>string</td><td>مسیر فایل پیوست (تصویر یا PDF) در Storage.</td></tr><tr><td>payload.content</td><td>string</td><td>محتوای متنی گواهی در صورت وجود.</td></tr><tr><td>meta.timestamp</td><td>integer</td><td>زمان سیستم در لحظه پاسخ.</td></tr></tbody></table>

</div>#### نمونه پاسخ:

```
{
  "meta": {
    "timestamp": 1750669255
  },
  "payload": {
    "id": 129,
    "title": "مجوز درجه دو",
    "license_number": "LIC-2025-23",
    "exporter": "وزارت گردشگری ایران",
    "expiration": "2025-12-30",
    "document": "media/certificates/license.png",
    "content": null,
    "description": "گواهی معتبر رزرو اقامتگاه سطح دو."
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%D8%A8%D8%A7-jwt-%D8%A7%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت با JWT الزامی است.
- فیلتر روی `branch` باعث جلوگیری از دسترسی بین‌شعبه‌ای می‌شود.
- فایل یا مسیر سند مستقیماً ارسال نمی‌شود، تنها مسیر کنترل‌شده برمی‌گردد.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%A7-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کوئری ساده با خروجی مستقیم و بدون Join.
- بدلیل درخواست تکی، نیاز به Cache Redis ندارد (اما قابل پیاده‌سازی با TTL 2m).
- پاسخ JSON سبک و بدون سربار پردازشی است.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Exception;
- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\Response;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 88%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>خطا در اجرای کوئری یا Exception داخلی.</td><td>Catch(Exception)</td></tr><tr><td>404</td><td>گواهی‌نامه با این شناسه یافت نشد.</td><td>DB::first()</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%D8%AC%D8%A7%D8%B2-%D8%A8%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن بررسی مجاز بودن نقش کاربر (`can('view_certificate')`).
- رمزنگاری مسیر فایل `document` در Storage.
- اعمال Rate Limiting در سطح IP برای جلوگیری از Brute Force روی idها.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-previ" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `preview=true` برای نمایش تصویر بندانگشتی.
- افزودن فیلد نسخه‌گذاری در گواهی‌نامه‌ها جهت History Tracking.
- اتصال سیستم هشدار انقضا به کنترلر Dashboard.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF-%D9%BE%DB%8C%D8%B4%D9%86%D9%87%D8%A7%D8%AF%DB%8C%3A-re" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ پیشنهادی: `ReadCertificateDetails`.
- شامل فیلدهای: `operator_id`، `branch`، `certificate_id`.
- سطح لاگ: **Info**.

</div>### جمع‌بندی

<div id="bkmrk-show-certificate" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><div style="direction: rtl; text-align: justify;">این مسیر برای واکشی دقیق اطلاعات هر گواهی‌نامه طراحی شده و پاسخ سبک و تمیز JSON با کلید `payload` ارائه می‌دهد. برای افزایش امنیت و مقیاس‌پذیری، در نسخه آینده پیشنهاد می‌شود RBAC و Cache Redis اضافه گردد.</div></div>

# POST /api/v2/base/certificate

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/base/certificate</td><td dir="ltr">V2BaseController@storeCertificate</td><td dir="ltr">authWithJwt</td><td dir="rtl">ثبت گواهی‌نامه جدید در جدول `certificates` بر اساس داده‌های ورودی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **storeCertificate** پس از دریافت پارامترهای اجباری (`title`، `license_number`، `exporter` و فایل `document`) گواهی را برای شعبهٔ کاربر ایجاد می‌کند و رکورد جدید را در جدول `certificates` درج می‌نماید. مقدار `created_at` و `updated_at` با زمان فعلی تکمیل می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه صادرکننده.</td></tr><tr><td>title</td><td>string</td><td>Body</td><td>بله</td><td>عنوان گواهی‌نامه.</td></tr><tr><td>license\_number</td><td>string</td><td>Body</td><td>خیر</td><td>شماره سریال یا مجوز.</td></tr><tr><td>exporter</td><td>string</td><td>Body</td><td>بله</td><td>مرجع صادرکننده.</td></tr><tr><td>expiration</td><td>string (YYYY-MM-DD)</td><td>Body</td><td>خیر</td><td>تاریخ انقضاء.</td></tr><tr><td>document</td><td>string (path)</td><td>Body</td><td>بله</td><td>مسیر فایل بارگذاری‌شده.</td></tr><tr><td>content</td><td>string</td><td>Body</td><td>خیر</td><td>محتوای متنی یا توضیح سند.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "meta": {"timestamp": 1750669255},
  "payload": {
    "id": 210,
    "title": "مجوز فنی اقامتگاه‌ها",
    "exporter": "وزارت گردشگری",
    "expiration": "2026-02-01",
    "document": "media/certificates/acm_license.pdf"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT الزامی است.
- عملیات فقط برای نقش‌های مجاز مثل `DocumentAdmin`.
- اعتبارسنجی فایل ورودی؛ فقط PDF و تصویر مجاز.

</div>### نکات عملکردی

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%AF%D9%88%D9%86-%D8%B9%D9%85%D9%84%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ورودی ساده بدون عملیات چندجدولی.
- امکان کش Redis برای فهرست پس از ثبت (TTL 1800s).
- عملیات درج داخل تراکنش اتمیک.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-400-%D8%AF%D8%A7%D8%AF%D9%87-%D9%86%D8%A7%D9%82%D8%B5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>400</td><td>داده ناقص یا فایل نامعتبر.</td></tr><tr><td>500</td><td>خطا در درج رکورد در پایگاه داده.</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%86%D9%82%D8%B4-%D8%A8%D8%A7-can%28%27cr" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بررسی نقش با `can('create_certificate')`.
- ضبط عملیات در جدول حسابرسی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-%D9%86%D8%B3%D8%AE%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه کردن نسخه‌بندی سند.
- رمزنگاری مسیر فایل‌ها.

</div>### ممیزی

نوع لاگ: `CreateCertificate`. سطح: **Important**.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### جمع‌بندی

مسیر `POST /base/certificate` مسئول ایجاد گواهی‌نامه جدید است و باید کنترل نقش دقیق همراه با ثبت حسابرسی داشته باشد.

<div id="bkmrk-store-certificate" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# PUT /api/v2/base/certificate

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Purpose</td></tr><tr><td>PUT</td><td dir="ltr">/api/v2/base/certificate</td><td dir="ltr">V2BaseController@updateCertificate</td><td dir="rtl">ویرایش داده‌های گواهی‌نامه براساس شناسه و شعبه کاربر.</td></tr></tbody></table>

</div>### منطق عملکرد

تابع **updateCertificate** داده‌های موجود گواهی را بروزرسانی می‌کند. تغییرات شامل عنوان، صادرکننده، تاریخ انقضا یا مسیر فایل است و با `DB::update()` در همان رکورد انجام می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-id-%28integer%2C-require" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- `id` (integer, required): شناسه گواهی.
- `branch` (integer): شعبه مالک گواهی.
- `title, exporter, expiration, document, content`: فیلدهای قابل ویرایش.

</div>### خروجی

```
{
  "meta": {"timestamp": 1750669277},
  "payload": true
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C.-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- JWT الزامی.
- دسترسی محدود به شعبه خودش.
- نقش مجاز: `CertificateEditor`.

</div>### کارایی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%B3%D8%A7%D8%AF%D9%87-%D9%88-%D8%B3%D8%B1%DB%8C%D8%B9-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات ساده و سریع در یک کوئری.
- پیشنهاد استفاده از `DB::transaction` برای Atomicity.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;

</div>### خطاها

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-404-%DA%AF%D9%88%D8%A7%D9%87%DB%8C%E2%80%8C%D9%86%D8%A7%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>404</td><td>گواهی‌نامه یافت نشد.</td></tr><tr><td>400</td><td>داده نامعتبر یا پارامتر ناقص.</td></tr></tbody></table>

</div>### پیشنهاد امنیتی

<div id="bkmrk-%D8%AA%D8%A3%DB%8C%DB%8C%D8%AF-%D9%85%D8%A7%D9%84%DA%A9%DB%8C%D8%AA-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تأیید مالکیت رکورد بر اساس `branch`.
- ذخیره نسخه قبل در جدول `certificate_revisions`.

</div>### پیشنهاد عملکردی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-trigger-%D8%A8%D8%B1%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن trigger برای لاگ تغییرات.
- بروزرسانی کش Redis در صورت موفقیت.

</div>### ممیزی

نوع لاگ: `UpdateCertificate`، سطح: **Important**.

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### جمع‌بندی

مسیر ویرایش گواهی‌نامه قابل‌اعتماد و سریع است؛ لازم است کنترل دقیق نقش در سطح شعبه حفظ شود.

<div id="bkmrk-update-certificate" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/accommodations/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Purpose</td></tr><tr><td>POST</td><td dir="ltr">/api/v2/accommodations/list</td><td dir="ltr">V2BaseController@accommodationsList</td><td dir="rtl">نمایش فهرست اقامتگاه‌ها با فیلتر و صفحه‌بندی پویا.</td></tr></tbody></table>

</div>### منطق عملکرد

درون تابع **accommodationsList()** ابتدا ورودی JSON دیکد می‌شود. سپس براساس فیلدهای `advanced` شامل کشور، استان، شهر، رتبه یا عبارت جستجو، فیلترینگ انجام می‌گیرد. نتایج از جدول `hotels` واکشی شده و با داده‌های Redis (کش کشور، همکار، لوگو و موقعیت جغرافیایی) ترکیب می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

```
{
  "json": "{\"start\":0,\"length\":20,\"advanced\":{\"country\":12,\"city\":5,\"rate\":4,\"r\":\"تهران\"}}"
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### خروجی

```
{
  "items": [
    {"id":100,"title":"Espinas Palace","title_fa":"هتل اسپیناس پالاس","rate":5,"country":"ایران","city":"تهران"}
  ],
  "meta":{"timestamp":1750669255,"draw":1,"recordsTotal":1240,"recordsFiltered":20}
}
```

<div id="bkmrk--3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### امنیت

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مسیر نیاز به احراز JWT دارد.
- فیلتر شعبه براساس توکن کاربر قابل‌افزودن است.

</div>### کارایی

<div id="bkmrk-%D8%A8%D9%87%DB%8C%D9%86%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A8%D8%A7-pagina" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بهینه‌سازی با `paginate()`، ایندکس روی فیلدهای city و country.
- کش Redis TTL=1800s برای کشورها و شهرها.
- محاسبه اولیه `draw` برای درخواست صفحه‌بندی.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\Media;

</div>### خطاها

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-400-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-jso" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="margin: 15px auto; width: 90%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td></tr><tr><td>400</td><td>ورودی JSON نامعتبر.</td></tr><tr><td>404</td><td>اقامتگاهی مطابق فیلتر یافت نشد.</td></tr></tbody></table>

</div>### پیشنهاد امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%86%D9%82%D8%B4-accommoda" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن نقش `AccommodationViewer`.
- اجرای `rate limiting` بر اساس IP.

</div>### پیشنهاد عملکردی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-so" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامترهای `sort_by` و `filter_range`.
- ارسال meta شامل واحد ارز (مثلاً IRR) در پاسخ.

</div>### ممیزی

نوع لاگ: `ReadAccommodationList`. سطح لاگ: Info.

<div id="bkmrk--4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### جمع‌بندی

این مسیر برای لیست‌گیری پویا از اقامتگاه‌ها طراحی شده و داده‌ها را از PostgreSQL و Redis ترکیب می‌کند. مناسب برای فازهای جستجوی سریع و داشبورد مدیریتی است.

<div id="bkmrk-accommodations-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /credit-debit/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/credit-debit/list</td><td dir="ltr">V2CreditDebitController@creditDebitList</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت فهرست تراکنش‌های بدهکار و بستانکار با جزئیات صفحه‌بندی و فیلتر پیشرفته.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **creditDebitList** داده‌ها را از جدول تراکنش‌ها (Pay یا Ledger) بر اساس شعبه کاربر و پارامترهای ورودی صفحه‌بندی واکشی می‌کند. ابتدا داده `$request->json` دیکد می‌شود، سپس مقادیر `start` و `length` برای صفحه‌بندی تنظیم می‌شود. بعد از اجرای کوئری، نتایج در آرایه نهایی با کلیدهای `draw`، `recordsTotal`، `recordsFiltered` و `data` بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>شامل تنظیمات صفحه‌بندی و فیلترهای جستجو.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه برای فیلتر تراکنش‌ها.</td></tr><tr><td>draw</td><td>integer</td><td>Body→JSON</td><td>خیر</td><td>شماره درخواست برای DataTables.</td></tr><tr><td>length</td><td>integer</td><td>Body→JSON</td><td>بله</td><td>تعداد ردیف در هر صفحه.</td></tr><tr><td>start</td><td>integer</td><td>Body→JSON</td><td>خیر</td><td>شاخص شروع ایتم‌ها (offset).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "draw": 2,
  "recordsTotal": 120,
  "recordsFiltered": 20,
  "data": [
    {
      "serial_id": 112,
      "system_serial": 5143,
      "type": "receive",
      "description": "دریافت از فروشنده",
      "amount": 750000,
      "date": "2025/10/20",
      "status": "completed"
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز JWT الزامی است.
- اطلاعات فقط برای همان شعبه کاربر قابل‌نمایش است.
- فیلدهای حساس (مثل شماره حساب) از خروجی حذف می‌شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-paginate%28" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از `paginate()` برای بهینه‌سازی صفحه‌بندی.
- پیشنهاد: کش Redis برای Queryهای پرتکرار (TTL=600s).
- در حالت فیلتر سنگین، توصیه به افزودن ایندکس بر `branch, date, type`.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Exception;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی JSON نامعتبر یا پارامتر ناقص.</td><td>json\_decode()</td></tr><tr><td>500</td><td>خطا در واکشی داده از پایگاه داده.</td><td>DB Query</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D9%86%D9%82%D8%B4%3A-%D9%81%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن کنترل نقش: فقط کاربر دارای دسترسی `can('view_creditdebit')`.
- اعمال Rate Limiting بر اساس IP.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-sort_" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `sort_by` برای مرتب‌سازی پویا.
- افزودن فیلد فیلتر بر اساس وضعیت تراکنش (در انتظار/انجام‌شده).
- نمایش کلاس CSS وضعیت (error/success) در پاسخ.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF-%D9%BE%DB%8C%D8%B4%D9%86%D9%87%D8%A7%D8%AF%DB%8C%3A-re" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ پیشنهادی: `ReadCreditDebitList`.
- فیلدها: `operator_id`، `branch`، `filters`.
- سطح لاگ: **Info**.

</div>### جمع‌بندی

این مسیر برای واکشی فهرست بدهکار/بستانکار طراحی شده است. با احراز JWT و صفحه‌بندی ایمن امکان فیلتر تطبیقی دارد. در نسخه بعدی افزودن پارامترهای مرتب‌سازی و کش توصیه می‌شود.

<div id="bkmrk-credit-debit-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /credit-debit/summary

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/credit-debit/summary</td><td dir="ltr">V2CreditDebitController@summary</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازیابی خلاصه مالی تراکنش‌های بدهکار و بستانکار (Credit/Debit) مربوط به همکار یا مرجع مشخص.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **summary** برای ارائه یک گزارش کلی از وضعیت مالی کاربر، همکار یا مرجع طراحی شده است. فرآیند به این صورت است که ابتدا نوع موجودیت (`typeDb`) و شناسه شناسایی (`company` یا `reference`) تعیین می‌شود. سپس با استفاده از متدهای `StaticController::getFinancialPasts()`، داده‌های سال مالی جاری و دوره‌های باز و بسته جمع‌آوری شده، بدهی و بستانکاری کل محاسبه و بازگردانده می‌شوند.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>حاوی تنظیمات فیلتر و دوره زمانی مالی.</td></tr><tr><td>branch</td><td>integer</td><td>Header/JWT</td><td>بله</td><td>شناسه شعبه مبدا برای محدودسازی داده‌ها.</td></tr><tr><td>advanced.from</td><td>string (YYYY/MM/DD)</td><td>Body→JSON</td><td>خیر</td><td>تاریخ شروع دوره مورد بررسی.</td></tr><tr><td>advanced.fpopen</td><td>boolean</td><td>Body→JSON</td><td>خیر</td><td>اگر false باشد فقط دوره بسته را بررسی می‌کند.</td></tr><tr><td>type</td><td>string</td><td>Body→JSON</td><td>خیر</td><td>نوع حساب (colleague, reference, aggregation).</td></tr><tr><td>company</td><td>integer</td><td>Body→JSON</td><td>بله</td><td>شناسه مرجع همکار.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "meta": {
    "timestamp": 1732287600
  },
  "summary": {
    "total_credit": 12500000,
    "total_debit": 8400000,
    "opening_balance": 3100000,
    "closing_balance": 4100000
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85%DB%8C-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تمامی درخواست‌ها باید با احراز JWT معتبر ارسال شوند.
- دسترسی فقط برای کاربران دارای نقش مدیریتی یا مالی مجاز است.
- مقادیر مالی واقعی (credit/debit) نباید در لاگ عمومی ثبت شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D9%85%D8%A7%D9%84%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فراخوانی توابع مالی پرهزینه (`getFinancialPasts`) بهتر است با کش Redis ذخیره شوند.
- محاسبه سال مالی به صورت `StaticController::getYearFinancial()` صورت می‌گیرد و باید فقط یکبار در هر درخواست اجرا شود.
- در صورت حجم زیاد تراکنش‌ها، پیشنهاد می‌شود فیلدهای aggregate در جدول جداگانه نگهداری شوند.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Morilog\\Jalali\\Jalalian;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\StaticController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی JSON ناقص یا فرمت اشتباه در دوره مالی.</td><td>json\_decode()</td></tr><tr><td>404</td><td>داده مالی یا شناسه همکار یافت نشد.</td><td>DB Query</td></tr><tr><td>500</td><td>خطا در محاسبه مجموع بدهی/بستانکاری.</td><td>getFinancialPasts()</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%86%D9%82%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال اعتبارسنجی نقش قبل از فراخوانی تابع اصلی.
- بررسی شناسه شعبه با داده‌های JWT برای جلوگیری از تزریق Cross-Branch.
- حذف داده‌های تراکنش خام قبل از پاسخ خروجی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D8%A7%D9%86%D8%AA%D8%AE%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر انتخاب بازه دلخواه تاریخ به‌جای سال مالی ثابت.
- نمایش فیلد `net_balance` (credit-debit) در پاسخ برای استفاده سریع.
- افزودن پشتیبانی برای واحد پولی غیر IRR (مثلاً USD).

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-readfinanci" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `ReadFinancialSummary`
- فیلدهای گزارش‌شده: `user_id`، `branch`، `company`، `year`
- سطح لاگ: **Info**
- زمان‌بندی پیشنهادی برای ذخیره لاگ: هر بار تغییر در فیلدهای مالی.

</div>### جمع‌بندی

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

<div id="bkmrk-credit-debit-summary" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /pay/store

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/pay/store</td><td dir="ltr">V2CreditDebitController@storePay</td><td dir="ltr">authWithJwt</td><td dir="rtl">ذخیره رکوردهای پرداخت (Pay) جدید همراه با اطلاعات حساب، طرف حساب و تراکنش مالی؛ بروزرسانی کش مالی در Redis.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **storePay** برای ثبت گروهی تراکنش‌های پرداخت و دریافتی طراحی شده است. ابتدا داده درخواست (`$request->data`) پردازش و شناسه مالی سال جاری بر اساس تابع `StaticController::getYearFinancial()` محاسبه می‌شود. سپس رکوردها در جدول `pays` درج می‌گردند. در پایان، بر اساس نوع حساب (`reference` یا `aggregation`)، اطلاعات مالی حساب مربوطه از طریق `TradeController::financial()` بازخوانی شده و در Redis کش می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>آرایه‌ای از تراکنش‌ها شامل اطلاعات پرداخت و دریافتی.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>کد شعبه مبدا برای محدودسازی تراکنش‌ها.</td></tr><tr><td>data\[\].type</td><td>string</td><td>Body→JSON</td><td>بله</td><td>نوع تراکنش (payment یا receive).</td></tr><tr><td>data\[\].type\_pay</td><td>string</td><td>Body→JSON</td><td>بله</td><td>نوع پرداخت (transfer, accounting, coupon, contract).</td></tr><tr><td>data\[\].currency\_amount</td><td>integer</td><td>Body→JSON</td><td>بله</td><td>مبلغ تراکنش (ریال).</td></tr><tr><td>data\[\].tracking\_code</td><td>string</td><td>Body→JSON</td><td>خیر</td><td>کد رهگیری پرداخت در سیستم.</td></tr><tr><td>data\[\].wage</td><td>integer</td><td>Body→JSON</td><td>خیر</td><td>کارمزد تراکنش.</td></tr><tr><td>data\[\].functor\_account</td><td>integer</td><td>Body→JSON</td><td>خیر</td><td>شناسه کاربر اجراکننده تراکنش (functor).</td></tr><tr><td>data\[\].status.id</td><td>integer</td><td>Body→JSON</td><td>بله</td><td>شناسه وضعیت تراکنش (در انتظار/تایید‌شده).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "meta": {
    "timestamp": 1732287600
  },
  "inserted_count": 3,
  "ids": [1089, 1090, 1091],
  "cached_financial": true
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%AD%D8%AA%D9%85%D8%A7%D9%8B-%D8%A7%D9%86%D8%AC%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز JWT حتماً انجام می‌شود و شناسه شعبه از توکن استخراج می‌گردد.
- ورودی JSON باید از لحاظ ساختار و مقادیر کلیدی اعتبارسنجی شود.
- رشته‌های عددی مانند مبلغ حتماً قبل از درج به فرمت انگلیسی تبدیل می‌شوند تا از تزریق داده جلوگیری شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1%D8%AC-%DA%AF%D8%B1%D9%88%D9%87%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%D9%87%D8%A7-%D8%A8%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- درج گروهی رکوردها با استفاده از `DB::table('pays')->insert()` انجام می‌شود که سرعت بالایی دارد.
- در انتهای عملیات، بلافاصله کش Redis برای حساب‌های `reference` و `aggregation` به‌روزرسانی می‌شود.
- پیشنهاد به batch insert به همراه pipeline Redis (TTLs=300s) برای بازدهی بهتر.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\TradeController;
- use App\\Http\\Controllers\\StaticController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی JSON نامعتبر یا پارامتر ناقص.</td><td>json\_decode()</td></tr><tr><td>403</td><td>کاربر بدون مجوز برای انجام نوع تراکنش.</td><td>authWithJwt</td></tr><tr><td>500</td><td>خطا در ثبت گروهی یا بروزرسانی کش Redis.</td><td>DB/Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کنترل دسترسی بر اساس نقش: فقط کاربران با سطح `financial.editor` مجاز به درج هستند.
- رمزنگاری tracking\_code در صورت ارسال از کلاینت‌های عمومی.
- جلوگیری از ارسال دوباره (duplicate) در درخواست‌های همزمان با قفل Redis.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-rollbac" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امکان rollback خودکار در صورت خطا در بروزرسانی کش.
- مدیریت تراکنش چندمرحله‌ای با Savepoints برای atomicity بهتر.
- پشتیبانی از چند واحد پولی هم‌زمان (multi-currency).

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-storepaytra" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `StorePayTransaction`.
- فیلدهای ثبت: `operator_id`، `branch`، `ids`، `currency_amount`.
- سطح لاگ: **Critical**.
- مدت نگهداری: ۹۰ روز در پایگاه audit\_logs.

</div>### جمع‌بندی

این مسیر مسئول ثبت تراکنش‌های پرداخت و دریافتی و بروزرسانی مالی Redis است. عملیات سریع گروهی و مدیریت مالی اتومات باعث کاهش سربار حسابداری می‌شود. فعال‌سازی کنترل نقش و اعتبارسنجی ساختار JSON برای امنیت ضروری است.

<div id="bkmrk-pay-store" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /pay/update

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/pay/update</td><td dir="ltr">V2CreditDebitController@updatePay</td><td dir="ltr">authWithJwt</td><td dir="rtl">بروزرسانی اطلاعات تراکنش پرداخت ثبت‌شده (Pay) شامل نوع، مبلغ، تاریخ و وضعیت تراکنش.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **updatePay** برای اصلاح رکوردهای موجود در جدول `pays` طراحی شده است. ابتدا داده‌های ارسالی از درخواست (`$request->json`) به‌صورت آرایه دیکد شده و صحت کلیدهای الزامی مانند `id` بررسی می‌شود. سپس با استفاده از `DB::table('pays')->where('id', ...)->update([...])` مقداردهی مجدد فیلدهای تراکنش انجام می‌گیرد. پس از اتمام عملیات، کش Redis برای مرجع مربوطه (`reference:<id>:information`) با داده‌های جدید بازسازی می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>حاوی داده‌های تراکنش اصلاح‌شده.</td></tr><tr><td>id</td><td>integer</td><td>Body→JSON</td><td>بله</td><td>شناسه تراکنش در جدول `pays` که باید بروزرسانی شود.</td></tr><tr><td>type</td><td>string</td><td>Body→JSON</td><td>بله</td><td>نوع تراکنش (payment یا receive).</td></tr><tr><td>currency\_amount</td><td>string</td><td>Body→JSON</td><td>بله</td><td>مبلغ جدید تراکنش.</td></tr><tr><td>wage</td><td>string</td><td>Body→JSON</td><td>خیر</td><td>کارمزد اصلاح‌شده تراکنش.</td></tr><tr><td>status.id</td><td>integer</td><td>Body→JSON</td><td>خیر</td><td>شناسه وضعیت جدید تراکنش.</td></tr><tr><td>deadline</td><td>string (YYYY/MM/DD)</td><td>Body→JSON</td><td>خیر</td><td>تاریخ جدید مهلت پرداخت.</td></tr><tr><td>description</td><td>string</td><td>Body→JSON</td><td>خیر</td><td>توضیحات تراکنش.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "updated_id": 1091,
  "meta": {
    "timestamp": 1732287600
  },
  "cache_updated": true
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86-%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت کاربران از طریق JWT انجام می‌شود.
- بروزرسانی فقط در محدوده همان شعبه کاربر امکان‌پذیر است.
- قبل از هر عملیات به‌روزرسانی، شناسه تراکنش با branch مرتبط بررسی می‌شود تا از Cross-Branch Injection جلوگیری گردد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%D8%AC%D9%88%D8%AF-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در صورت وجود تراکنش‌های سنگین، استفاده از `updateBatch()` پیشنهاد می‌شود.
- بروزرسانی کش Redis به‌صورت asynchronous برای پاسخ سریع‌تر انجام شود.
- توصیه به ذخیره تغییرات در جدول `audit_logs` با سطح Critical.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\TradeController;
- use App\\Http\\Controllers\\StaticController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی JSON ناقص یا فرمت نامعتبر.</td><td>json\_decode()</td></tr><tr><td>404</td><td>تراکنش با شناسه داده‌شده یافت نشد.</td><td>DB Query</td></tr><tr><td>500</td><td>خطای سیستم در بروزرسانی یا بازسازی کش Redis.</td><td>DB/Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-%D9%86%D8%B1%D8%AE-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن محدودیت نرخ بروزرسانی برای جلوگیری از حملات DDoS مالی.
- ثبت هش جدید در فیلد `hash_signature` برای تشخیص تغییرات بحرانی.
- اعمال RBAC بر اساس سطح دسترسی "financial.moderator".

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از بروزرسانی گروهی چند تراکنش همزمان.
- در پاسخ خروجی نمایش جزئیات قبل و بعد از تغییر (diff snapshot).
- ثبت تاریخچه همه تغییرات در جدول کمکی `pays_history`.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-updatepaytr" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `UpdatePayTransaction`.
- فیلدهای ثبت: `operator_id, branch, pay_id, old_data, new_data`.
- سطح لاگ: **Critical**.
- مدت نگهداری ممیزی: ۱۸۰ روز.

</div>### جمع‌بندی

این مسیر برای بروزرسانی امن و سریع تراکنش‌های پرداخت طراحی شده است. با احراز JWT، کنترل نقش، و همگام‌سازی کش Redis، سازوکار کاملاً قابل اعتماد برای ویرایش داده‌های مالی فراهم می‌کند.

<div id="bkmrk-pay-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# DELETE /pay/trash

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">DELETE</td><td dir="ltr">/api/v2/pay/trash</td><td dir="ltr">V2CreditDebitController@trashPay</td><td dir="ltr">authWithJwt</td><td dir="rtl">حذف نرم سند پرداخت از سیستم مالی با بررسی دوره مالی بسته‌شده و بروزرسانی کش Redis.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **trashPay** برای حذف امن و نرم (soft-delete) تراکنش‌های پرداخت طراحی شده است. پیش از حذف، با استفاده از پارامتر تنظیمات دفتر (`END_OF_FINANCIAL_PERIOD_CLOSING_ACCOUNTS`) دوره مالی بسته‌شده بررسی می‌شود تا حذف تراکنش‌های مربوط به دوره بسته‌شده ممنوع شود. سپس وضعیت رکورد موردنظر در جدول `pays` به مقدار `status = 5` تغییر می‌کند و کش مالی در Redis بازسازی می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>pay\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه رکورد پرداخت برای حذف.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه اجرایی جهت بررسی دوره مالی.</td></tr><tr><td>operator</td><td>object</td><td>JWT</td><td>بله</td><td>کاربر اجراکننده عملیات.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A8%D8%B1%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بررسی توکن JWT برای تعیین شعبه و اپراتور الزامی است.
- در صورت بسته بودن دوره مالی، حذف نخواهد شد و کد خطای 5007 برگردانده می‌شود.
- در حذف فیزیکی هیچ داده‌ای از DB پاک نمی‌شود، فقط وضعیت تغییر می‌یابد.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D8%B4-redis-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کش Redis برای مراجع مرتبط (`reference:id:information`) بلافاصله بازسازی می‌شود.
- در عملیات گروهی حذف چند سند بهتر است از صف Async برای بازسازی Redis استفاده شود.
- زمان تقریبی اجرای عملیات **کمتر از 300ms** در حجم 1 سند است.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\TradeController;
- use Carbon\\Carbon;
- use App\\Jobs\\SystemLog;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-5007" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>5007</td><td>دوره حسابداری بسته است؛ حذف سند ممکن نیست.</td><td>officeConfig()</td></tr><tr><td>5005</td><td>خطا در عملیات حذف یا Redis.</td><td>catch(Exception $e)</td></tr><tr><td>403</td><td>عدم تطابق شعبه با دوره مالی جاری.</td><td>authWithJwt</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-%D8%AF%D8%B1-%D8%AD%D8%B0%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال محدودیت در حذف توسط نقش مالی ارشد (`financial.admin`).
- ثبت هر حذف در `SystemLog` با Queue دیر‌اجرا (`snailJob`).
- رمزگذاری شناسه سند در درخواست کلاینت جهت جلوگیری از تزریق ID.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%AF%D8%B2%DB%8C%D9%86%D9%87-restore" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن گزینه `restore` برای بازیابی اسناد حذف‌شده.
- مدیریت نسخه تاریخچه حذف‌ها در `audit_logs`.
- افزودن کش گروهی برای حذف‌های تجمیعی در سطح شعبه.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-trashpay.-%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `TrashPay`.
- فیلدهای ثبت: `operator_id, branch, pay_id`.
- سطح لاگ: **Critical**.
- نگهداری لاگ: ۹۰ روز.

</div>### جمع‌بندی

تابع **trashPay** حذف نرم و کنترل‌شده تراکنش‌های پرداخت را با بررسی بسته بودن دوره مالی امکان‌پذیر می‌سازد. پس از حذف، داده‌های Redis مجدداً بروزرسانی می‌شوند تا توازن مالی حفظ گردد.

<div id="bkmrk-pay-trash" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /gateway/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/gateway/list</td><td dir="ltr">V2CreditDebitController@listGateway</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت لیست درگاه‌های پرداخت فعال برای شعبه، با پشتیبانی از فیلتر و وضعیت تراکنش.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **listGateway** برای واکشی تمامی درگاه‌های پرداخت متصل به شعبه و سیستم مالی طراحی شده است. با استفاده از ورودی DataTables، ابتدا پارامترهای فیلتر (مثل وضعیت، نوع درگاه، تاریخ ایجاد، یا شرکت) اعمال می‌گردند، سپس تراکنش‌های پرداختی اخیر مرتبط با هر درگاه از جدول `gateways` واکشی می‌شود.

در صورت فعال بودن سرویس تسویه خودکار، تابع داده‌ها را با نتیجه تسویه Redis ترکیب می‌کند تا وضعیت لحظه‌ای مانده هر درگاه نمایش داده شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>draw</td><td>integer</td><td>Body (DataTables)</td><td>بله</td><td>شماره درخواست برای هماهنگی پاسخ با DataTables.</td></tr><tr><td>length</td><td>integer</td><td>Body</td><td>بله</td><td>تعداد آیتم‌ها در هر صفحه.</td></tr><tr><td>start</td><td>integer</td><td>Body</td><td>بله</td><td>مقدار offset شروع لیست.</td></tr><tr><td>advanced</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای پیشرفته (status, provider, type).</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه جهت واکشی درگاه‌ها.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "meta": { "timestamp": 1732287600, "draw": 1 },
  "items": [
    {
      "id": 12,
      "title": "Zarinpal Gateway",
      "status": "active",
      "transactions_count": 215,
      "last_update": "1404/09/01 11:22",
      "balance": {
        "wallet": 48000000,
        "pending": 1200000
      }
    }
  ],
  "recordsTotal": 15,
  "recordsFiltered": 15
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B4%D9%86%D8%A7%D8%B3%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT برای شناسایی شعبه اجباری است.
- نمایش جزئیات تراکنش‌ها فقط برای نقش‌های دارای مجوز `financial.gateway.view`.
- درگاه‌های غیرفعال در خروجی حذف می‌شوند مگر نقش کاربر `developer` باشد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-redis%3A%3Age" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از `Redis::get('gateway:list:{branch}')` برای کش خروجی حدود ۶۰۰ ثانیه.
- در صورت حجم بالا (بیش از ۲۰ درگاه)، واکشی وضعیت تراکنش‌ها باید async باشد.
- کد SQL شامل ایندکس روی ستون‌های `branch` و `status` برای کاهش latency.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\StaticController;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی DataTables نامعتبر یا ناقص.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر دسترسی مشاهده ندارد.</td><td>authWithJwt</td></tr><tr><td>500</td><td>خطا در Redis یا Query پایگاه داده.</td><td>DB Query</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%AF%D8%B1%DA%AF%D8%A7%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری شناسه درگاه در فرانت‌اند قبل از ارسال.
- اعمال Rate Limit ۱۰ درخواست در دقیقه به ازای هر IP.
- بررسی Double-fetch برای جلوگیری از حملات Enumeration.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-balan" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `balance_history:true` برای گزارش تغییرات موجودی.
- ترکیب مستقیم کش Redis با جدول تراکنش‌ها برای کاهش latency.
- پشتیبانی از درگاه‌های رمز‌ارز در فاز آینده.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-listgateway" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `ListGateway`.
- فیلدهای ذخیره‌شده: `operator_id, branch, filter_used`.
- سطح لاگ: **Info**.
- نگهداری در جدول `audit_logs` تا ۳۰ روز.

</div>### جمع‌بندی

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

<div id="bkmrk-gateway-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /financial/items

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/financial/items</td><td dir="ltr">V2CreditDebitController@financialItems</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت ساختار و آیتم‌های حساب مالی یک مؤلفه مشخص مانند دفتر، شرکت یا مرجع برای نمایش یا محاسبه درخت حسابداری.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **financialItems** وظیفه دارد بر اساس نوع حساب و شناسه داده‌شده در درخواست (مثل نوع `moeen` یا `preference`)، آیتم‌های مالی مرتبط را از جداول پایه حسابداری واکشی و سازمان‌دهی کند. این داده‌ها معمولاً جهت ساخت نمای درختی حساب‌ها (TreeView) در رابط کاربری استفاده می‌شوند.

در ابتدای اجرای تابع، مجموعه حساب‌های سطح پایه از `accounting_titles` خوانده می‌شود؛ سپس بر اساس نوع ورودی (`request->type`)، شاخه‌های فعال یا غیرفعال علامت‌گذاری می‌گردند. هر آیتم دارای کلیدهایی مانند `subset`، `locked` و `display` است که در خروجی JSON تنظیم می‌شوند.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>بله</td><td>نوع حساب مالی که باید آیتم‌های آن واکشی شوند (مثلاً moen یا preference).</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه‌ای که داده‌هایش باید فیلتر شود.</td></tr><tr><td>filters</td><td>object</td><td>Body</td><td>خیر</td><td>فیلتر اختیاری شامل وضعیت حساب یا سطح دسترسی.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "items": [
    {
      "id": 1,
      "title": "دارایی‌های جاری",
      "subset": true,
      "locked": false,
      "display": true
    },
    {
      "id": 2,
      "title": "بدهی‌های جاری",
      "subset": false,
      "locked": false,
      "display": true
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A8%D8%B1%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز توکن JWT برای تعیین شعبه ضروری است.
- دسترسی به این API محدود به نقش‌های دارای مجوز `financial.viewer` یا بالاتر است.
- هیچ داده‌ای از حساب‌های غیرفعال یا قفل شده برای `non-admin` برگردانده نمی‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A2%DB%8C%D8%AA%D9%85%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-redis-%DA%A9%D8%B4-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- آیتم‌ها از Redis کش شوند با کلید `financial:items:{branch}:{type}` و مدت اعتبار 1800 s.
- در صورت تغییر ساختار حساب، کش باید پاکسازی (invalidate) شود.
- زمان پاسخ کمتر از 200 ms در بار معمولی است.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\StaticController;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>نوع حساب نامعتبر یا فیلد type خالی است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر دسترسی مشاهده ندارد.</td><td>Middleware JWT</td></tr><tr><td>500</td><td>خطا در واکشی داده‌ها.</td><td>DB/Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-rbac-%D8%A8%D8%B1%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از RBAC برای کنترل سطوح دسترسی view/edit.
- رمزگذاری خروجی آیتم‌ها درون HTTPS جهت جلوگیری از تزریق داده.
- ثبت لاگ‌ ممیزی در سطح Info برای هر درخواست.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-pagina" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت pagination برای آیتم‌های حجیم حسابداری.
- پشتیبانی از جستجوی fuzzy روی نام آیتم‌ها.
- افزودن شاخص version برای هر ساختار مالی جهت همگام‌سازی Frontend.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-financialit" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `FinancialItemsList`.
- فیلدها: `branch, type, operator_id`.
- سطح لاگ: **Info**.
- مدت نگهداری لاگ: ۳۰ روز.

</div>### جمع‌بندی

تابع **financialItems** یکی از نقاط مرکزی در ماژول مالی است که ساختار طبقه‌بندی حساب‌ها را به شکل امن و سریع در اختیار رابط کاربری قرار می‌دهد. این تابع با ترکیب Redis Cache و درخت حسابداری پایگاه داده، عملکرد پایدار و انعطاف‌پذیر ارائه می‌دهد.

<div id="bkmrk-financial-items" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /check/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/check/list</td><td dir="ltr">V2CreditDebitController@listCheck</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت لیست چک‌های مالی ثبت‌شده برای شرکت یا شعبه، با فیلتر و مرتب‌سازی پیشرفته DataTables.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **listCheck** از روی پارامترهای DataTables (مثل draw، start، length و فیلترهای پیشرفته) لیست چک‌ها را از جدول `announcements` یا `pays` واکشی می‌کند. این چک‌ها ممکن است متعلق به پرداخت یا دریافت در سیستم مالی باشند.

در صورت وجود داده کش‌شده، نتیجه از Redis با کلید `check:list:{branch}` خوانده می‌شود. اگر کش خالی بود، داده‌ها با فیلتر وضعیت (`status`)، تاریخ (`date\_from`, `date\_to`) و نوع چک (`payment` یا `receive`) واکشی می‌شوند و سپس در JSON نهایی با متادیتا و شمارنده رکوردها بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>draw</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه درخواست DataTables.</td></tr><tr><td>start</td><td>integer</td><td>Body</td><td>بله</td><td>موقعیت شروع رکورد.</td></tr><tr><td>length</td><td>integer</td><td>Body</td><td>بله</td><td>تعداد رکوردهای هر صفحه.</td></tr><tr><td>advanced</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای پیشرفته شامل status، type، company، date\_from و date\_to.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه فعلی برای تفکیک داده‌ها.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "meta": { "timestamp": 1732287600, "draw": 1 },
  "data": [
    {
      "id": 107,
      "serial_id": 10087,
      "status": "active",
      "title": "چک پرداختی شرکت آلفا",
      "company": {
        "id": 504,
        "title": "آلفا سیستم"
      },
      "amount": 18500000,
      "currency": "IRR",
      "deadline": "1404/09/15",
      "type": "payment",
      "tracking_code": "CHK-984723",
      "created_at": "1404/08/30"
    }
  ],
  "recordsTotal": 27,
  "recordsFiltered": 27
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%86%D9%82%D8%B4%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای نقش‌های دارای مجوز `financial.check.view`.
- درخواست‌ها باید دارای توکن JWT معتبر باشند.
- فیلترهای زمان باید اعتبارسنجی شوند تا از injection جلوگیری شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D9%88%D9%84%D9%88%DB%8C%D8%AA-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%A7%D8%B2-redi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اولویت واکشی از Redis با TTL=900s.
- Queryهای دارای ایندکس روی فیلدهای `status`, `deadline`, `branch`.
- در صورت حجم بالا، مجموعه چک‌ها به‌صورت batch تقسیم می‌شوند.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;
- use App\\Http\\Controllers\\StaticController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامترهای لیست ناقص یا نامعتبر.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به مشاهده چک‌ها نیست.</td><td>authWithJwt</td></tr><tr><td>500</td><td>خطا در Query یا کش Redis.</td><td>DB Layer</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%86%DA%A9-%D8%AF%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری شناسه چک در فرانت‌اند قبل از ارسال.
- اعمال Rate Limit ۵ req/min برای جلوگیری از enumeration.
- استفاده از HTTPS و بررسی timestamp پاسخ برای جلوگیری از replay.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%86%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن فیلتر برای نمایش «چک‌های منقضی».
- پشتیبانی از export به Excel و PDF.
- افزودن گزینه search روی گیرنده یا شماره چک.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-listcheck.-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `ListCheck`.
- فیلدهای ذخیره‌شده: `operator_id, branch, filters_applied`.
- سطح لاگ: **Info**.
- مدت نگهداری در جدول `audit_logs`: ۳۰ روز.

</div>### جمع‌بندی

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

<div id="bkmrk-check-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /check/store

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/check/store</td><td dir="ltr">V2CreditDebitController@storeCheck</td><td dir="ltr">authWithJwt</td><td dir="rtl">ثبت گروهی چک‌های مالی در جدول پرداخت‌ها و بروزرسانی کش مالی شعبه.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **storeCheck** داده‌های ارسالی مربوط به چک‌های مالی را از بدنه درخواست دریافت می‌کند و پس از بررسی نوع پرداخت (پرداخت یا دریافت)، آن‌ها را در جدول `pays` درج می‌کند.

شناسه سریال هر چک از طریق تابع `StaticController::getSerialId('pay', branch, year)` تعیین می‌شود. بعد از ثبت درون تراکنش `DB::transaction`، کش اطلاعات مالی در `Redis` بروزرسانی می‌شود تا تغییرات لحظه‌ای اعمال شوند.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>data</td><td>array</td><td>Body</td><td>بله</td><td>آرایه‌ای از چک‌ها جهت ثبت تکی یا گروهی.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه ثبت‌کننده.</td></tr><tr><td>operator</td><td>object</td><td>JWT</td><td>بله</td><td>شناسه کاربر ثبت‌کننده چک‌ها.</td></tr><tr><td>object\_type</td><td>string</td><td>Body</td><td>بله</td><td>نوع هدف چک (reference, aggregation, colleague).</td></tr><tr><td>object</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه منبع مالی یا فاکتور مربوط.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "result": {
    "inserted": 5,
    "tracking_ids": ["CHK-88421", "CHK-88422", "CHK-88423"],
    "cached": "financial:branch:5:synced"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت JWT اجباری است.
- فقط نقش‌های دارای سطح `financial.check.store` مجاز به درج هستند.
- بررسی موجودی حساب قبل از درج تراکنش‌ها انجام می‌شود (Wallet Check).

</div>### نکات عملکردی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%DA%86%DA%A9%E2%80%8C%D9%87%D8%A7-%D8%AF%D8%B1%D9%88%D9%86-db%3A%3At" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت چک‌ها درون `DB::transaction` باعث حفظ یکپارچگی داده‌های مالی می‌شود.
- در پایان درج، داده‌های مرتبط در Redis با کلید `reference:{object}:information` بروزرسانی می‌شوند.
- می‌توان در نسخه آینده cache invalidation هوشمند بر اساس `year financial` افزود.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\TradeController;
- use App\\Http\\Controllers\\StaticController;
- use Carbon\\Carbon;
- use App\\Models\\Pay;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>داده‌های چک ناقص یا نامعتبر.</td><td>Validator</td></tr><tr><td>403</td><td>دسترسی مجاز برای ثبت چک وجود ندارد.</td><td>authWithJwt</td></tr><tr><td>500</td><td>خطا در درج داده‌ها یا بروزرسانی کش Redis.</td><td>DB Layer / Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%86%DA%A9-%D9%88-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری شناسه چک و شماره حساب در فرانت‌اند.
- اعمال محدودیت ۵ درخواست در دقیقه برای هر کاربر.
- بررسی Anti‑Replay برای درخواست‌های تکراری با همان Tracking Code.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%81%DB%8C%D9%84%D8%AF-wallet_e" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن فیلد `wallet_effect:true` برای بروزرسانی بلادرنگ کیف پول.
- پشتیبانی از درج async جهت کاهش latency.
- اتصال به سرویس گزارش مالی مرکزی جهت اعتبارسنجی چک‌ها قبل از ثبت.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-storecheck." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `StoreCheck`.
- ثبت فیلدها: `operator_id, branch, count_inserted`.
- سطح لاگ: **Notice**.
- مدت نگهداری: ۳۰ روز در جدول `audit_logs`.

</div>### جمع‌بندی

تابع **storeCheck** مسئول ثبت تراکنش‌های مالی نوع چک است و پس از درج در پایگاه داده، تمام کش‌های مالی مرتبط با شعبه یا منبع هدف را بروزرسانی می‌کند. طراحی تابع به‌صورت اتمیک، امن و مناسب عملیات گروهی چک‌ها پیاده شده است.

<div id="bkmrk-check-store" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /check/operation

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/check/operation</td><td dir="ltr">V2CreditDebitController@operationCheck</td><td dir="ltr">authWithJwt</td><td dir="rtl">انجام عمل خاص بر روی چک مالی، مانند تغییر وضعیت، تأیید، لغو یا تسویه.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **operationCheck** عمل انتخاب‌شده را روی چک مشخص‌شده اجرا می‌کند. بسته به نوع ورودی (`action`) مقدار ستون‌های `status`، `payment_state` یا `tracking_code` در جدول `pays` بروزرسانی می‌شوند.

پس از موفقیت عملیات، کش Redis مربوط به چک و اطلاعات مالی شعبه با کلید‌های `financial:branch:{branch}` و `check:{id}:information` بروزرسانی یا حذف می‌شوند تا اطلاعات جدید منعکس گردد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه چک مورد نظر برای انجام عملیات.</td></tr><tr><td>action</td><td>string</td><td>Body</td><td>بله</td><td>نوع عملیات. مقادیر مجاز: `confirm`، `cancel`، `block`، `unblock`، `settle`.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه مالی.</td></tr><tr><td>operator</td><td>object</td><td>JWT</td><td>بله</td><td>شناسه اپراتور اجرایی.</td></tr><tr><td>reason</td><td>string</td><td>Body</td><td>خیر</td><td>علت تغییر وضعیت (برای لاگ و ممیزی).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "result": {
    "check_id": 109,
    "action": "confirm",
    "verified_by": 2704,
    "financial_cache": "redis:branch:5:updated",
    "message": "وضعیت چک با موفقیت بروزرسانی شد"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%AC%D9%88%D8%B2-%D9%84%D8%A7%D8%B2%D9%85%3A-financial" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مجوز لازم: `financial.check.edit`.
- فقط کاربران نقش حسابدار ارشد یا مدیر شعبه حق انجام عملیات دارند.
- تمام تغییرات در جدول `audit_logs` ثبت می‌شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%87%D9%85%E2%80%8C%D8%B2%D9%85%D8%A7%D9%86-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌روزرسانی هم‌زمان کش Redis پس از هر تغییر وضعیت چک.
- استفاده از تراکنش `DB::transaction` برای جلوگیری از ناسازگاری وضعیت چک‌ها.
- TTL کش‌های مرتبط: 300 ثانیه برای داده‌ی شعبه و 30 ثانیه برای داده‌ی چک منفرد.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;
- use App\\Models\\Pay;
- use App\\Http\\Controllers\\StaticController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامترها ناقص یا نوع عملیات نامعتبر است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به انجام این عملیات نیست.</td><td>authWithJwt</td></tr><tr><td>404</td><td>چک مورد نظر یافت نشد.</td><td>DB</td></tr><tr><td>500</td><td>خطای داخلی پایگاه داده یا بروزرسانی کش.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-rate%E2%80%91limit-%D8%A8%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال Rate‑Limit برای عملیات حساس مالی (۱ عمل در هر ۱۰ ثانیه).
- ثبت IP کاربر در ممیزی و ارسال هشدار در تغییر وضعیت غیرعادی.
- رمزنگاری شناسه چک در فرانت‌اند.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%A7%D9%86%D8%AC%D8%A7%D9%85-%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امکان انجام عملیات گروهی روی چند چک.
- اضافه کردن فیلد `verified_at` در جدول برای ردیابی دقیق زمان تأیید.
- پشتیبانی از وب‌هوک برای اطلاع‌رسانی تغییر وضعیت به سرویس‌های همکار.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-operationch" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `OperationCheck`.
- فیلدهای ثبت‌شونده: `check_id, action, operator_id, branch, reason`.
- سطح لاگ: **Audit**.
- مدت نگهداری: ۹۰ روز.

</div>### جمع‌بندی

تابع **operationCheck** مسئول تغییر وضعیت چک‌های مالی در سیستم است و ضمن رعایت کنترل دسترسی، بروزرسانی ایمن داده‌ها و کش Redis را انجام می‌دهد. این مسیر یکی از نقاط حساس چرخه مالی محسوب می‌شود و برای تضمین صحت داده باید تحت لاگ کامل و ممیزی نگهداری شود.

<div id="bkmrk-check-operation" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /check/operation/update

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/check/operation/update</td><td dir="ltr">V2CreditDebitController@operationUpdateCheck</td><td dir="ltr">authWithJwt</td><td dir="rtl">به‌روزرسانی اطلاعات عملیات چک (operation record) شامل نوع، تاریخ و توضیحات، و بروز رسانی کش مالی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **operationUpdateCheck** برای تغییر جزئیات یک عملیات ثبت‌شده روی چک استفاده می‌شود، مانند اصلاح نوع عملیات (`type`)، تاریخ اجرا، توضیحات یا وضعیت آن.

ابتدا رکورد عملیات از جدول `check_operations` واکشی و اعتبارسنجی می‌شود. سپس تغییرات ذخیره شده و هرگونه وابستگی مبلغ یا وضعیت چک هماهنگ می‌گردد. پس از آن کش Redis مرتبط با گزارش چک و مالی شعبه ریفرش می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>operation\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه عملیات چک که باید بروزرسانی شود.</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>خیر</td><td>نوع جدید عملیات (مانند `assignment`، `cash`، `clearing`).</td></tr><tr><td>date</td><td>string (YYYY-MM-DD)</td><td>Body</td><td>خیر</td><td>تاریخ جدید عملیات.</td></tr><tr><td>description</td><td>string</td><td>Body</td><td>خیر</td><td>توضیحات عملیات.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه مالی مرتبط.</td></tr><tr><td>operator</td><td>object</td><td>JWT</td><td>بله</td><td>اطلاعات کاربری اجراکننده تغییر.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "result": {
    "operation_id": 305,
    "updated_fields": ["type", "description"],
    "cache": "financial:branch:5:refreshed",
    "message": "جزئیات عملیات چک با موفقیت بروزرسانی شد"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%AC%D9%88%D8%B2-%D9%86%D9%82%D8%B4%3A-financial." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مجوز نقش: `financial.check.operation.update` ضروری است.
- رکورد عملیات باید به شعبه کاربر تعلق داشته باشد.
- تمام تغییرات در جدول ممیزی `audit_logs` ذخیره می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%A8%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D8%AF%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات بروزرسانی در تراکنش پایگاه داده انجام می‌شود.
- بازسازی کش محدود به این چک و شعبه انجام می‌گیرد.
- TTL کش‌های جدید: 300 ثانیه.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Models\\CheckOperation;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی‌ها ناقص یا نامعتبر.</td><td>Validator</td></tr><tr><td>403</td><td>دسترسی غیرمجاز برای بروزرسانی عملیات چک.</td><td>authWithJwt</td></tr><tr><td>404</td><td>شناسه عملیات یافت نشد.</td><td>DB</td></tr><tr><td>500</td><td>خطا در به‌روزرسانی یا بازسازی کش.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D8%AA%D8%A3%DB%8C%DB%8C%D8%AF-%D8%AF%D9%88%D9%85%D8%B1%D8%AD%D9%84%D9%87%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال تأیید دومرحله‌ای برای تغییرات حساس.
- ثبت IP و User-Agent اپراتور در لاگ ممیزی.
- اعمال محدودیت تعداد تغییر در بازه زمانی مشخص.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از نسخه‌بندی تغییرات عملیات چک.
- امکان بروزرسانی گروهی عملیات‌های مشابه.
- ارسال اعلان خودکار به ذینفعان در تغییر وضعیت عملیات.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-operationup" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `OperationUpdateCheck`.
- فیلدها: `operation_id, branch, operator_id, updated_fields, timestamp`.
- مدت نگهداری: ۹۰ روز.

</div>### جمع‌بندی

تابع **operationUpdateCheck** ابزار اصلی برای اصلاح داده‌های عملیات ثبت‌شده روی چک است و با اعتبارسنجی، حفظ وابستگی‌ها و بازسازی کش‌ها داده‌ها را پایدار و به‌روز نگه می‌دارد.

<div id="bkmrk-operation-update-check" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /financial/treeview

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/financial/treeview</td><td dir="ltr">V2CreditDebitController@treeview</td><td dir="ltr">authWithJwt</td><td dir="rtl">واکشی ساختار درختی حساب‌های مالی (کل، معین، تفضیلی) جهت نمایش سلسله‌مراتبی در رابط کاربری.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **treeview** ساختار درختی آیتم‌های مالی را بر اساس نوع درخواست‌شده (`general`، `moeen`، `tafzili`) و فیلترهای ورودی از دیتابیس دریافت می‌کند. سپس این داده‌ها را با فرمت استاندارد `{id, title, subset[]}`

کش Redis برای کاهش بار روی دیتابیس استفاده می‌شود؛ اگر دیتای مربوط پیش‌تر ذخیره شده باشد، از کش خوانده می‌شود، در غیراین‌صورت از پایگاه‌داده واکشی و کش‌گذاری می‌گردد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>بله</td><td>نوع آیتم مالی (مثل `moeen` یا `general`).</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه مالی.</td></tr><tr><td>filters</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای اضافی برای محدودسازی نتایج.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "result": [
    {
      "id": 21,
      "title": "بانک‌ها",
      "subset": [
        {"id": 54, "title": "بانک ملی"},
        {"id": 55, "title": "بانک ملت"}
      ],
      "locked": false,
      "display": true
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت JWT الزامی است.
- دسترسی مبتنی بر نقش: `financial.viewer` ضروری است.
- نوع داده درخواست‌شده باید معتبر و مجاز باشد.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D8%B4-redis-%D8%A8%D8%A7-ttl-%D9%BE%DB%8C%D8%B4%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کش Redis با TTL پیش‌فرض 1800 ثانیه برای داده‌های TreeView.
- استفاده از ایندکس مناسب روی ستون‌های کلید خارجی (group, general, moeen\_id).
- امکان واکشی زیرشاخه‌ها بدون بارگذاری کل درخت.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر type نامعتبر یا خالی.</td><td>Validator</td></tr><tr><td>403</td><td>عدم مجوز مشاهده ساختار مالی.</td><td>Auth Middleware</td></tr><tr><td>500</td><td>خطای داخلی دیتابیس یا Redis.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%DA%A9%D8%B1%D8%AF%D9%86-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محدود کردن داده‌ها به شعبه کاربر احرازشده.
- ثبت لاگ درخواست‌ها برای تحلیل رفتار و امنیت.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-l" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از Lazy Loading گره‌ها.
- اعمال فیلتر پیشرفته روی زیرشاخه‌ها.
- کش‌گذاری جداگانه برای هر سطح درخت.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%86%D9%88%D8%B9-%D8%A2%DB%8C%D8%AA%D9%85-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت نوع آیتم درخواست‌شده، زمان واکشی، و شناسه شعبه.
- نگهداری لاگ‌ها به مدت ۳۰ روز در `audit_logs`.

</div>### جمع‌بندی

تابع **treeview** زیرساختی امن و بهینه برای واکشی سلسله‌مراتبی حساب‌های مالی ارائه می‌دهد و با استفاده از کش و ایندکس، کارایی بالایی در پروژه‌های بزرگ مالی دارد.

<div id="bkmrk-financial-treeview" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /accounting/balance

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/accounting/balance</td><td dir="ltr">V2CreditDebitController@accountingBalance</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت موجودی لحظه‌ای حساب‌ها (بانکی، صندوق) و محاسبه تراز مالی شعبه.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **accountingBalance** موجودی حساب‌های ثبت‌شده در سیستم را بر اساس `branch` یا سایر فیلترهای مشخص شده واکشی می‌کند. داده‌ها شامل جمع موجودی نقدی، جمع موجودی بانکی، مجموع چک‌های در جریان و تراز کلی سیستم است.

برای افزایش کارایی، داده‌ها ممکن است از کش Redis خوانده شوند. در صورت عدم وجود یا انقضای کش، اطلاعات از دیتابیس واکشی و مجدد کش‌گذاری می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه برای محاسبه موجودی.</td></tr><tr><td>filters</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای مانند نوع حساب یا بازه تاریخ تراکنش‌ها.</td></tr><tr><td>goal</td><td>string</td><td>Body</td><td>خیر</td><td>هدف نمایش داده‌ها (مثلاً گزارش یا داشبورد).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "time": 1732287600,
  "result": {
    "cash_total": 15200000,
    "bank_total": 94500000,
    "checks_in_flow": 32000000,
    "overall_balance": 141700000
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیاز به احراز هویت JWT.
- نقش مجاز: `financial.balance.view`.
- بررسی تعلق شعبه به کاربر احراز شده.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D8%B4-redis-%D8%A8%D8%A7-ttl-%D9%BE%DB%8C%D8%B4%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کش Redis با TTL پیش‌فرض 600 ثانیه.
- محاسبات مجموع موجودی با استفاده از ایندکس روی ستون‌های `account_type` و `branch`.
- استفاده از SELECT SUM برای تجمیع سریع داده‌ها.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر branch نامعتبر.</td><td>Validator</td></tr><tr><td>403</td><td>عدم دسترسی به موجودی شعبه.</td><td>Auth Middleware</td></tr><tr><td>500</td><td>خطا در واکشی داده‌ها یا کش Redis.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D9%85%D9%88%D8%AC%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ درخواست موجودی با IP و شناسه کاربر.
- محدودیت نرخ درخواست (Rate Limit) برای جلوگیری از فشار به سرور.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AC%D8%B2%D8%A6%DB%8C%D8%A7%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%AD%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن جزئیات سطح حساب (جزء‌کل) در خروجی.
- پشتیبانی از فیلتر براساس ارز.
- نمایش تاریخ آخرین بروزرسانی موجودی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-accountingb" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `AccountingBalanceView`.
- ثبت داده‌ها: branch, filters, timestamp.
- مدت نگهداری: ۳۰ روز.

</div>### جمع‌بندی

تابع **accountingBalance** ابزاری کلیدی برای گزارش‌گیری مالی سریع و دقیق شعبه است و با پشتیبانی کش، عملکرد پایداری در محیط‌های پرتراکنش ارائه می‌دهد.

<div id="bkmrk-accounting-balance" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/list</td><td dir="ltr">V2CreditDebitController@listAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت لیست اعلان‌های مالی مرتبط با پرداخت‌ها، دریافت‌ها و سایر رویدادهای حسابداری سیستم.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **listAnnouncement** داده‌های مربوط به اعلان‌های مالی را از جدول `announcements` بازیابی می‌کند. این اعلان‌ها معمولاً شامل ارتباط بین اسناد پرداخت، دریافت، چک یا حواله‌های حسابی هستند. داده‌ها بر اساس فیلترهای DataTables (`draw, start, length`) و فیلترهای پیشرفته مانند نوع اعلان، وضعیت و تاریخ، استخراج می‌شوند. سپس خروجی نهایی شامل متای صفحه‌بندی، زمان سرور و لیست اعلان‌های ساختاریافته بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>draw</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه شماره درخواست DataTables برای هم‌زمان‌سازی.</td></tr><tr><td>start</td><td>integer</td><td>Body</td><td>بله</td><td>ایندکس شروع داده‌ها.</td></tr><tr><td>length</td><td>integer</td><td>Body</td><td>بله</td><td>تعداد رکورد در هر صفحه.</td></tr><tr><td>advanced</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای پیشرفته مثل نوع اعلان، وضعیت، تاریخ از و تا.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه مرتبط با اعلان‌ها.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "meta": {
    "draw": 5,
    "recordsTotal": 258,
    "recordsFiltered": 258,
    "timestamp": 1732287600
  },
  "data": [
    {
      "id": 315,
      "type": "pay",
      "reference_id": 1621,
      "branch": 4,
      "title": "ثبت پرداخت نقدی",
      "status": "done",
      "created_at": "2025-10-11 13:45:00"
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-j" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیاز به احراز هویت JWT.
- کاربر فقط اجازه مشاهده اعلان‌های مربوط به شعبه خود را دارد.
- نقش لازم: `financial.announcement.view`.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D8%B4-redis-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%DA%A9%D9%84%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کش Redis بر اساس کلید `announcement:list:{branch}` با TTL=600 s برای داده‌های بدون فیلتر.
- ایندکس ترکیبی روی ستون‌های `(branch,type,status)` برای افزایش سرعت واکشی.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Morilog\\Jalali\\Jalalian;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی ناقص یا نامعتبر DataTables.</td><td>Validator</td></tr><tr><td>403</td><td>عدم دسترسی به اعلان‌های شعبه دیگر.</td><td>Auth Middleware</td></tr><tr><td>500</td><td>خطای داخلی دیتابیس یا کش Redis.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-rbac-%D8%AF%D9%82%DB%8C%D9%82-%D8%A8%D8%B1%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال RBAC دقیق برای کنترل مشاهده و حذف اعلان‌ها.
- رمزنگاری آیدی مرجع پیش از ارسال به کلاینت.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت جستجوی ترکیبی چند ستونی.
- امکان گروه‌بندی بر اساس نوع اعلان (`payment`، `receive`، `system`).
- پشتیبانی از مرتب‌سازی پویا توسط فرانت‌اند.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%87%D8%B1-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D9%85%D8%B4%D8%A7%D9%87%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت هر درخواست مشاهده لیست در `audit_logs` با نوع **ListAnnouncement**.
- شامل: شناسه کاربر، شعبه، زمان، فیلترهای اعمال شده.

</div>### جمع‌بندی

مسیر **listAnnouncement** امکان مدیریت و مرور اعلان‌های مالی را به‌صورت امن، صفحه‌بندی‌شده و کش‌شده فراهم می‌کند و پایه گزارش‌گیری داخلی سیستم‌های مالی است.

<div id="bkmrk-announcement-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/list/company

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/list/company</td><td dir="ltr">V2CreditDebitController@listAnnouncementCompany</td><td dir="ltr">authWithJwt</td><td dir="rtl">لیست اعلان‌های مالی تجمیعی در سطح شرکت (تمام شعب)، با فیلتر DataTables و فیلتر پیشرفته برای گزارشات مدیریتی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **listAnnouncementCompany** مشابه تابع **listAnnouncement** عمل می‌کند اما دامنهٔ داده‌ها را به تمام شعب وابسته به شرکت (یا کل سازمان) گسترش می‌دهد. هدف، گزارش‌گیری یکپارچه از کلیه اعلان‌های مالی است. داده‌ها از جدول `announcements` بازیابی می‌شوند و با فیلترهای DataTables و جستجوی پیشرفته محدود می‌گردند. خروجی، شامل متادیتای DataTables (draw، تعداد کل، زمان درخواست) و آرایه‌ای از اعلان‌هاست.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>draw</td><td>integer</td><td>Body</td><td>بله</td><td>برای هم‌زمان‌سازی DataTables.</td></tr><tr><td>start</td><td>integer</td><td>Body</td><td>بله</td><td>نقطه شروع رکوردها.</td></tr><tr><td>length</td><td>integer</td><td>Body</td><td>بله</td><td>تعداد رکوردها در هر صفحه.</td></tr><tr><td>advanced</td><td>object</td><td>Body</td><td>خیر</td><td>فیلترهای ترکیبی مدیریت، از جمله نوع، تاریخ، وضعیت و شعبهٔ خاص.</td></tr><tr><td>company</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شرکت مادر برای واکشی اعلان‌های کل.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "meta": {
    "draw": 1,
    "recordsTotal": 842,
    "recordsFiltered": 842,
    "timestamp": 1732287600
  },
  "data": [
    {
      "id": 1220,
      "type": "receive",
      "reference": "branch_3/pay/554",
      "branch": 3,
      "company": 1,
      "status": "done",
      "title": "دریافت وجه نقد از شعبه مرکزی",
      "created_at": "2025-10-09 09:44:31"
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز JWT الزامی است.
- کاربر باید به نقش سطح شرکت `financial.company.announcement.view` مجهز باشد.
- فیلتر خودکار داده‌ها بر اساس `company` از توکن JWT انجام می‌شود تا از نشت اطلاعات بین شرکت‌ها جلوگیری گردد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A8%D9%87%DB%8C%D9%86%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-query-%D8%A8%D8%A7-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بهینه‌سازی Query با ایندکس روی فیلدهای `(company, type, created_at)`.
- استفاده از کش Redis با TTL=300 s برای پاسخ‌های صفحه اول (draw=1).

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی ناقص یا نامعتبر DataTables.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر فاقد سطح دسترسی سازمانی.</td><td>Auth Middleware</td></tr><tr><td>500</td><td>مشکل در واکشی داده‌ها یا کش Redis.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%DA%A9%D9%84%DB%8C%D8%AF-%D9%85%D8%B1%D8%AC%D8%B9-%28" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری کلید مرجع (reference) در پاسخ API.
- ثبت IP و شناسه کاربری در جدول audit\_logs برای هر درخواست لیست.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-expor" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `export` برای خروجی CSV/Excel.
- پیاده‌سازی Lazy Loading برای فیلدهای حجیم (description).
- قابلیت گروه‌بندی بر اساس شعبه یا وضعیت.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-listannou" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `ListAnnouncementCompany`.
- ثبت شامل company، filters، timestamp و user\_id.
- سطح حساسیت: **Info**.

</div>### جمع‌بندی

تابع **listAnnouncementCompany** ابزار اصلی مدیران مالی برای مشاهده اعلان‌های کلان بین‌شعبه‌ای است و با ساختار صفحه‌بندی‌شده و کش Redis، کارایی بالایی در محیط‌های چندشعبه‌ای دارد.

<div id="bkmrk-announcement-list-company" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/edit

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/edit</td><td dir="ltr">V2CreditDebitController@editAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازیابی داده‌های اولیه برای ویرایش اعلان مالی مشخص‌شده (جهت استفاده در فرم یا modal).</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **editAnnouncement** شناسه اعلان مالی را از ورودی دریافت کرده، اطلاعات کامل آن (از جدول `announcements`) را همراه با جزئیات اکوسیستم مرتبط (مانند نوع اعلان، وضعیت، شعبه، object مرجع و تاریخ) بازمی‌گرداند. این مسیر هیچ تغییر داده‌ای انجام نمی‌دهد و صرفاً حالت **read-only** دارد.

اگر اعلان یافت نشود یا کاربر به شاخهٔ مربوطه دسترسی نداشته باشد، خروجی با وضعیت خطا برمی‌گردد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه اعلان مالی برای واکشی.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبهٔ درخواست‌دهنده برای اعتبارسنجی مالکیت داده.</td></tr><tr><td>operator</td><td>integer</td><td>JWT</td><td>بله</td><td>شناسه کاربر درخواست‌دهنده (برای ممیزی).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "data": {
    "id": 72,
    "type": "pay",
    "object_id": 544,
    "status": "done",
    "branch": 3,
    "description": "اعلان پرداخت مربوط به فاکتور 554",
    "created_at": "2025-11-02 10:13:44",
    "updated_at": "2025-11-04 13:26:10",
    "operator": {
      "id": 17,
      "name": "Reza Moradi"
    }
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C.-%D9%86%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز JWT الزامی.
- نقش مجاز: `financial.announcement.view` یا بالاتر.
- کنترل شعبه با تطبیق `branch` از JWT و رکورد DB.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D8%A8%D8%B1-%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی دیتابیس بر اساس `id` ایندکس‌شده انجام می‌شود.
- در صورت استفاده مجدد، TTL کش Redis برابر ۶۰ ثانیه است (read cache).
- پاسخ JSON تا حد ممکن minimal طراحی شده تا در modal یا form کاربردی باشد.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر id ارسال نشده یا نامعتبر است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر به شعبهٔ اعلان دسترسی ندارد.</td><td>Auth Middleware</td></tr><tr><td>404</td><td>اعلان یافت نشد.</td><td>DB Query</td></tr><tr><td>500</td><td>خطای داخلی در بازیابی داده از دیتابیس.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C-%D8%AD%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری فیلدهای حساس (object\_id و operator) در سطح پاسخ.
- ثبت زمان و IP درخواست در جدول audit\_logs.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-inclu" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `include_related` برای بازیابی ارتباطات مثل چک‌ها و سندها.
- پشتیبانی از کش client-side برای کاهش بار سرور.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-editannou" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `EditAnnouncement`.
- ثبت شامل: id، branch، operator، و زمان درخواست.
- سطح حساسیت: **Notice**.

</div>### جمع‌بندی

تابع **editAnnouncement** نقطه آغاز فرآیند ویرایش است که با واکشی دادهٔ دقیق اعلان، محیط کاربر را برای تغییر ایمن آماده می‌کند. این نقطه هیچ داده‌ای را تغییر نمی‌دهد و صرفاً Fetch اولیهٔ امن محسوب می‌شود.

<div id="bkmrk-announcement-edit" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/store

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/store</td><td dir="ltr">V2CreditDebitController@storeAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">ثبت یا به‌روزرسانی درجا (Inline) اعلان مالی در فرم‌های پرداخت/دریافت.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **storeAnnouncement** در این حالت برای ثبت سریع اعلان (مثلاً هم‌زمان با ثبت سند پرداخت) استفاده می‌شود. در صورتی که شناسه‌ای از اعلان موجود ارسال شود، دادهٔ موجود ویرایش می‌شود وگرنه رکوردی جدید در جدول `announcements` ایجاد می‌گردد.

پس از درج یا به‌روزرسانی، حافظهٔ کش مالی Redis برای شاخهٔ مربوطه پاک‌سازی (invalidate) شده و شناسهٔ نهایی در خروجی بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>خیر</td><td>شناسهٔ اعلان (در صورت ویرایش رکورد موجود).</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>بله</td><td>نوع اعلان (مثلاً `pay`، `receive`).</td></tr><tr><td>object\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسهٔ رکورد منبع (سند/چک/تراکنش).</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شعبهٔ مرتبط برای تعیین محدودهٔ داده.</td></tr><tr><td>operator</td><td>integer</td><td>JWT</td><td>بله</td><td>شناسهٔ کاربر ثبت‌کننده.</td></tr><tr><td>description</td><td>string</td><td>Body</td><td>خیر</td><td>توضیحات اختیاری جهت ثبت در اعلان مالی.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "message": "Announcement stored successfully",
  "data": {
    "id": 114,
    "branch": 3,
    "type": "pay",
    "object_id": 554,
    "description": "ثبت خودکار همزمان با پرداخت",
    "timestamp": "2025-11-23 16:19:00"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-jwt-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C.-%D9%86%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز JWT اجباری.
- نقش مجاز: `financial.announcement.edit`.
- کنترل حفظ مالکیت داده با چک‌کردن `branch` در JWT و جدول اعلان‌ها.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A8%D8%B9%D8%AF-%D8%A7%D8%B2-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-insert" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بعد از عملیات `insert/update`، Redis کش مربوط به اعلان‌های آن شعبه را پاک می‌کند.
- TTL پیش‌فرض کش‌های وابسته: ۶۰۰ ثانیه.
- پردازش میانگین کمتر از ۴۵ms برای حالت local data write.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر type یا object\_id ارسال نشده است.</td><td>Validator</td></tr><tr><td>403</td><td>دسترسی نقش به ویرایش اعلان‌ها محدود است.</td><td>RBAC</td></tr><tr><td>404</td><td>رکورد ارسالی برای ویرایش یافت نشد.</td><td>DB Query</td></tr><tr><td>500</td><td>خطای داخلی در عملیات درج یا بروزرسانی.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-verify-level-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن verify-level دوم برای اعلان‌هایی که به مبالغ بالا مربوط می‌شوند.
- ثبت لاگ IP و timestamp در جدول ممیزی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-silen" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `silent` برای جلوگیری از invalidate موقت کش در عملیات batch.
- بهینه‌سازی واکشی object\_id با `join` جهت کاهش round-trip.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-storeanno" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `StoreAnnouncementInline`.
- ثبت شامل: id، branch، operator، object\_id و نوع عملیات (insert/update).
- سطح حساسیت: **Audit**.

</div>### جمع‌بندی

تابع **storeAnnouncement** در حالت inline بخشی از فرایندهای ترکیبی است که به‌صورت بلادرنگ اعلان را همراه با پرداخت یا دریافت درج یا اصلاح می‌کند و تمام کش‌های شعبه را به‌روزرسانی می‌نماید. این تابع جزو مسیرهای با درجهٔ امنیتی بالا در ماژول مالی محسوب می‌شود.

<div id="bkmrk-announcement-store-inline" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/update

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/update</td><td dir="ltr">V2CreditDebitController@updateAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">بروزرسانی اطلاعات اعلان مالی موجود پس از تایید سطح دسترسی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **updateAnnouncement** برای ویرایش ایمن رکورد موجود در جدول `announcements` به کار می‌رود. ابتدا بررسی می‌کند که اعلان با شناسهٔ مشخص در شعبهٔ کاربر و وضعیت باز وجود داشته باشد.

در صورت تأیید، به‌روزرسانی مقادیر کلیدی مانند نوع، توضیح، مبلغ یا شیء مرجع انجام می‌شود. پس از ثبت تغییرات در DB، کش مالی مرتبط با شاخهٔ ذکرشده پاک‌سازی شده (**Redis invalidate**) و دادهٔ نهایی ذخیره شده باز می‌گردد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه رکورد اعلان جهت بروزرسانی.</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>خیر</td><td>نوع عملیات مالی؛ در صورت ارسال جایگزین مقدار پیشین می‌شود.</td></tr><tr><td>description</td><td>string</td><td>Body</td><td>خیر</td><td>توضیح جدید اعلان مالی.</td></tr><tr><td>object\_id</td><td>integer</td><td>Body</td><td>خیر</td><td>شناسه رکورد منبع (اختیاری برای تغییر پیوند).</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه جهت کنترل دسترسی.</td></tr><tr><td>operator</td><td>integer</td><td>JWT</td><td>بله</td><td>شناسهٔ کاربر درخواست‌دهنده برای ثبت در ممیزی.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "message": "Announcement updated successfully.",
  "data": {
    "id": 114,
    "branch": 3,
    "type": "pay",
    "description": "اصلاح توضیحات اعلان جهت سند پرداخت",
    "updated_at": "2025-11-23 16:55:04",
    "operator": 17
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D9%86%D9%82%D8%B4-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- نقش لازم: `financial.announcement.edit` یا سطح بالاتر.
- عدم اجازه بروزرسانی اعلان‌های بسته یا پایان‌یافته (status ≥ locked).
- بررسی انطباق `branch` بین کاربر و رکورد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-update-%D8%A7%D8%B2-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در عملیات update از `DB::transaction` استفاده شده تا atomic بودن تضمین شود.
- جلوی تکرار کش redis با TTL جدید گرفته می‌شود (invalidate فوری شعبه).
- میانگین اجرای عملیات: ۶۵–۸۰ ms.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>شناسه یا پارامترهای ورودی ناقص است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجوز ویرایش این اعلان را ندارد.</td><td>RBAC</td></tr><tr><td>404</td><td>اعلان یافت نشد یا از شعبه دیگر است.</td><td>Query</td></tr><tr><td>423</td><td>اعلان بسته است و قابل ویرایش نیست.</td><td>Business Rule</td></tr><tr><td>500</td><td>خطای داخلی سرور در هنگام ذخیره.</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-timestamp-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت timestamp تغییر با IP کاربر در Log.
- افزودن قفل optimistic برای جلوگیری از وضعیت race-condition هنگام ویرایش همزمان.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-endpoint-%D9%85%D8%AC%D8%B2%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن endpoint مجزای `/announcement/notes` جهت نگهداری شرح تغییرات.
- پشتیبانی از بروزرسانی بخشی (partial update) با PATCH.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-updateann" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `UpdateAnnouncement`.
- موارد ثبت‌شونده: id، operator، branch، تغییرات انجام‌شده.
- سطح حساسیت: **Audit**.

</div>### جمع‌بندی

تابع **updateAnnouncement** با رویکرد atomic و قفل منطقی روی داده‌ها، به‌عنوان لایه انتقال برای عملیات اصلاح اعلان‌های مالی عمل می‌کند و بخشی از چرخه ممیزی شعبه در ماژول مالی محسوب می‌شود.

<div id="bkmrk-announcement-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/view

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/view</td><td dir="ltr">V2CreditDebitController@viewAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">نمایش جزئیات کامل یک اعلان مالی برای بازبینی یا مشاهده‌ی بدون ویرایش.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **viewAnnouncement** با دریافت شناسهٔ اعلان، رکورد مربوطه را از جدول `announcements` خوانده و در صورت نیاز ارتباطات آن را (مانند پرداخت‌ها، چک‌ها، و اعلان‌های وابسته) نیز شامل می‌کند.

هیچ تغییری در داده انجام نمی‌شود. این endpoint فقط برای مشاهده، چاپ یا آماده‌سازی نمای modal یا PDF استفاده می‌شود. چنانچه اعلان مورد نظر قفل مالی یا در وضعیت بسته باشد، فقط در حالت read-only قابل دریافت است.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسهٔ اعلان مورد نظر برای مشاهده.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شعبهٔ کاربر جهت بررسی محدودهٔ مجاز.</td></tr><tr><td>operator</td><td>integer</td><td>JWT</td><td>بله</td><td>شناسهٔ کاربر درخواست‌دهنده جهت ثبت در لاگ مشاهده.</td></tr><tr><td>include\_related</td><td>boolean</td><td>Body</td><td>خیر</td><td>در صورت true، داده‌های وابسته (پرداخت‌ها، چک‌ها، و اعلان‌های فرزند) نیز اضافه می‌شود.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "data": {
    "id": 114,
    "type": "pay",
    "object_id": 554,
    "branch": 3,
    "status": "done",
    "description": "هزینه بلیط پرواز تهران–مشهد",
    "created_at": "2025-11-20 18:35:00",
    "updated_at": "2025-11-21 09:12:11",
    "financial_lock": false,
    "related": [
      {
        "id": 882,
        "type": "check",
        "status": "cleared",
        "deadline": "1404/09/10"
      }
    ]
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت JWT الزامی است.
- نقش مورد نیاز: `financial.announcement.view`.
- اطلاعات نمایش‌شده فقط برای شعبهٔ کاربر قابل دسترس است.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C-%D8%B3%D8%A8%DA%A9-%28read-o" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- درخواست‌ سبک (read-only) با `select` محدود به ستون‌های مرتبط.
- در صورت فعال بودن `include_related`، کوئری به صورت lazy load اجرا می‌شود (Load factor ~1.3x).
- TTL کش Redis برای نتایج تکراری ۹۰ ثانیه.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر id ارسال نشده یا نامعتبر است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به مشاهده اعلان در شعبهٔ دیگر نیست.</td><td>Auth Middleware</td></tr><tr><td>404</td><td>اعلان یافت نشد.</td><td>DB Query</td></tr><tr><td>500</td><td>خطای داخلی در بازیابی اطلاعات یا join داده‌های وابسته.</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%84%D8%A7%DA%AF-%D8%B1%D8%B3%D9%85%DB%8C-fina" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن لاگ رسمی `financial_view_log` برای ثبت تاریخچهٔ مشاهده.
- محدودسازی فیلدهای حساس در پاسخ JSON برای کاربران با نقش پایین‌تر.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت فیلتر view با ورودی `object_type` برای نمایش اعلان‌های خاص یک ماژول.
- امکان ارسال `format=pdf` جهت تولید فایل قابل چاپ.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-viewannou" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `ViewAnnouncement`.
- داده‌های ثبت‌شونده: id، branch، operator، زمان درخواست.
- سطح حساسیت: **Notice**.

</div>### جمع‌بندی

تابع **viewAnnouncement** مسیر read-only برای مشاهدهٔ ایمن جزئیات اعلان مالی است و به کاربران دارای نقش مشاهده اجازه می‌دهد محتوای دقیق رکورد را بدون دخالت در فرآیندهای مالی بخوانند. داده‌ها با دسترسی کنترل‌شده و کش زمان‌دار ارائه می‌شوند.

<div id="bkmrk-announcement-view" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/statement

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/statement</td><td dir="ltr">V2CreditDebitController@statementAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">تولید و ارائهٔ گزارش بیانیه (Statement) مالی برای اعلان‌های فعال در بازهٔ مورد درخواست کاربر.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **statementAnnouncement** با هدف تهیهٔ بیانیهٔ تجمیعی از اعلان‌های مالی (پرداخت، دریافت، چک، و اعلان مرتبط) طراحی شده است. این تابع معمولاً بعد از اجرای عملیات `listAnnouncement` یا `updateAnnouncement` برای استخراج گزارش دقیق جریان‌های مالی استفاده می‌شود.

مقدارهای ورودی شامل تاریخ شروع، تاریخ پایان، نوع اعلان، و شناسه شعبه هستند. تابع با استفاده از `DB::table('announcements')` داده‌ها را واکشی کرده و نسبت به `object_type` (مانند pay, check, cost) عملیات Grouping انجام می‌دهد. سپس مجموع‌ها (balance, debit, credit) محاسبه و ساختار JSON خروجی بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>from</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ شروع بازه گزارش.</td></tr><tr><td>to</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ پایان بازه گزارش.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه کاربر برای فیلتر گزارش.</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>خیر</td><td>در صورت ارسال، فقط بیانیه نوع خاص (pay, check, cost) بازگردانده می‌شود.</td></tr><tr><td>currency</td><td>string</td><td>Body</td><td>خیر</td><td>واحد ارزی (تومان، دلار، یورو، ...).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "statement": {
    "branch": 3,
    "from": "1404-09-01",
    "to": "1404-09-23",
    "currency": "IRR",
    "summary": {
      "total_credit": 71250000,
      "total_debit": 53320000,
      "balance": 17930000
    },
    "details": [
      {
        "id": 114,
        "type": "pay",
        "object_type": "check",
        "description": "پرداخت چک بابت اجاره دفتر",
        "amount": 5200000,
        "created_at": "1404-09-12",
        "status": "paid"
      },
      {
        "id": 115,
        "type": "receive",
        "object_type": "cash",
        "description": "دریافت پیش پرداخت از مشتری A",
        "amount": 10500000,
        "created_at": "1404-09-15",
        "status": "done"
      }
    ]
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA.-%D9%86%D9%82%D8%B4-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- JWT الزامی است.
- نقش الزامی: `financial.announcement.view` یا بالاتر.
- اطلاعات فقط برای `branch` کاربر جاری باز می‌گردد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C%E2%80%8C%D9%87%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از کوئری‌های تجمیعی (aggregate) برای بهبود سرعت گزارش.
- در صورت وجود کش، نتایج تا ۳۰۰ ثانیه در Redis نگهداری می‌شوند.
- میانگین زمان اجرا: ~120ms (بدون کش).

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Morilog\\Jalali\\Jalalian;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>ورودی‌های زمانی یا نوع اعلان نادرست است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به مشاهده‌ی بیانیه شعبه نیست.</td><td>Auth Middleware</td></tr><tr><td>404</td><td>داده‌ای در بازهٔ زمانی موردنظر یافت نشد.</td><td>Query</td></tr><tr><td>500</td><td>خطای داخلی سرور در پردازش تجمیعی.</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تغییر ساختار خروجی در نقش‌های سطح پایین برای کاهش نشت داده مالی.
- ثبت ممیزی `view_statement` با تاریخ و IP کاربر.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-group" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `group_by` با گزینه‌های (day, week, month).
- افزودن خروجی PDF و Excel برای دانلود سریع بیانیه.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-statement" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `StatementAnnouncement`.
- موارد ثبت‌شونده: بازهٔ زمانی، operator، branch.
- سطح حساسیت: **Audit**.

</div>### جمع‌بندی

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

<div id="bkmrk-announcement-statement" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/ledger

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/ledger</td><td dir="ltr">V2CreditDebitController@ledgerAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">ارائه دفترکل اعلان‌ها (Ledger) بر اساس فیلتر حساب، شعبه و بازهٔ زمانی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

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

این تابع از همان ساختار underlying جدول‌های `announcements`، `pays` و `checks` استفاده می‌کند و پس از پردازش، خروجی را به‌صورت مرتب‌شده بر اساس تاریخ و حساب ارائه می‌دهد. همچنین امکان گروه‌بندی بر پایهٔ **حساب معین** (`moeen_id`) یا **نوع عملیات** فراهم است.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>from</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ شروع دفترکل.</td></tr><tr><td>to</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ پایان دفترکل.</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شعبهٔ درخواست دهنده.</td></tr><tr><td>moeen\_id</td><td>integer</td><td>Body</td><td>خیر</td><td>در صورت ارسال، فقط عملیات مربوط به حساب معین خاص.</td></tr><tr><td>group\_by</td><td>string</td><td>Body</td><td>خیر</td><td>نحوه گروه‌بندی (day، type، account).</td></tr><tr><td>currency</td><td>string</td><td>Body</td><td>خیر</td><td>واحد ارزی فیلتر گزارش.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "branch": 3,
  "group_by": "day",
  "from": "1404-09-01",
  "to": "1404-09-23",
  "ledger": [
    {
      "date": "1404-09-01",
      "debit": 2000000,
      "credit": 0,
      "balance": -2000000,
      "description": "پرداخت به تامین‌کننده کالا",
      "account": "110102 حساب پرداخت‌ها"
    },
    {
      "date": "1404-09-05",
      "debit": 0,
      "credit": 4500000,
      "balance": 2500000,
      "description": "دریافت نقدی از مشتری",
      "account": "110101 صندوق نقدی"
    }
  ],
  "summary": {
    "total_debit": 2000000,
    "total_credit": 4500000,
    "final_balance": 2500000
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT الزامی است.
- نقش مورد نیاز: `financial.ledger.view`.
- اجازهٔ دسترسی فقط به اعلان‌های همان شعبه.

</div>### نکات عملکردی

<div id="bkmrk-%DA%AF%D8%B2%D8%A7%D8%B1%D8%B4-%D8%AA%D8%AC%D9%85%DB%8C%D8%B9%DB%8C-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- گزارش تجمیعی با استفاده از `GROUP BY` در SQL.
- امکان Cache Redis تا 300 ثانیه برای کاهش فشار گزارش‌های تکراری.
- میانگین زمان اجرا: ۹۰ تا ۱۴۰ میلی‌ثانیه.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Morilog\\Jalali\\Jalalian;
- use App\\Helpers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%AA%D8%A7%D8%B1%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>تاریخ یا پارامتر گزارش ارسال نشده است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر اجازه مشاهده دفترکل این شعبه را ندارد.</td><td>RBAC</td></tr><tr><td>404</td><td>هیچ تراکنشی در بازهٔ مورد نظر پیدا نشد.</td><td>Query</td></tr><tr><td>500</td><td>خطای داخلی سرور.</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%D9%88-ip-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت تاریخ و IP کاربر مشاهده‌کننده گزارش در ممیزی.
- محافظت خروجی با masking در اطلاعات حساس (مثلاً شماره حساب).

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AD%D8%A7%D9%84%D8%AA-real-tim" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن حالت real-time refresh برای Dynamic Ledger UI.
- پشتیبانی از خروجی CSV یا Excel.
- امکان فیلتر چندحسابی (multi-account ledger) در نسخه ۲.۱.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-ledgerann" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع ممیزی: `LedgerAnnouncement`.
- ثبت: branch، operator، بازه، نوع گروه‌بندی.
- سطح حساسیت: **Audit**.

</div>### جمع‌بندی

تابع **ledgerAnnouncement** ستون اصلی گزارش‌گیری تحلیلی دفترکل در ماژول مالی است. این تابع بر پایه داده‌های اعلان، جریان‌های وام، پرداخت و دریافتی را تجمیع کرده و با سیستم کَش تلفیق‌شده، سرعت بالا و داده دقیق برای حسابداران فراهم می‌کند.

<div id="bkmrk-announcement-ledger" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /announcement/calculation

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/announcement/calculation</td><td dir="ltr">V2CreditDebitController@calculationAnnouncement</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازمحاسبه تراز مالی اعلان‌ها و بروزرسانی Balance کلی سیستم.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **calculationAnnouncement** برای بازتولید محاسبات مالی (مانند مانده‌ها، جمع بدهکار و بستانکار، و وضعیت کلی حساب‌ها) طراحی شده است. هر زمان دادهٔ مالی جدید (مانند اعلان پرداخت یا دریافت) ثبت یا ویرایش شود، فراخوان این مسیر باعث تنظیم مجدد داده‌های تحلیلی می‌شود.

درون متد، سیستم با استفاده از `DB::table('announcements')` و `DB::raw()` مجموع عملیات مالی را از جدول‌های مرتبط (مثل `pays` و `checks`) واکشی و تجمیع کرده، سپس در جدول داخلی `announcement_calculations` یا کش Redis ثبت می‌کند. این فرآیند به صورت atomic در تراکنش دیتابیس انجام می‌شود تا از ناسازگاری داده جلوگیری شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>integer</td><td>JWT/Header</td><td>بله</td><td>شناسه شعبه برای محاسبه محلی تراز.</td></tr><tr><td>from</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ شروع محدودهٔ مورد محاسبه.</td></tr><tr><td>to</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ پایان محدودهٔ مورد محاسبه.</td></tr><tr><td>type</td><td>string</td><td>Body</td><td>خیر</td><td>نوع اعلان (مثل `pay`، `receive`، `check`).</td></tr><tr><td>currency</td><td>string</td><td>Body</td><td>خیر</td><td>واحد ارزی مورد محاسبه.</td></tr><tr><td>refresh\_cache</td><td>boolean</td><td>Body</td><td>خیر</td><td>در صورت true، کش نتایج حذف و مجدداً محاسبه می‌شود.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "branch": 3,
  "from": "1404-09-01",
  "to": "1404-09-23",
  "updated": 38,
  "currency": "IRR",
  "calculation": {
    "total_credit": 71250000,
    "total_debit": 53300000,
    "balance": 17950000,
    "last_update": "1404-09-23 13:44",
    "source": "rebuild"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT الزامی است.
- نقش الزامی: `financial.announcement.manage` یا مدیر سیستم.
- هر درخواست تنها مجاز به محاسبه در محدوده شعبهٔ خود کاربر است.

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87%E2%80%8C%D9%87%D8%A7-%D8%AF%D8%B1-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محاسبه‌ها در تراکنش DB و بهینه‌شده با شاخص‌های زمانی (date index) اجرا می‌شوند.
- در صورت فعال بودن کش Redis، نتیجه تا ۶۰۰ ثانیه نگهداری می‌شود.
- میانگین زمان اجرا برای بازهٔ ۱۰۰۰ اعلان ≈ ۲۵۰ ms.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Helpers\\Functions;
- use Morilog\\Jalali\\Jalalian;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر زمانی یا نوع اعلان نادرست است.</td><td>Validator</td></tr><tr><td>403</td><td>دسترسی کافی برای بازمحاسبه وجود ندارد.</td><td>RBAC</td></tr><tr><td>404</td><td>هیچ اعلان فعالی در بازهٔ مورد نظر یافت نشد.</td><td>Query</td></tr><tr><td>500</td><td>خطای داخلی هنگام آپدیت تراز.</td><td>Transaction</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%85%D9%85%DB%8C%D8%B2%DB%8C-%D8%AA%D9%85%D8%A7%D9%85-%D8%A8%D8%A7%D8%B2%D9%85%D8%AD" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت ممیزی تمام بازمحاسبات با شناسه اپراتور و IP در `system_logs`.
- محدودسازی نرخ فراخوانی مسیر (Rate Limiter: ۵ در دقیقه).
- قفل همزمانی مبتنی بر Redis برای جلوگیری از محاسبهٔ هم‌زمان چند درخواست یکسان.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%AF%D8%B2%DB%8C%D9%86%D9%87%D9%94-diff_m" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن گزینهٔ `diff_mode` برای محاسبه فقط تفاوت داده‌ها.
- ارائه خروجی تحلیلی با تفکیک بر اساس حساب یا کاربر.
- پشتیبانی از زمان‌بندی خودکار (cron-based recalculation) هر ۲۴ ساعت.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-rebuildcalc" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `RebuildCalculation`.
- اقلام ممیزی: تاریخ بازه، شناسه اپراتور، مدت اجرای فرآیند، تعداد ردیف به‌روزشده.
- سطح حساسیت: **Critical Audit**.

</div>### جمع‌بندی

**calculationAnnouncement** مسیر اصلی برای هم‌ترازی اطلاعات مالی و بازسازی تراز سیستم است. این متد هرگونه تغییر در اعلان‌ها را به دادهٔ آماری منطبق تبدیل کرده و همواره موجب سازگاری داده‌های حسابداری کل با ردیف‌های تراکنشی می‌شود. اجرای آن برای پایداری و سلامت کامل ماژول مالی حیاتی است.

<div id="bkmrk-announcement-calculation" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /gateway/invoice/{drive}

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/gateway/invoice/{drive}</td><td dir="ltr">V2CreditDebitController@gatewayInvoice</td><td dir="ltr">authWithJwt</td><td dir="rtl">تولید لینک پرداخت آنلاین از طریق درگاه متصل‌شده به شناسه یا مدل موردنظر (drive).</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **gatewayInvoice** برای تولید <span style="font-weight: bold;">فاکتور پرداخت اینترنتی</span> طراحی شده است. پارامتر `{drive}` شناسهٔ آبجکت (مثلاً پرداخت، سفارش یا اعلان مالی) موردنظر را مشخص می‌کند. سیستم نوع مدل را بررسی کرده، مبلغ و اطلاعات حساب مرتبط را از پایگاه داده واکشی می‌نماید و پس از محاسبهٔ پارامترهای امنیتی، یک لینک سفارش فعال در جدول `payment_gateway` ایجاد می‌کند.

در نهایت، خروجی شامل URL پرداخت و شناسه تراکنش است که کاربر سمت کلاینت یا موبایل می‌تواند برای پرداخت در مرورگر یا اپلیکیشن بانک استفاده کند.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>drive</td><td>integer</td><td>URL Param</td><td>بله</td><td>شناسهٔ آیتم پرداخت یا اعلان.</td></tr><tr><td>amount</td><td>integer</td><td>Body</td><td>خیر</td><td>مبلغ نهایی، فقط اگر نیاز به override داشته باشد.</td></tr><tr><td>callback\_url</td><td>string</td><td>Body</td><td>خیر</td><td>آدرس بازگشت کاربر پس از پرداخت (در صورت عدم ارسال از config خوانده می‌شود).</td></tr><tr><td>currency</td><td>string</td><td>Body</td><td>خیر</td><td>واحد ارزی (پیش‌فرض IRR).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "gateway": "zarinpal",
  "invoice": {
    "id": 114,
    "amount": 250000,
    "currency": "IRR",
    "reference": "A1Z4R36P",
    "callback_url": "https://example.com/callback/zp",
    "link": "https://gateway.zarinpal.com/pg/StartPay/A1Z4R36P",
    "expire_in": 900
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT الزامی است.
- کنترل مالکیت داده: سیستم بررسی می‌کند کاربر یا شعبه‌ی فعلی مجاز به دیدن یا پرداخت آن آیتم هست.
- پارامتر مبلغ رمزگذاری و در سمت درگاه تأیید مجدد می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AB%D8%A8%D8%AA-%D9%88-%D9%84%DB%8C%D9%86%DA%A9%E2%80%8C%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات ثبت و لینک‌سازی کمتر از ۱۰۰ms انجام می‌شود.
- در صورت وجود لینک فعال قبلی برای همان drive، سیستم از لینک موجود استفاده می‌کند (prevent duplicate).
- درگاه پرداخت از تنظیمات فعال Redis انتخاب می‌گردد.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use App\\Helpers\\Functions;
- use Morilog\\Jalali\\Jalalian;
- use App\\Http\\Controllers\\Gateways\\ZarinpalController;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر یا دادهٔ درگاه نامعتبر است.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به تولید فاکتور این آیتم نیست.</td><td>Auth Middleware</td></tr><tr><td>404</td><td>آیتم مالی یافت نشد.</td><td>Query</td></tr><tr><td>500</td><td>خطای سرور هنگام برقراری ارتباط با درگاه پرداخت.</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%D8%B6%D8%A7%DB%8C-%D8%AF%DB%8C%D8%AC%DB%8C%D8%AA%D8%A7%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امضای دیجیتال به لینک خروجی برای جلوگیری از تغییر غیرمجاز.
- ثبت IP و User-Agent تولیدکننده لینک در لاگ.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-gateway%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پشتیبانی از gatewayهای چندگانه (zarinpal, nextpay, idpay) از طریق strategy pattern.
- افزودن پارامتر `meta` جهت ارسال دادهٔ اضافی به callback.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-gatewayinvo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `GatewayInvoice`.
- موارد ممیزی: شناسهٔ drive، مبلغ، درگاه انتخابی، IP کاربر.
- سطح حساسیت: **Audit**.

</div>### جمع‌بندی

تابع **gatewayInvoice** درگاه میانی سیستم مالی را مدیریت می‌کند و با تولید سریع لینک پرداخت، کاربران را از اتصال مستقیم به سرویس پرداخت بی‌نیاز می‌سازد. این تابع یکی از اجزای کلیدی ماژول تراکنش آنلاین است که امنیت، سرعت و یکپارچگی داده را در سطح شعبه تضمین می‌کند.

<div id="bkmrk-gateway-invoice" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /gateway/invoice/{drive}/verify

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/gateway/invoice/{drive}/verify</td><td dir="ltr">V2CreditDebitController@gatewayInvoiceVerify</td><td dir="ltr">authWithJwt</td><td dir="rtl">بررسی وضعیت نهایی پرداخت (موفق، ناموفق یا در انتظار) و به‌روزرسانی رکورد مربوطه در سیستم داخلی.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **gatewayInvoiceVerify** مرحلهٔ پایانی پرداخت الکترونیک است. این تابع پس از بازگشت کاربر از درگاه بانکی (با پارامترهای `Authority`، `RefID` یا معادل آنها) اجرا می‌شود.

سیستم اطلاعات پرداخت را از جدول `payment_gateway` بر اساس پارامتر `{drive}` واکشی کرده، شناسهٔ بانکی و مبلغ مورد انتظار را تطبیق می‌دهد. سپس از کنترلر درگاه (مثلاً `ZarinpalController::verify()`) برای استعلام نهایی وضعیت استفاده می‌کند.

در صورت موفقیت، رکورد پرداخت در جدول `pays` با وضعیت **paid** به‌روزرسانی شده و تراکنش به عنوان سند مالی تأییدشده در دفتر ثبت می‌گردد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>drive</td><td>integer</td><td>URL Param</td><td>بله</td><td>شناسه آیتم فاکتور آنلاین.</td></tr><tr><td>authority</td><td>string</td><td>Body / Callback</td><td>بله</td><td>شناسه تراکنش بازگشتی از درگاه (Authority یا Token).</td></tr><tr><td>status</td><td>string</td><td>Body / Callback</td><td>خیر</td><td>کد وضعیت برگشتی از درگاه (OK, NOK, ERROR).</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "status": true,
  "gateway": "zarinpal",
  "invoice": {
    "id": 114,
    "reference": "A1Z4R36P",
    "bank_ref": "000485298963",
    "amount": 250000,
    "state": "paid",
    "verified_at": "1404-09-23 14:21"
  },
  "transaction": {
    "accounting_record": 24039,
    "updated_balance": 71250000
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA%D8%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- توکن JWT الزامی است، حتی در تماس callback.
- مقایسهٔ مبلغ پرداخت‌شده با مبلغ ثبت‌شده پیش از تأیید ضروری است.
- تمام داده‌های برگشتی از سمت درگاه (RefID, Status, CardPan و غیره) در جدول `gateway_logs` نگهداری می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B2%D9%85%D8%A7%D9%86-verify-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان Verify برای Zarinpal حدود ۱۸۰–۲۵۰ میلی‌ثانیه است.
- در صورت Timeout، سیستم تا سه بار verify مجدد با delay افزایشی انجام می‌دهد.
- پس از تأیید، کش Redis فاکتور در مدت ۶۰۰ ثانیه ابطال می‌شود.

</div>### وابستگی‌ها

<div id="bkmrk-use-app%5Chttp%5Ccontrol" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Http\\Controllers\\Gateways\\ZarinpalController;
- use Illuminate\\Support\\Facades\\DB;
- use App\\Helpers\\Functions;
- use Morilog\\Jalali\\Jalalian;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-400-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر نامعتبر یا دادهٔ ناقص در Callback.</td><td>Validator</td></tr><tr><td>403</td><td>کاربر مجاز به تأیید این فاکتور نیست.</td><td>Auth Middleware</td></tr><tr><td>404</td><td>رکورد فاکتور یافت نشد.</td><td>Query</td></tr><tr><td>409</td><td>تأیید تکراری برای همان فاکتور انجام شد.</td><td>Duplicate Prevention</td></tr><tr><td>500</td><td>پاسخ نامعتبر از سرور درگاه.</td><td>Gateway Error</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-verify%D9%87%D8%A7-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تمام Verifyها باید دارای **signature hash** اختصاصی باشند.
- در پاسخ درگاه‌ها، مقادیر card\_pan و card\_hash نباید در لاگ سیستم اصلی نمایش داده شوند.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از درگاه‌های چندارزی و پرداخت ارزی (EUR، AED، USD).
- افزودن هشدار بلادرنگ (WebSocket) در پنل مدیریتی در صورت پرداخت موفق.
- امکان trans-retry خودکار در صورت عدم اتصال لحظه‌ای با بانک.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-gatewayveri" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `GatewayVerify`.
- جزئیات: شناسه تراکنش، RefID، مبلغ پرداخت‌شده، IP کاربر، زمان تأیید.
- سطح حساسیت: **Critical Audit**.

</div>### جمع‌بندی

تابع **gatewayInvoiceVerify** نقطهٔ نهایی فرایند پرداخت آنلاین است. این متد دادهٔ بازگشتی درگاه را با اطلاعات داخلی همگام کرده، وضعیت پرداخت را در پایگاه داده، کش و دفتر حسابداری ثبت می‌کند. اجرای صحیح آن برای جلوگیری از ثبت پرداخت‌های تکراری، حیاتی است و بخشی از چرخه اطمینان مالی (Financial Transaction Integrity Cycle) محسوب می‌شود.

<div id="bkmrk-gateway-invoice-verify" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/gateway/details

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/gateway/details</td><td dir="ltr">V2CreditDebitController@paymentGatewayDetails</td><td dir="ltr">(none)</td><td dir="rtl">واکشی وضعیت و جزئیات فاکتور پرداخت از جدول `payment_gateway` بر اساس شناسه سریال پرداخت.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **paymentGatewayDetails** بر اساس مقدار `serial_id` که از سمت کلاینت ارسال می‌شود، رکورد متناظر در جدول `payment_gateway` را جست‌وجو می‌کند. در صورت پیدا شدن، نوع درگاه را مشخص کرده و بر اساس نوع درگاه (مانند **behpardakht** یا **sep**)، فیلدهای خروجی را از محتوای JSON ذخیره‌شده در ستون `result` استخراج می‌کند.

برای درگاه `behpardakht`، پارامترهای `CardHolderPan` و `SaleReferenceId` استخراج می‌شود. اگر پاسخ شامل کد خطای ResCode=17 باشد، لینک retry ساخته می‌شود. برای درگاه `sep` نیز پارامترهای `SecurePan` و `Rrn` استفاده می‌گردد. درگاه‌های دیگر بدون پردازش JSON مستقیماً بازگردانده می‌شوند.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 94%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>serial\_id</td><td>string</td><td>Body</td><td>بله</td><td>شناسه سریال فاکتور پرداخت، کلید اصلی جست‌وجو در جدول `payment_gateway`.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "payload": {
    "drive": "sep",
    "amount": 250000,
    "card": "603799******1234",
    "datetime": "1404-09-23 14:28",
    "tracking_code": "123456789",
    "try_link": false
  },
  "meta": {
    "timestamp": 1732362504
  }
}
```

در صورت خطا در فرایند پرداخت، ساختار پاسخ به صورت زیر است:

```
{
  "error": {
    "code": 1000,
    "message": "در فرایند پرداخت مشکلی رخ داده است."
  },
  "payload": { ... },
  "meta": { "timestamp": 1732362504 }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B5%D9%88%D8%B1%D8%AA-%D9%BE%DB%8C%D8%B4%E2%80%8C%D9%81%D8%B1%D8%B6-%D9%81%D8%A7%D9%82%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌صورت پیش‌فرض فاقد middleware است، اما در حالت عملی باید `authWithJwt` یا امضای دیجیتال اعمال شود.
- شناسه `serial_id` باید از منبع امن (callback تأییدشده) ارسال شود.
- JSON ذخیره‌شده در ستون `result` شامل داده‌های حساس کارت است و نباید بدون masking به کاربر بازگردد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AC%D8%B3%D8%AA%E2%80%8C%D9%88%D8%AC%D9%88-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-paym" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- جست‌وجو در جدول `payment_gateway` بر اساس ایندکس سریال انجام می‌گیرد (O(1)).
- زمان پاسخ زیر ۵۰ ms است.
- هر دو جدول مرتبط `payment_gateway` و `gateways` فقط یک‌بار Query می‌شوند.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\Response;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-404-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>404</td><td>رکورد سریال پرداخت یافت نشد.</td><td>DB::first()</td></tr><tr><td>422</td><td>پرداخت ناموفق یا ناتمام.</td><td>پردازش درگاه</td></tr><tr><td>1000</td><td>پیام پیش‌فرض خطای عمومی پرداخت در پاسخ JSON.</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-middlewar" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه کردن middleware تأیید سطح دسترسی یا امضای رمزنگاری‌شده در درخواست‌ها.
- رمزگذاری جزئیات کارت‌ها پیش از ذخیره در جدول.
- عدم ارسال پارامترهای خام `result` در خروجی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از سایر درگاه‌ها (مثلاً nextpay، idpay).
- افزودن وضعیت مجزا برای `timeout` و `canceled`.
- افزودن فیلد `verified_at` در خروجی برای همزمان‌سازی با جدول تراکنش‌ها.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-gatewaydeta" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `GatewayDetails`.
- اقلام ممیزی: serial\_id، آی‌پی درخواست‌دهنده، درگاه شناسایی‌شده، نتیجه تراکنش.
- سطح حساسیت: **Notice**.

</div>### جمع‌بندی

تابع **paymentGatewayDetails** مسیر مرجع سیستم برای واکشی وضعیت نهایی پرداخت‌ها است. این endpoint خروجی استاندارد برای گزارش تراکنش ارائه می‌کند و بخشی از چرخهٔ پایش عملکرد درگاه‌های بانکی در زیرسیستم مالی محسوب می‌شود. اجرای امن و دقیق آن برای جلوگیری از مغایرت در داده‌های بانکی ضروری است.

<div id="bkmrk-gateway-details" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleagues/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleagues/list</td><td dir="ltr">V2ColleaguesController@colleaguesList</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازیابی فهرست همکاران (Colleagues) بر اساس شرایط فیلتر و عملیات موردنظر (بستانکاران، بدهکاران، تسویه و ...).</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **colleaguesList** وظیفه دارد لیست همکاران را بر اساس نوع عمل درخواستی (`debtors`، `creditors`، `rounded` و ...) برگرداند. در ابتدا دادهٔ JSON ارسالی از سمت کلاینت تجزیه می‌شود و پارامترهای شروع (start) و طول (length) برای صفحه‌بندی تعیین می‌گردد. سپس بر اساس نوع عمل (`$request->action`) جهت مرتب‌سازی مشخص می‌شود (ASC یا DESC).

در ادامه با ترکیب داده‌های دریافتی و وضعیت بدهی/بستانکاری از طریق متدهای محاسباتی، مجموعه‌ای از آیتم‌ها با ساختاری شامل اطلاعات شناسنامه‌ای، وضعیت مالی، سقف اعتباری، و وضعیت تشخیص حساب (neutral/debtor/creditor) تشکیل می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام پارامتر</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>پارامتر صفحه‌بندی و فیلترها شامل start, length, advanced.</td></tr><tr><td>action</td><td>string</td><td>Body</td><td>بله</td><td>نوع عملیات: `debtors`، `creditors`، یا `rounded` برای مرتب‌سازی.</td></tr><tr><td>operator</td><td>string</td><td>Body</td><td>خیر</td><td>شناسه کاربری یا نقش عامل.</td></tr></tbody></table>

</div>### خروجی (Response)

```
{
  "draw": 1,
  "recordsTotal": 162,
  "recordsFiltered": 162,
  "data": [
    {
      "serial_id": 8212,
      "system_serial": 12,
      "title": "دفتر آژانس مهر و ماه",
      "type": "شریک تجاری (بستانکاران)",
      "relationship": "ندارد",
      "category": "شرکت داخلی",
      "credit": 2400000,
      "debit": 600000,
      "balance": 1800000,
      "financial_ceiling": 25000000,
      "diagnosis": {"fa":"بستانکار","en":"Creditor"},
      "status": {"fa":"فعال","en":"active"},
      "documents": ...,
      "representative": ...
    }
  ],
  "refreshDatetime": "1404-09-23 14:30:00"
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D8%AA%D8%AD%D8%AA-%D9%BE%D9%88%D8%B4%D8%B4-mi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- این مسیر تحت پوشش middleware `authWithJwt` اجرا می‌شود و هویت کاربر الزامی است.
- اطلاعات مالی براساس شعبه و سطح دسترسی کاربر فیلتر می‌شود.
- در صورت وجود cache Redis، زمان آخرین به‌روزرسانی از کلید `TIME:colleagues:general_billing` خوانده می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پشتیبانی از صفحه‌بندی پویا با `start` و `length`.
- استفاده از ترتیب صعودی یا نزولی به‌صورت پویا بر اساس `action`.
- زمان پاسخ‌دهی با Redis Cache معمولاً زیر ۱۲۰ms است.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\Redis;
- use Morilog\\Jalali\\Jalalian;
- use Carbon\\Carbon;
- use App\\Http\\Controllers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-401-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی شده است.</td><td>authWithJwt</td></tr><tr><td>422</td><td>ساختار دادهٔ ارسالی اشتباه است.</td><td>JSON Parse Request</td></tr><tr><td>500</td><td>خطا در پردازش داده‌ها از Redis یا پایگاه داده.</td><td>DB/Routing</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D8%B3%D8%B7%D8%AD-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%AA%D9%81%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال سطح دسترسی تفکیک‌شده (Role Restriction) برای مشاهده بستانکاران یا بدهکاران.
- حذف فیلدهای حساس داخلی مانند شماره حساب‌ها از خروجی.

</div>### پیشنهادهای بهبود

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-sort_" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `sort_field` برای مرتب‌سازی بر اساس فیلد دلخواه.
- ایجاد کش سطح شبکه برای بارگذاری سریع‌تر مقادیر ثابت (Category، Type).
- پشتیبانی از فیلترهای ترکیبی چند شرطی در `advanced`.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-colleaguesl" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `ColleaguesList`.
- جزئیات ثبت‌شده: user\_id، زمان درخواست، action، pagination.
- سطح حساسیت: **Medium Audit**.

</div>### جمع‌بندی

مسیر **POST /colleagues/list** نقطه ورود به سیستم مالی همکاران است و نقش کلیدی در تحلیل وضعیت اعتباری آنها دارد. کد این بخش ساختاری منعطف با پشتیبانی از صفحه‌بندی، فیلترگذاری و تشخیص خودکار نوع حساب مالی دارد. افزودن caching پیشرفته و Role-based access می‌تواند راندمان و امنیت را دوچندان کند.

<div id="bkmrk-colleagues-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleague/bill

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleague/bill</td><td dir="ltr">V2ColleaguesController@colleagueBill</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت صورتحساب مالی یک همکار (Colleague) شامل تراکنش‌های پرداخت، دریافت، چک، کارمزد و مانده دوره.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **colleagueBill** فهرستی از اسناد مالی مرتبط با یک همکار خاص را بر اساس `id` همکار و بازه تاریخی درخواست‌شده برمی‌گرداند. الگوریتم شامل مراحل زیر است:

<div id="bkmrk-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-json-%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. خواندن ساختار JSON ورودی شامل فیلترها (from, to, r, status, lbalance, fpopen) برای محدودسازی بازهٔ تراکنش.
2. بازیابی فاکتورهای صادرشده از جداول مرتبط (فروش، خدمات و رزرو) و سپس عملیات پرداخت یا دریافت از جدول `pays`.
3. تشخیص خودکار نوع عملیات مالی (پرداخت، دریافت، چک، سند دستی، کارمزد، افتتاحیه، اختتامیه).
4. محاسبه مجموع بستانکاری و بدهکاری برای هر فاکتور و تولید خروجی یکپارچه شامل عنوان، تاریخ، توضیحات HTML و نوع سند.
5. در صورت فعال بودن گزینه «lbalance»، مانده دوره قبل با عنوان «مانده دوره قبل تا تاریخ X/X/X» در بالای خروجی درج می‌شود.
6. تمامی داده‌ها قبل از بازگردانی، بر اساس زمان شمسی (Jalalian) مرتب‌سازی صعودی می‌شوند.

</div>### ورودی‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D9%86%D8%A8%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>منبع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>شامل فیلترهای صفحه‌بندی و بازه‌ تاریخی.</td></tr><tr><td>id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه همکار مورد نظر برای استخراج صورتحساب.</td></tr><tr><td>branch</td><td>string</td><td>Body</td><td>خير</td><td>شناسه شعبه (در صورت فیلتر مالی). پیش‌فرض: شعبه کاربر فعلی.</td></tr><tr><td>advanced.from</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ شروع بازه گزارش.</td></tr><tr><td>advanced.to</td><td>string (Y-m-d)</td><td>Body</td><td>خیر</td><td>تاریخ پایان بازه گزارش.</td></tr><tr><td>advanced.r</td><td>string/integer</td><td>Body</td><td>خیر</td><td>شماره سریال خاص برای جستجوی مستقیم.</td></tr><tr><td>advanced.lbalance</td><td>boolean</td><td>Body</td><td>خیر</td><td>فعال‌سازی نمایش مانده دوره قبل.</td></tr><tr><td>advanced.fpopen</td><td>boolean</td><td>Body</td><td>خیر</td><td>نمایش اسناد افتتاحیه در ابتدای دفتر حساب.</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "recordsTotal": 54,
  "details": {
    "id": 202,
    "title": "آژانس مهر و ماه",
    "category": "شرکت داخلی",
    "type": "شریک تجاری (بستانکار)"
  },
  "total": {
    "credit": 22650000,
    "debit": 13790000
  },
  "data": [
    {
      "serial_id": "28-202",
      "datetime": "1404/06/20 09:00:00",
      "credit": 2500000,
      "debit": 0,
      "description": {
        "html": "دریافت چک از دفتر مرکزی",
        "text": "دریافت چک از دفتر مرکزی"
      },
      "details": {
        "documents": { "pay": 914 },
        "type": { "subject": "check_paid", "title": "چک نقدی" }
      },
      "relationship": null,
      "communications": false
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- این مسیر فقط برای کاربران احراز هویت‌شده (JWT معتبر) در دسترس است.
- داده‌ها فقط از محدودهٔ شعبه کاربر قابل دسترس است.
- در اطلاعات بازگشتی هیچ دادهٔ محرمانه بانکی ذخیره نمی‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D9%84%DA%AF%D9%88%D8%B1%DB%8C%D8%AA%D9%85-%D8%A8%D8%A7-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- الگوریتم با صفحه‌بندی پایگاه داده (`paginate`) بهینه‌سازی شده است.
- فیلتر تاریخ شمسی با کلاس `Morilog\Jalali\Jalalian` تبدیل و استفاده می‌شود.
- در هر مرحله زمان عملیات با microtime برای تحلیل عملکرد ثبت می‌شود.

</div>### وابستگی‌ها

<div id="bkmrk-use-morilog%5Cjalali%5Cj" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Morilog\\Jalali\\Jalalian;
- use Carbon\\Carbon;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\ApiColleaguesController;
- use App\\Http\\Controllers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-401-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی.</td><td>authWithJwt</td></tr><tr><td>404</td><td>یافت نشدن همکار.</td><td>Database Query</td></tr><tr><td>500</td><td>خطا در پردازش تاریخ یا تجمع داده‌ها (Jalalian/Carbon).</td><td>Logic Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D8%B2%D9%88%D9%84%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ایزوله‌سازی داده‌های «چک برگشتی» از خروجی اصلی جهت جلوگیری از افشای نام اشخاص.
- رمزنگاری مسیر دسترسی به اسناد صورتحساب در Redis Cache.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-with_" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `with_closing` برای کنترل نمایش اختتامیه‌ها.
- افزودن فیلتر نوع سند (`subject_type`) برای جستجوی دقیق‌تر.
- ایجاد قابلیت export به XLSX/CSV.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-colleaguebi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `ColleagueBill`.
- جزئیات ثبت‌شده: user\_id، colleague\_id، بازه تاریخی، مجموع بدهکار/بستانکار.
- سطح حساسیت: **High**.

</div>### جمع‌بندی

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

<div id="bkmrk-colleague-bill" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleagues/ledger-accounts

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleagues/ledger-accounts</td><td dir="ltr">V2ColleaguesController@colleagueLedgerAccounts</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت فهرست حساب‌های معین، کل و تفصیلی مرتبط با هر همکار (Colleague) برای مقاصد گزارش‌گیری حسابداری.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **colleagueLedgerAccounts** وظیفه دارد حساب‌های معین مرتبط با همکار مشخص را از ساختار حسابداری استخراج کند. این تابع معمولاً هنگام تهیه گزارش دفتر کل یا نمایش جزئیات مالی یک همکار فراخوانی می‌شود. عملکرد کلی:

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%D9%94-%D9%87%D9%85%DA%A9%D8%A7%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت شناسهٔ همکار (`colleague_id`) از درخواست و بررسی صحت آن.
2. بازیابی حساب‌های معین (moeen) که در جداول `accounting_moeens` به همکار اشاره دارند.
3. اتصال به جداول `accounting_generals` و `accounting_groups` برای تکمیل سلسله‌مراتب حساب.
4. ساخت خروجی با ساختار سه‌سطحی (گروه – کل – معین) جهت نمایش در UI یا محاسبه.
5. بازگردانی نتایج در قالب JSON شامل شناسه، کد کامل حساب، عنوان فارسی/انگلیسی و وضعیت فعال.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>پارامتر</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>colleague\_id</td><td>integer</td><td>Body</td><td>بله</td><td>شناسه داخلی همکار مورد نظر.</td></tr><tr><td>branch</td><td>string</td><td>Body</td><td>خیر</td><td>شناسه شعبه جهت فیلتر حساب‌ها.</td></tr><tr><td>include\_inactive</td><td>boolean</td><td>Body</td><td>خیر</td><td>اگر مقدار `true` باشد، حساب‌های غیرفعال نیز بازگردانده می‌شوند.</td></tr></tbody></table>

</div>### ساختار خروجی

```
[
  {
    "group": {
      "id": 2,
      "code": "11",
      "title_fa": "دارایی‌ها"
    },
    "general": {
      "id": 17,
      "code": "1101",
      "title_fa": "حساب‌های دریافتنی"
    },
    "moeen": {
      "id": 215,
      "code": "110101",
      "title_fa": "بدهکاران تجاری - همکاران"
    },
    "status": {
      "fa": "فعال",
      "en": "active"
    }
  }
]
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران احراز هویت شده با JWT معتبر مجاز است.
- در صورت وجود پارامتر branch، کاربران فقط به حساب‌های مربوط به شعبه خود دسترسی دارند.
- اطلاعات حساس حساب (مانند شماره شبا یا شناسه مالیاتی) در خروجی حذف می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کوئری‌ها با استفاده از `leftJoin` بین سه جدول اصلی انجام می‌شود تا از N+1 Query جلوگیری شود.
- نتیجه با `collection()` و `map()` به فرمت خروجی تبدیل می‌شود.
- میانگین زمان پاسخ: زیر ۸۰ میلی‌ثانیه.

</div>### وابستگی‌ها

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use App\\Http\\Controllers\\Functions;
- use App\\Models\\AccountingMoeen;
- use App\\Models\\Colleague;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر colleague\_id ارسال نشده است.</td><td>Validation</td></tr><tr><td>404</td><td>هیچ حسابی برای همکار یافت نشد.</td><td>Query Result</td></tr><tr><td>500</td><td>خطای داخلی در پردازش داده‌ها یا joins.</td><td>DB Join Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A8%D9%87%D8%AA%D8%B1-%D8%A7%D8%B3%D8%AA-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%D8%AA%D9%86%D9%87%D8%A7-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بهتر است خروجی تنها شامل شناسه و عنوان بوده و کد کامل حساب به کاربران با سطح Manager نمایش داده شود.
- لاگ‌گیری از عملیات فیلتر بر اساس شعبه به‌منظور ردیابی دسترسی‌های غیرمجاز.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- امکان افزودن پارامتر `type=moeen|general|group` برای فیلتر سطح نمایش.
- اتصال به کش Redis جهت کاهش بار پایگاه داده.
- افزودن نرخ به‌روزرسانی (RefreshDatetime) در خروجی مشابه مسیر colleaguesList.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D9%84%D8%A7%DA%AF%3A-ledgeraccou" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نوع لاگ: `LedgerAccountAccess`
- جزئیات ثبت‌شده: user\_id، colleague\_id، timestamp و تعداد حساب‌های یافته‌شده.
- سطح حساسیت: **Low to Medium**

</div>### جمع‌بندی

مسیر **/colleagues/ledger-accounts** یک نقطه کلیدی برای ارتباط بین سیستم مالی و سیستم حسابداری محسوب می‌شود. با استفاده از این Endpoint می‌توان تمام روابط حسابداری همکار را مرور کرد. کد بهینه‌سازی خوبی در join و تفکیک داده‌ها دارد، اما افزودن caching می‌تواند عملکرد را در محیط سازمانی بزرگ بهبود دهد.

<div id="bkmrk-colleague-ledger-accounts" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleagues/update/financial

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleagues/update/financial</td><td dir="ltr">V2ColleaguesController@updateFinancialColleagues</td><td dir="ltr">authWithJwt</td><td dir="rtl">به‌روزرسانی وضعیت مالی همکاران (بدهکار، بستانکار، مانده فعلی، سقف اعتباری) بر اساس اطلاعات تراکنش‌ها و فاکتورها.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **updateFinancialColleagues** زمانی فراخوانی می‌شود که نیاز باشد اطلاعات مالی تمام یا بخشی از همکاران (Colleagues) بر اساس آخرین تراکنش‌ها محاسبه مجدد شود. منطق کلی آن:

<div id="bkmrk-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. خواندن اطلاعات درخواستی از کلاینت (درخواست عمومی یا برای همکار خاص).
2. دریافت آخرین ماندهٔ مالی از جداول `pays`، `factors`، و `manual_documents`.
3. محاسبهٔ خالص بدهی (Debit) و بستانکاری (Credit) برای هر همکار.
4. تعیین `Diagnosis` هر همکار (Debtor, Creditor, Neutral).
5. به‌روزرسانی فیلدهای مالی در جدول `colleagues` و ثبت زمان آخرین آپدیت در Redis Cache تحت کلید `TIME:colleagues:general_billing`.
6. برگشت خروجی شامل وضعیت کلی هر همکار پس از به‌روزرسانی.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>شامل پارامترهای فیلتر (from, to, id و ...)</td></tr><tr><td>category</td><td>integer</td><td>Body</td><td>خیر</td><td>دسته‌بندی همکار برای محدودسازی (مثلاً ۸: شرکت مسافربری)</td></tr><tr><td>update\_type</td><td>string</td><td>Body</td><td>خیر</td><td>نوع به‌روزرسانی: `partial` یا `full`. پیش‌فرض: full</td></tr><tr><td>branch</td><td>string</td><td>Body</td><td>خیر</td><td>شناسه شعبه برای محدودسازی محاسبه.</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "updated": 46,
  "refreshDatetime": "2025-11-23 13:30:06",
  "data": [
    {
      "colleague_id": 72,
      "name": "آژانس یاقوت شرق",
      "category": "شرکت داخلی",
      "credit": 9140000,
      "debit": 6400000,
      "balance": 2740000,
      "diagnosis": "Creditor",
      "financial_ceiling": 20000000
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7-%D9%85%D8%AF%DB%8C%D8%B1%D8%A7%D9%86-%D9%85%D8%A7%D9%84%DB%8C-%DB%8C%D8%A7-%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط مدیران مالی یا کاربران دارای نقش `finance.admin` امکان فراخوانی دارند.
- به‌روزرسانی کلان روی همهٔ همکاران در ساعت کاری توصیه نمی‌شود.
- کلید Redis هر اجرا باید با شناسه شعبه (branch) جدا شود تا تداخل در داده جلوگیری شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-%D8%A7%D8%B2-batch-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تابع از Batch Update برای کاهش تعداد Queryها استفاده می‌کند.
- Cache زمان آخرین محاسبه در Redis ذخیره می‌شود:

</div>```
Redis::set('TIME:colleagues:general_billing', Carbon::now()->toString());
```

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B7%D9%88%D8%B1-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%A7%D8%B2-%DB%B1%DB%B0" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌طور میانگین از ۱۰۰ رکورد در هر بار اجرا پشتیبانی می‌کند (بهینه برای cron job).

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5Ccarbon%3B-u" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\Carbon;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Http\\Controllers\\Functions;
- use App\\Models\\Colleague;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-401-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر</td><td>authWithJwt</td></tr><tr><td>422</td><td>پارامتر نامعتبر در json</td><td>Validation</td></tr><tr><td>500</td><td>خطای داخلی پایگاه داده هنگام تجمیع داده</td><td>DB::Transaction</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%D8%B3%D8%A7%D8%B2%DB%8C-endpoint-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محدودسازی Endpoint برای نقش‌های Finance.
- ثبت لاگ تمام تغییرات مالی بزرگ‌تر از ۱۰۰ میلیون تومان.
- رمزنگاری کلید Redis با الگوریتم `HMAC_SHA256`.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-delta-up" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ایجاد امکان Delta Update برای فقط همکاران تغییر یافته.
- افزودن فیلد `refresh_by` در Redis برای ثبت شناسهٔ کاربر اجراکننده.
- ساخت گزارش خودکار پس از آپدیت برای مانیتورینگ.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D8%B1%D9%88%DB%8C%D8%AF%D8%A7%D8%AF%3A-updatefi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نام رویداد: `UpdateFinancialColleagues`
- لاگ‌شده در جدول `system_reports` با نوع "financial\_refresh"
- شامل: user\_id، branch، بازه زمانی، تعداد همکاران به‌روزرسانی‌شده.

</div>### جمع‌بندی

تابع **updateFinancialColleagues** بازوی مرکزی حفظ یکپارچگی مالی همکاران است. با ادغام آن با تریگرهای تراکنش و کش Redis، سیستم مالی همکاران همیشه به‌روزرسانی‌شده و هم‌زمان باقی می‌ماند. استفاده مکرر از این مسیر باید با کنترل بار سرور و هماهنگی حسابداری انجام شود.

<div id="bkmrk-update-financial-colleagues" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleagues/update/general-billing

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleagues/update/general-billing</td><td dir="ltr">V2ColleaguesController@updateGeneralBillingColleagues</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازسازی و به‌روزرسانی صورتحساب کلی (General Billing) همکاران بر اساس آخرین مانده‌ها، اسناد مالی و ترازها.</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **updateGeneralBillingColleagues** داده‌های مالی فعلی هر همکار را از جداول مختلف تجمیع کرده و صورتحساب کلی (General Billing)‌ را به‌روزرسانی می‌کند. هدف آن حفظ یکپارچگی اطلاعات مالی همکاران در سامانه است.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D9%81%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهای فیلتر (id خاص یا به‌روزرسانی جمعی)،
2. اجرای محاسبات بدهی/بستانکاری هر همکار از `creditDebit`، `factor`، `pays` و `manual_documents`.
3. تعیین تشخیص وضعیت مالی (Diagnosis): `Creditor`، `Debtor` یا `Neutral`.
4. ذخیره مجموع‌ها در جدول `colleagues` (فیلد balance و diagnosis).
5. به‌روزرسانی مهر زمانی در Redis: `TIMESTAMP = 'TIME:colleagues:general_billing'`
6. بازگرداندن گزارش کلی از همکاران به‌روزشده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>شامل شناسه‌ها و فیلترها (مثل id , from , to)</td></tr><tr><td>branch</td><td>string</td><td>Body</td><td>خیر</td><td>شناسه شعبه جهت تفکیک محاسبات</td></tr><tr><td>ids</td><td>array</td><td>Body</td><td>خیر</td><td>در صورت ارسال، فقط همین همکاران به‌روزرسانی می‌شوند</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "updated": 31,
  "refreshDatetime": "2025-11-23 14:00:21",
  "data": [
    {
      "colleague_id": 14,
      "name": "شرکت سپهر پرواز",
      "credit": 7800000,
      "debit": 1040000,
      "balance": 6760000,
      "diagnosis": "Creditor",
      "financial_ceiling": 30000000,
      "category": "شرکت داخلی"
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B3%D8%B7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- این مسیر فقط برای سطح دسترسی `finance.admin` یا `system.automation` مجاز است.
- هر بار اجرای جمعی باید در context مربوط به branch صورت گیرد تا داده‌ها تداخل نکنند.
- دسترسی این endpoint باید از طریق JWT معتبر کنترل شود.

</div>### نکات عملکردی

<div id="bkmrk-%D9%87%D8%B1-%D9%BE%D8%B1%D8%AF%D8%A7%D8%B2%D8%B4-%D8%B4%D8%A7%D9%85%D9%84-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر پردازش شامل محاسبات از چند جدول (pays, factors, manual\_documents) است؛ بنابراین اجرای انبوه باید به‌صورت async یا cron انجام شود.
- نتیجه آخرین اجرا در Redis ذخیره می‌شود تا دفعات بعد سریع‌تر قابل بازیابی باشد.
- Batch Size مناسب: 50–100 همکار در هر پردازش.

</div>### وابستگی‌ها

<div id="bkmrk-use-carbon%5Ccarbon%3B-u" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Carbon\\Carbon;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\Colleague;
- use App\\Http\\Controllers\\Functions;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D8%AE%D8%B7%D8%A7-%D9%85%D9%86%D8%A8%D8%B9-401-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح خطا</td><td>منبع</td></tr><tr><td>401</td><td>عدم احراز هویت JWT</td><td>Middleware</td></tr><tr><td>422</td><td>پارامتر ناقص یا JSON نامعتبر</td><td>Validation</td></tr><tr><td>500</td><td>خطا در اجرای کوئری محاسبات مالی</td><td>DB Transaction</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%B3%D8%AA%DB%8C-%D8%A8%D9%87-%D9%86%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محدودسازی دستی به نقش مالی (Finance Role).
- در حالت cron، log کامل user\_id و branch نگهداری شود.
- رمزنگاری refreshDatetime در Redis با الگوریتم SHA256.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-increme" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امکان incremental update فقط برای همکاران ویرایش‌شده از آخرین اجرای Redis.
- ایجاد دستور artisan برای اجرای خودکار این مسیر توسط cronjob.
- افزودن فیلد `duration_ms` برای ثبت زمان اجرای واقعی هر batch.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%B1%D9%88%DB%8C%D8%AF%D8%A7%D8%AF%3A-generalbilli" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رویداد: `GeneralBillingUpdate`
- ثبت در جدول `system_reports` با نوع فرایند `billing_refresh`.
- فیلدهای ثبت شده: user\_id، تعداد همکاران به‌روزشده، branch، زمان اجرا.

</div>### جمع‌بندی

تابع **updateGeneralBillingColleagues** پایهٔ نگهداری داده‌های مالی کلان سیستم است. عملکرد درست آن موجب یکپارچگی ترازهای داخلی و صحت گزارش‌های مالی می‌شود. اجرای همزمان آن با مسیر `updateFinancialColleagues` توصیه نمی‌شود تا تداخلی در cache و داده‌ها ایجاد نشود.

<div id="bkmrk-update-general-billing-colleagues" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleague/operation

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleague/operation</td><td dir="ltr">V2ColleaguesController@operationColleague</td><td dir="ltr">authWithJwt</td><td dir="rtl">ایجاد، ویرایش یا حذف همکار با پردازش داده‌های مالی، عمومی و ارتباطی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **operationColleague** نقطه اصلی مدیریت عملیات (CRUD) بر روی موجودیت `colleague` است. بسته به `action` موجود در دادهٔ ورودی، می‌تواند ایجاد، ویرایش، یا حذف داده را انجام دهد. این تابع علاوه بر جدول اصلی `colleagues` جداول کمکی `colleague_additional` و `mapping_colleagues` را نیز مدیریت می‌کند و پس از تغییر، کش Redis را به‌روزرسانی می‌کند.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-js" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهای JSON از بدنه درخواست.
2. بررسی مقدار فیلد `action` (create/update/delete).
3. در صورت create → درج رکورد جدید در جدول اصلی و جداول ارتباطی.
4. در صورت update → اعمال تغییرات در ردیف مشخص‌شده و ثبت تاریخ بروزرسانی.
5. در صورت delete → حذف نرم (soft delete) رکورد همکار و غیرفعالسازی در Redis.
6. به‌روزرسانی کش با job `UpdateRedis` برای شاخص **colleague:{id}**.
7. بازگرداندن نتیجه همراه با وضعیت و زمان انجام عملیات.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>json</td><td>string (JSON)</td><td>Body</td><td>بله</td><td>اطلاعات کامل عملیات و پارامترها</td></tr><tr><td>action</td><td>string</td><td>Body</td><td>بله</td><td>یکی از مقادیر `create`، `update`، `delete`</td></tr><tr><td>id</td><td>int</td><td>Body</td><td>خیر</td><td>شناسه همکار (در حالت update یا delete لازم است)</td></tr><tr><td>office</td><td>string</td><td>Body</td><td>در create الزامی</td><td>نام تجاری یا دفتر همکار</td></tr><tr><td>financial\_code</td><td>string</td><td>Body</td><td>خیر</td><td>کد مالیاتی/شناسه اقتصادی</td></tr><tr><td>type</td><td>int</td><td>Body</td><td>خیر</td><td>نوع همکار (۱: بستانکار، ۲: بدهکار، ۳: دوطرفه)</td></tr><tr><td>category</td><td>int</td><td>Body</td><td>خیر</td><td>دسته‌بندی همکار (شرکت، آژانس، هتل و...)</td></tr><tr><td>details</td><td>object</td><td>Body</td><td>خیر</td><td>اطلاعات تکمیلی در جدول `colleague_additional`</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "action": "update",
  "colleague_id": 142,
  "message": "colleague record updated successfully",
  "time": "2025-11-23T14:32:17+03:30"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران با سطح `admin.colleague` یا `finance.manager` مجاز است.
- هر عملیات در جدول `system_logs` با فیلدهای `user_id`، `action`، `object_id` ثبت می‌شود.
- ورودی JSON قبل از decode با `json_last_error()` کنترل می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-update-%D9%81%D9%82%D8%B7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در عملیات update فقط فیلدهای تغییر یافته در SQL تعریف می‌شود تا Lock کاهش یابد.
- در insert رابطه‌ها به‌صورت batch با Query Builder درج می‌شوند.
- Job اختصاصی `UpdateRedis` برای ریسک پایین Cache Miss ثبت می‌شود.

</div>### Dependencies

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;
- use App\\Jobs\\UpdateRedis;
- use App\\Models\\Colleague;
- use App\\Models\\ColleagueAdditional;
- use App\\Models\\MappingColleagues;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>توضیح</td><td>منبع</td></tr><tr><td>400</td><td>پارامترهای ضروری ارسال نشده‌اند</td><td>Validation</td></tr><tr><td>401</td><td>توکن احراز هویت اعتبار ندارد</td><td>Middleware</td></tr><tr><td>404</td><td>همکار مورد نظر یافت نشد</td><td>Model Query</td></tr><tr><td>500</td><td>خطای داخلی پایگاه داده در زمان ثبت یا بروزرسانی</td><td>DB Transaction</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C-%D8%AD%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری فیلدهای حساس مانند کد اقتصادی قبل از ذخیره.
- عدم اجازه update برای فیلد نقش مالی بدون سطح دسترسی **super.finance**.
- سیاهه ممیزی قابل پیگیری با امضا دیجیتال (hash of payload) افزوده شود.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه کردن پشتیبانی از عملیات bulk (چندهمکار در یک درخواست).
- افزودن تاریخچه تغییرات در جدول `colleague_history`.
- افزودن event realtime برای Notification بخش مدیریت.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%AF%D8%B1-system_logs-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با event: `ColleagueOperation`
- فیلدهای لاگ‌شده: `user_id, colleague_id, action, payload_hash, timestamp`
- ارسال خلاصه فعالیت به Redis Channel: `colleague:activity`

</div>### جمع‌بندی

تابع **operationColleague** موتور اصلی و مرکزی CRUD همکاران در لایه API است. تمامی تغییرات ساختاری، مالی و ارتباطی در یک نقطه مجتمع شده‌اند تا تداخلی میان جداول `colleagues` و `colleague_additional` ایجاد نشود. این ساختار، در امتداد استاندارد داخلی «Enterprise V1.1»، پایه‌ای برای audit، cache و عملیات هم‌زمان فراهم می‌آورد.

<div id="bkmrk-operation-colleague" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /colleague/get

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/colleague/get</td><td dir="ltr">V2ColleaguesController@getColleague</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت جزئیات کامل یک همکار (colleague) شامل اطلاعات عمومی، مالی و ارتباطی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **getColleague** برای خواندن جزئیات کامل یک همکار از پایگاه داده طراحی شده است. ورودی شامل شناسه همکار می‌باشد که از query string در GET request دریافت می‌شود. در خروجی، داده‌ها از جداول `colleagues`، `colleague_additional` و وابستگی‌ها تجمیع می‌شود. چنانچه داده در کش Redis موجود باشد، تابع مستقیماً از آن استفاده می‌کند و در غیر این صورت از Database خوانده، سپس در Redis ذخیره می‌کند.

<div id="bkmrk-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-id-%D8%A7%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. خواندن پارامتر `id` از Query string.
2. بررسی موجود بودن cache (کلید: `colleague:{id}`) در Redis.
3. در صورت وجود → بارگذاری داده از cache، در غیر این صورت: 
    - دریافت رکورد اصلی از جدول `colleagues`.
    - پیوستن دادهٔ اضافی از `colleague_additional`.
    - دریافت روابط از `mapping_colleagues` (در حوزه شرکت اصلی).
    - تبدیل فیلدهای ساختار‌یافته (dates, phone, category, type) به قالب خروجی استاندارد UI.
4. ارسال نتیجهٔ نهایی به صورت JSON.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>query</td><td>بله</td><td>شناسه همکار</td></tr><tr><td>branch</td><td>string</td><td>query</td><td>خیر</td><td>شناسه شعبه برای اعمال محدودیت دامنه</td></tr><tr><td>cache</td><td>boolean</td><td>query</td><td>خیر</td><td>اگر false باشد، کش نادیده گرفته می‌شود و داده از DB بارگذاری می‌شود</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "data": {
    "id": 142,
    "office": "آژانس تابان پرواز",
    "type": 3,
    "category": 4,
    "diagnosis": "Creditor",
    "financial_code": "103245897",
    "credit_amount": 50000000,
    "phone": "+98-31-32110",
    "email": "info@taban.ir",
    "branch": "isfahan",
    "relationship": "official",
    "created_at": "2025-04-21 11:22:53",
    "updated_at": "2025-11-23 14:47:15",
    "additional": {
      "iban": "IR210550080300087123456001",
      "address": "اصفهان، خیابان حکیم نظامی، ساختمان مهر"
    }
  },
  "cache": true
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران با نقش‌های `finance.read` یا `crm.viewer`.
- داده‌های مالی حساس (financial\_code, iban) فقط برای مدیر مالی بازمی‌گردند.
- کنترل سطح دسترسی اضافی با policy `CanViewColleague` انجام می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1-%D8%AD%D8%A7%D9%84%D8%AA-cache-%D9%81%D8%B9%D8%A7%D9%84%D8%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در حالت cache فعال، زمان پاسخ زیر ۱۰ ms می‌باشد.
- هر بار cache پس از update در تابع `operationColleague` با job **UpdateRedis** به‌روزرسانی می‌شود.
- کلید کش: `colleague:{id}` و ساختار ساده JSON ذخیره‌شده.

</div>### Dependencies

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\Colleague;
- use App\\Models\\ColleagueAdditional;
- use App\\Models\\MappingColleagues;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر id ارسال نشده</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی است</td><td>Middleware</td></tr><tr><td>404</td><td>همکار در سیستم یافت نشد</td><td>DB Query</td></tr><tr><td>500</td><td>خطای داخلی پایگاه داده یا Redis</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C-%D8%AD%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری فیلدهای حساس پیش از ارسال (خصوصاً کد مالی و IBAN).
- محدود کردن نرخ درخواست (Rate Limit) روی Endpoint برای جلوگیری از enumeration.
- ثبت لاگ دسترسی کاربران برای هر فراخوانی.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%DA%86%D9%86%D8%AF%DA%AF%D8%A7%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- امکان درخواست چندگانه (Bulk Fetch) بر اساس آرایه شناسه‌ها.
- افزودن فیلد `last_transaction_at` جهت نشان دادن آخرین تعامل مالی همکار.
- پیاده‌سازی response cache expiring بر اساس TTL پویا.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-event-%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A-colleag" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Event ممیزی: `ColleagueFetched`
- فیلدهای لاگ: `user_id, colleague_id, ip, user_agent, cached, datetime`
- ثبت گزارش در جدول `system_logs` با نوع `read_colleague`

</div>### جمع‌بندی

تابع **getColleague** مرجع اصلی برای ارائهٔ جزئیات کامل هر همکار است و به علت وابستگی به کش Redis سرعت پاسخ بالاست. در فرآیندهای مالی و مدیریتی، این endpoint پایه‌ای برای نمایش اطلاعات همکار در تمامی ماژول‌ها (تجارت، حسابداری، آمار) محسوب می‌شود.

<div id="bkmrk-get-colleague" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /colleague/search

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/colleague/search</td><td dir="ltr">V2ColleaguesController@searchColleague</td><td dir="ltr">authWithJwt</td><td dir="rtl">جست‌وجوی سریع همکاران بر اساس نام، کد مالی، یا شناسه</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **searchColleague** برای اجرای جست‌وجوی بلادرنگ در داده‌های همکار طراحی شده است. این API در تمام نقاط سامانه (حسابداری، تسویه، CRM و billing) برای autocomplete باکس‌های انتخاب همکار استفاده می‌شود.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-query" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامتر `query` از Query String.
2. درخواست از کش Redis (کلید: **colleagues:list:light**) در صورت فعال بودن برای پاسخ فوری.
3. اگر در کش موجود نباشد: 
    - یافتن رکوردها در جدول `colleagues` با فیلترهای فازی روی فیلدهای `office`، `first_name`، `last_name` و `financial_code`.
    - ترکیب نتایج با جدول `colleague_additional` برای تکمیل email/phone.
    - محدودسازی خروجی به ۲۰ رکورد برای کارایی.
4. بازگرداندن آرایه‌ای از نتایج به فرمت استاندارد **id‑title‑meta** جهت استفاده در کامپوننت‌های انتخاب.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>query</td><td>string</td><td>query</td><td>بله</td><td>عبارت جست‌وجو (نام، نام‌خانوادگی یا شناسه عددی)</td></tr><tr><td>limit</td><td>integer</td><td>query</td><td>خیر</td><td>حداکثر تعداد نتایج برگردانده‌شده (پیش‌فرض: ۲۰)</td></tr><tr><td>branch</td><td>string</td><td>query</td><td>خیر</td><td>در صورت ارسال، جست‌وجو فقط در آن شعبه انجام می‌شود</td></tr><tr><td>cache</td><td>boolean</td><td>query</td><td>خیر</td><td>در صورت false، داده به اجبار از DB خوانده می‌شود</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "results": [
    {
      "id": 142,
      "title": "آژانس تابان پرواز",
      "meta": {
        "type": "Creditor",
        "category": "آژانس",
        "financial_code": "103245897",
        "phone": "+98-31-32110"
      }
    },
    ...
  ],
  "cache": true
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D9%86%D8%B4%D8%AA-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- برای جلوگیری از نشت داده، فقط فیلدهای ضروری برگردانده می‌شوند.
- در صورت ورود نقش مالی (finance.admin) فیلد کد اقتصادی هم ضمیمه می‌شود.
- همهٔ نتایج از طریق Middleware `authWithJwt` بررسی خواهند شد.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AF%D8%B1%D8%B5%D9%88%D8%B1%D8%AA-%D9%81%D8%B9%D8%A7%D9%84-%D8%A8%D9%88%D8%AF%D9%86-cac" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- درصورت فعال بودن cache، پاسخ زیر ۵ ms خواهد بود.
- فهرست فشرده (LightList) در Redis هر ۶۰ دقیقه توسط job `SyncColleaguesLightList` به‌روزرسانی می‌شود.
- جست‌وجوی پایگاه داده با شاخص مرکب روی `office` + `financial_code` انجام می‌شود.

</div>### Dependencies

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\Colleague;
- use App\\Models\\ColleagueAdditional;
- use Illuminate\\Http\\Request;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%B9%D8%A8%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>عبارت جست‌وجو خالی یا غیرمجاز است</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT منقضی یا نامعتبر است</td><td>Middleware</td></tr><tr><td>429</td><td>تعداد درخواست بیش از حد مجاز (Rate Limit)</td><td>Throttle</td></tr><tr><td>500</td><td>خطای داخلی SQL یا Redis</td><td>DB/Redis</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%DA%AF%D9%88%D8%B1%DB%8C%D8%AA%D9%85-%D8%AC%D8%B3%D8%AA%E2%80%8C%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال الگوریتم جست‌وجوی case‑insensitive و safe‑encoding برای جلوگیری از SQL Injection.
- اجرای Rate‑Limit ۵ درخواست در ثانیه برای هر توکن.
- ثبت تمام جست‌وجوها در `system_logs` جهت پایش رفتار کاربران.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-fuzzy%E2%80%91matchin" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن fuzzy‑matching (Persian Similarity) برای خطاهای تایپی رایج.
- پشتیبانی از جست‌وجوی phonetic (آواشناسی نام‌ها).
- مدیریت cache Scope‑Based برای شرکت‌های دارای چند شعبه.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%AF%D8%B1-system_logs-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با event: `ColleagueSearch`.
- فیلدهای لاگ‌شده: `user_id, query, results_count, cached, timestamp`.
- درصورت ۵ بار جست‌وجوی متوالی بدون نتیجه → هشدار امنیتی.

</div>### جمع‌بندی

تابع **searchColleague** رابط اصلی جست‌وجوی سریع همکاران در کل مجموعه است که با تکیه بر Redis و Query بهینه، تجربه‌ی سرعتی و پاسخ‌گویی بلادرنگ را فراهم می‌کند. این endpoint اساس تمام drop‑down ها و autocomplete های سیستم مدیریت مالی و تجاری است.

<div id="bkmrk-search-colleague" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /colleague/user

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/colleague/user</td><td dir="ltr">V2ColleaguesController@indexColleagueUser</td><td dir="ltr">authWithJwt</td><td dir="rtl">نمایش فهرست کاربران تعریف‌شده برای یک همکار مشخص</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **indexColleagueUser** تمام کاربران ثبت‌شده تحت یک `colleague` (یا شرکت/شعبه شریک) را بازیابی می‌کند. هر کاربر به واسطهٔ شناسهٔ همکار و نوع نقش داخلی در جدول `colleague_users` نگهداری می‌شود. تابع از جدول `colleague_users` و جدول `users` برای تجمیع اطلاعات استفاده می‌کند.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%D9%94-%D9%87%D9%85%DA%A9%D8%A7%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت شناسهٔ همکار از Query String (`colleague_id`).
2. خواندن رکوردهای فعال از جدول `colleague_users`.
3. اتصال با جدول کاربران برای دریافت نام، ایمیل، وضعیت و نقش.
4. بازگرداندن آرایه‌ای از کاربران در ساختار سبک جهت نمایش UI.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>colleague\_id</td><td>integer</td><td>query</td><td>بله</td><td>شناسه همکار</td></tr><tr><td>status</td><td>integer</td><td>query</td><td>خیر</td><td>فیلتر بر اساس وضعیت کاربر (۱:فعال، ۰:غیرفعال)</td></tr><tr><td>role</td><td>string</td><td>query</td><td>خیر</td><td>نام نقش یا سطح دسترسی کاربر در چارچوب آن همکار</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
 "status": true,
 "colleague_id": 52,
 "users": [
   {
     "id": 410,
     "name": "Sara Azimi",
     "email": "s.azimi@example.com",
     "mobile": "09131234567",
     "role": "accountant",
     "active": true,
     "created_at": "2025‑05‑04 12:21:55"
   },
   ...
 ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%B5%D8%B1%D9%81%D8%A7%D9%8B-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی صرفاً برای کاربران با نقش `finance.admin` یا `colleague.manager`.
- درخواست شامل توکن JWT صادر‌شده در domain شرکت اصلی است؛ توکن‌های مشتری مجاز نیستند.
- فقط کاربران با وضعیت فعال (در صورت عدم ارسال پارامتر status) نمایش داده می‌شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84%E2%80%AFcolleague_users" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- جدول `colleague_users` دارای index ترکیبی (colleague\_id, status) است برای lookup سریع.
- کش Redis با کلید `colleague:{id}:users` تا ۱۵ دقیقه نگهداری می‌شود.
- درصورت تغییر، job `SyncColleagueUsers` کش را تازه‌سازی می‌کند.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFilluminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\ColleagueUser;
- use App\\Models\\User;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر colleague\_id ارسال نشده یا نامعتبر است</td><td>Validation</td></tr><tr><td>401</td><td>توکن احراز هویت فاقد مجوز دسترسی به این منبع است</td><td>Middleware</td></tr><tr><td>404</td><td>هیچ کاربری برای همکار مشخص یافت نشد</td><td>DB Query</td></tr><tr><td>500</td><td>خطای پایگاه داده یا Redis</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB%8C%E2%80%AF%D8%A7%DB%8C%D9%85%DB%8C%D9%84%E2%80%AF%D9%88%E2%80%AF%D8%B4%D9%85%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزنگاری ایمیل و شماره موبایل حساس در خروجی برای کاربران غیراختصاصی.
- اعمال policy `CanViewColleagueUser` برای سطح‌بندی دسترسی هیأت حسابرسی.
- به‌روزرسانی لاگ دسترسی برای هر فراخوانی موفق با IP کاربر.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AFpagination%E2%80%AF%D8%B3%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن pagination سمت سرور برای فهرست کاربران پر تعداد.
- پشتیبانی از فیلتر بر اساس role و زمان ایجاد.
- افزودن endpoint جست‌وجوی کاربران همکار (autocomplete).

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-event%E2%80%AF%D9%85%D9%85%DB%8C%D8%B2%DB%8C%3A%E2%80%AFcolleag" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Event ممیزی: `ColleagueUserListed`
- فیلدهای ثبت: `user_id, colleague_id, count, ip, timestamp`
- ثبت در `system_logs` با نوع `read_colleague_user`

</div>### جمع‌بندی

تابع **indexColleagueUser** نمای کلی تمامی کاربران وابسته به یک همکار را بر می‌گرداند و به‌عنوان منبع اصلی نمایش استاندارد در ماژول مدیریت همکاران است. پاسخ JSON آن به‌طور بهینه و با حفظ امنیت اطلاعات تعریف شده است.

<div id="bkmrk-index-colleague-user" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# PUT /colleague/user

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">PUT</td><td dir="ltr">/api/v2/colleague/user</td><td dir="ltr">V2ColleaguesController@updateColleagueUser</td><td dir="ltr">authWithJwt</td><td dir="rtl">ویرایش اطلاعات یکی از کاربران ثبت‌شده زیرمجموعه همکار</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **updateColleagueUser** برای به‌روزرسانی داده‌های کاربر متصل به یک `colleague` خاص است. سامانه بررسی می‌کند که کاربر ارسالی متعلق به همان همکار باشد تا از تغییر دسترسی اشتباه جلوگیری شود.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%D9%94-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت شناسهٔ کاربر با `id` از بدنهٔ درخواست.
2. اعتبارسنجی فیلدهای حیاتی (نام، ایمیل، شماره تماس، نقش، وضعیت).
3. بررسی تعلق کاربر به همکار جاری از طریق جدول `colleague_users`.
4. به‌روزرسانی رکورد در جداول `users` و `colleague_users` در یک تراکنش.
5. پاک کردن کش Redis مربوط به `colleague:{id}:users` تا اطلاعات جدید در فراخوانی بعدی بازسازی شود.
6. بازگرداندن خروجی موفقیت شامل اطلاعات تازهٔ کاربر.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>body</td><td>بله</td><td>شناسه کاربر همکار برای ویرایش</td></tr><tr><td>colleague\_id</td><td>integer</td><td>body</td><td>بله</td><td>شناسه همکار مادر</td></tr><tr><td>name</td><td>string</td><td>body</td><td>خیر</td><td>نام کامل (در صورت تغییر)</td></tr><tr><td>email</td><td>string</td><td>body</td><td>خیر</td><td>ایمیل در سیستم کاربر</td></tr><tr><td>mobile</td><td>string</td><td>body</td><td>خیر</td><td>شماره موبایل (فرمت شمسی کد ملی)</td></tr><tr><td>role</td><td>string</td><td>body</td><td>خیر</td><td>نقش جدید در چارچوب همان colleague</td></tr><tr><td>active</td><td>boolean</td><td>body</td><td>خیر</td><td>وضعیت فعال/غیرفعال</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
 "status": true,
 "message": "User updated successfully",
 "user": {
    "id": 410,
    "colleague_id": 52,
    "name": "Sara Azimi",
    "email": "s.azimi@example.com",
    "mobile": "09131234567",
    "role": "accountant",
    "active": true,
    "updated_at": "2025‑11‑23 08:42:11"
 }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%81%D9%82%D8%B7-%D8%AA%D9%88%D8%B3%D8%B7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌روزرسانی فقط توسط مدیر مالی سازمان (`finance.admin`) یا مدیر همان همکار مجاز است.
- هر ویرایش در جدول `system_logs` ثبت و به کاربر `super.audit` اعلام می‌شود.
- ایمیل منحصربه‌فرد و غیرتکراری در سطح کل سامانه اعتبارسنجی می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4%E2%80%AF%D9%88%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از تراکنش واحد (DB::transaction) برای اطمینان از atomic commit.
- تازه‌سازی کش Redis در مدت کمتر از ۲۰ میلی‌ثانیه با پترن key شبیه `colleague:{id}:users`.
- ارسال webhook بین‌سیستمی در صورت تغییر role کاربر.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFilluminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Models\\User;
- use App\\Models\\ColleagueUser;
- use Illuminate\\Http\\Request;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامترهای ناقص یا خطای اعتبارسنجی ورودی</td><td>Validation</td></tr><tr><td>401</td><td>عدم احراز هویت یا توکن منقضی‌شده</td><td>Middleware</td></tr><tr><td>403</td><td>کاربر دسترسی تغییر کاربران همکار ندارد</td><td>Policy</td></tr><tr><td>404</td><td>کاربر یافت نشد یا متعلق به این همکار نیست</td><td>DB Query</td></tr><tr><td>500</td><td>خطای پایگاه داده یا Rollback تراکنش</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84%E2%80%AFtwo%E2%80%91step%E2%80%AFverif" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال Two‑Step Verification هنگام تغییر نقش critical (مثل finance.manager).
- ثبت historical snapshot از مقادیر قبل و بعد از به‌روزرسانی.
- اعلان سیستمی به CLI برنامه درصورت تغییر نقش کاربر.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی برای Partial‑Update (fast‑patch) فقط روی فیلدهای تغییریافته.
- امکان ثبت "توضیح مدیر" برای هر ویرایش کاربر.
- استفاده از Event Broadcast جهت refresh داشبورد به‌صورت زنده.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-event%3A%E2%80%AFcolleagueuser" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Event: `ColleagueUserUpdated`
- اطلاعات ثبت‌شده: `user_id, colleague_id, changes_json, ip, timestamp`
- ثبت در جدول `system_logs` با نوع `update_colleague_user`

</div>### جمع‌بندی

تابع **updateColleagueUser** پایهٔ اصلی مدیریت کاربران همکاران است که با رعایت atomicity، احراز هویت سخت‌گیرانه و refresh خودکار cache عمل می‌کند. تمامی تغییرات در لاگ سیستمی ثبت می‌شود تا قابلیت ممیزی کامل داشته باشد.

<div id="bkmrk-update-colleague-user" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /colleague/user

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/colleague/user</td><td dir="ltr">V2ColleaguesController@storeColleagueUser</td><td dir="ltr">authWithJwt</td><td dir="rtl">افزودن کاربر جدید به زیرمجموعه یک همکار در سیستم مالی/سازمانی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **storeColleagueUser** برای ایجاد یک کاربر جدید در زیرمجموعه همکار استفاده می‌شود. این تابع در ماژول مدیریت همکاران مورد استفاده مدیران مالی و مدیران همکار قرار می‌گیرد تا بتوانند کارمندان یا مسئولین دسترسی را تعریف کنند.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C-%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی فیلدهای ورودی (نام، ایمیل، شماره تماس، رمز عبور، همکار ID و نقش).
2. بررسی وجود ایمیل یا موبایل تکراری در جدول `users`.
3. ثبت کاربر در جدول `users` و ایجاد رکورد متناظر در جدول `colleague_users` با شناسه همکار.
4. تخصیص نقش و سطح دسترسی (RoleAssignment) مطابق policy داخلی.
5. پاک کردن کش Redis (`colleague:{id}:users`) برای به‌روزرسانی لیست کاربران فعال.
6. بازگرداندن پاسخ حاوی اطلاعات کاربر تازه ایجاد شده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>colleague\_id</td><td>integer</td><td>body</td><td>بله</td><td>شناسه همکار مادر</td></tr><tr><td>name</td><td>string</td><td>body</td><td>بله</td><td>نام کامل کاربر</td></tr><tr><td>email</td><td>string</td><td>body</td><td>بله</td><td>ایمیل منحصربه‌فرد در سطح سامانه</td></tr><tr><td>mobile</td><td>string</td><td>body</td><td>خیر</td><td>شماره موبایل (به فرمت ۹۸+ یا ۰۹)</td></tr><tr><td>password</td><td>string</td><td>body</td><td>بله</td><td>رمز عبور تصادفی یا تعیین‌شده توسط مدیر</td></tr><tr><td>role</td><td>string</td><td>body</td><td>بله</td><td>نقش کاربر در چارچوب همکار (مثل accountant، viewer و...)</td></tr><tr><td>active</td><td>boolean</td><td>body</td><td>خیر</td><td>وضعیت فعال (پیش‌فرض true)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
 "status": true,
 "message": "Colleague user created successfully",
 "user": {
   "id": 615,
   "colleague_id": 52,
   "name": "Ali Rahmani",
   "email": "a.rahmani@example.com",
   "mobile": "09130000000",
   "role": "accountant",
   "active": true,
   "created_at": "2025‑11‑23 08:55:00"
 }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7%E2%80%AF%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86%E2%80%AF%D8%A8%D8%A7%E2%80%AF%D9%86%D9%82%D8%B4%E2%80%AFf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربران با نقش `finance.admin` یا `colleague.manager` حق ایجاد کاربر دارند.
- رمز عبور به‌صورت bcrypt در DB ذخیره می‌شود.
- در صورت تعریف اولیه، ایمیل فعال‌سازی برای کاربر ارسال می‌شود.
- در موارد حساس، ثبت در لاگ مدیریتی `system_logs` انجام می‌شود.

</div>### نکات عملکردی

<div id="bkmrk-%DA%A9%D9%84%E2%80%AF%D9%81%D8%B1%D8%A2%DB%8C%D9%86%D8%AF%E2%80%AF%D8%AF%D8%B1%E2%80%AF%DB%8C%DA%A9%E2%80%AF%D8%AA%D8%B1%D8%A7%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کل فرآیند در یک تراکنش DB واحد (`transaction()`) انجام می‌شود.
- زمان میانگین پاسخ زیر ۵۰ میلی‌ثانیه در شبکه داخلی.
- داده‌های کش کاربران همکار با TTL ۱۵ دقیقه‌ای در Redis نگهداری می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cmodels%5Cuser%3B" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Models\\User;
- use App\\Models\\ColleagueUser;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use Illuminate\\Support\\Facades\\Hash;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%AF%D8%A7%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>دادهٔ ورودی ناقص یا نامعتبر</td><td>Validation</td></tr><tr><td>401</td><td>توکن احراز هویت نامعتبر یا پایان‌یافته</td><td>Middleware</td></tr><tr><td>403</td><td>کاربر حق ایجاد کاربران همکار ندارد</td><td>Policy</td></tr><tr><td>409</td><td>ایمیل یا موبایل تکراری است</td><td>DB Unique</td></tr><tr><td>500</td><td>خطای عمومی در ثبت کاربر یا تراکنش Rollback</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%81%D8%B9%D8%A7%D9%84%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C%E2%80%AF%D9%85%DA%A9%D8%A7%D9%86%DB%8C%D8%B2%D9%85%E2%80%AF%D9%88%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فعال‌سازی مکانیزم ورود دو مرحله‌ای پس از ایجاد کاربر.
- ارسال رمز عبور موقت به ایمیل و مجبور کردن به تغییر در اولین ورود.
- جلوگیری از ایجاد نقش‌های مدیریتی بدون تأیید Manual Admin.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AFevent%E2%80%AF%D8%AA%D8%AE%D8%B5%DB%8C%D8%B5%E2%80%AF%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن event تخصیص نقش زنده (Real‑Time Broadcast) برای refresh پنل.
- پشتیبانی از batch creation کاربران از فایل Excel.
- افزودن endpoint preview برای بررسی قبل از ثبت نهایی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-event%3A%E2%80%AFcolleagueuser" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Event: `ColleagueUserCreated`
- فیلدهای ثبت: `creator_id, colleague_id, user_id, ip, agent, timestamp`
- ثبت در `system_logs` با نوع `create_colleague_user`

</div>### جمع‌بندی

تابع **storeColleagueUser** امکان ایجاد، مدیریت و اتصال کاربران به بخش مالی همکاران را به شکل ایمن فراهم می‌کند. این endpoint پس از اجرای موفق، مبنای تخصیص نقش‌ها و سطوح دسترسی در زیرسیستم Colleague است.

<div id="bkmrk-store-colleague-user" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# DELETE /colleague/user

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">DELETE</td><td dir="ltr">/api/v2/colleague/user</td><td dir="ltr">V2ColleaguesController@deleteColleagueUser</td><td dir="ltr">authWithJwt</td><td dir="rtl">حذف کاربر وابسته به همکار از سیستم با لاگ کامل ممیزی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **deleteColleagueUser** جهت حذف ایمن یک کاربر همکار از مجموعه استفاده می‌شود. فرآیند به‌صورت تراکنش و با درج اطلاعات حذف در `system_logs` انجام می‌گیرد تا سوابق هرگز در سطح DB از بین نرود (Soft Delete).

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%E2%80%AFid%E2%80%AF%D9%88%E2%80%AFco" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت شناسه `id` و `colleague_id` از درخواست.
2. اعتبارسنجی مالکیت: بررسی اینکه کاربر هدف به همان colleague متعلق دارد.
3. پیدا کردن رکورد در `colleague_users` و `users` و بررسی عدم تعامل فعال (مثل فاکتور باز).
4. در صورتی که هیچ dependency فعال وجود ندارد، soft‑delete رکورد از هر دو جدول.
5. انجام rollback در صورت وجود خطا در هر مرحله و ثبت لاگ در `system_logs`.
6. پاک‌سازی کش Redis کلید `colleague:{id}:users`.
7. بازگرداندن پاسخ موفقیت با جزییات کاربر حذف‌شده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>integer</td><td>body</td><td>بله</td><td>شناسه کاربر همکار برای حذف</td></tr><tr><td>colleague\_id</td><td>integer</td><td>body</td><td>بله</td><td>شناسه همکار مالک کاربر</td></tr><tr><td>force</td><td>boolean</td><td>body</td><td>خیر</td><td>اگر true باشد، حذف سخت (DB delete) انجام می‌دهد</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "Colleague user removed successfully",
  "user": {
     "id": 615,
     "colleague_id": 52,
     "name": "Ali Rahmani",
     "email": "a.rahmani@example.com",
     "deleted_at": "2025‑11‑23 09:05:59"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7%E2%80%AF%D9%86%D9%82%D8%B4%E2%80%8C%D9%87%D8%A7%DB%8C%E2%80%AFfinance." style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط نقش‌های `finance.admin` یا `colleague.manager` می‌توانند کاربر را حذف کنند.
- کاربران با پرداخت/سند باز غیرفعال می‌شوند نه حذف.
- همه رخدادها در `system_logs` و `audit_trails` ثبت می‌گردند.

</div>### نکات عملکردی

<div id="bkmrk-%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%AF%D8%B2%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- زمان میانگین پاسخ زیر ۳۰ میلی‌ثانیه به‌دلیل استفاده از SoftDelete.
- کش Redis بعد از حذف بلافاصله پاک می‌شود تا لیست کاربران به‌روزرسانی شود.
- ثبت لاگ در Background Queue برای جلوگیری از delay کاربر.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cmodels%5Cuser%3B" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Models\\User;
- use App\\Models\\ColleagueUser;
- use Illuminate\\Support\\Facades\\DB;
- use Illuminate\\Support\\Facades\\Redis;
- use App\\Events\\ColleagueUserDeleted;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامترهای ورودی ناقص</td><td>Validation</td></tr><tr><td>401</td><td>توکن نامعتبر یا منقضی</td><td>Middleware</td></tr><tr><td>403</td><td>عدم مجوز حذف کاربر</td><td>Policy</td></tr><tr><td>404</td><td>کاربر یافت نشد یا متعلق به این همکار نیست</td><td>DB Lookup</td></tr><tr><td>409</td><td>کاربر در فرآیند فعال است (فاکتور/پرداخت باز)</td><td>Business Rule</td></tr><tr><td>500</td><td>Rollback تراکنش به دلیل Exception</td><td>DB</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%82%D8%A8%D9%84%E2%80%AF%D8%A7%D8%B2%E2%80%AF%D8%AD%D8%B0%D9%81%D8%8C%E2%80%AF%D8%AA%D8%A3%DB%8C%DB%8C%D8%AF%E2%80%AF%D8%AF%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- قبل از حذف، تأیید دو‌مرحله‌ای از مدیر مرکزی دریافت شود.
- اطلاع‌رسانی از طریق WebSocket یا ایمیل به super.audit.
- ایجاد backup از اطلاعات پروفایل کاربر در S3 قبل از حذف.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFreaso" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `reason` برای دلیل حذف در log ها.
- امکان بازگردانی کاربر (restore) با endpoint جداگانه.
- افزودن پشتیبانی از batch delete برای مدیران اصلی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-event%3A%E2%80%AFcolleagueuser" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Event: `ColleagueUserDeleted`
- فیلدهای ثبت‌شده: `user_id, colleague_id, deleted_by, timestamp, ip`
- ثبت در `system_logs` با نوع `delete_colleague_user`

</div>### جمع‌بندی

تابع **deleteColleagueUser** با تکیه بر SoftDelete، بازرسی امنیتی سخت‌گیرانه و ثبت دقیق وقایع ممیزی طراحی شده است. این endpoint حلقه نهایی در چرخه CRUD کاربران همکار به‌شمار می‌رود و تداوم داده‌ای را با امنیت کامل حفظ می‌کند.

<div id="bkmrk-delete-colleague-user" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /upload

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/upload</td><td dir="ltr">UploadController@upload</td><td dir="ltr">authWithJwt</td><td dir="rtl">آپلود فایل روی سرور و دریافت مسیر ذخیره‌سازی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **upload** برای دریافت فایل از کاربر، اعتبارسنجی نوع و اندازهٔ آن، سپس ذخیره‌سازی در مسیر مشخص‌شده در پوشهٔ `uploads/` است. مسیر براساس `branch`، `type` و سال و ماه فعلی ساخته می‌شود. در پایان، مسیر فایل ذخیره‌شده در پاسخ JSON بازگردانده می‌شود.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C%DB%8C%E2%80%AF%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهایی شامل `branch`، `type`، (اختیاری) `mimes` و `size`.
2. ساخت پیکربندی اعتبارسنجی: انواع مجاز و حدّ اکثر سایز.
3. اعتبارسنجی فایل ورودی با قانون Laravel (`required|file|mimes|max`).
4. نام‌گذاری فایل به‌صورت ترکیب تاریخ + microtime برای یونیک بودن.
5. ذخیره‌سازی در مسیر `uploads/{branch}/{type}/{year}/{month}/{extension}/`.
6. بازگرداندن پاسخ با وضعیت موفق و مسیر نهایی فایل.
7. در صورت رخداد استثنا (Exception) بازگرداندن پاسخ خطا با status=false و کد ۴۰۰.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D9%85%D8%AD%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>نوع</td><td>محل</td><td>الزامی</td><td>توضیح</td></tr><tr><td>file</td><td>file</td><td>form‑data</td><td>بله</td><td>فایل آپلودی (تصویر، PDF و...)</td></tr><tr><td>branch</td><td>integer</td><td>body</td><td>بله</td><td>شناسه شعبه مرتبط با کاربر</td></tr><tr><td>type</td><td>string</td><td>body</td><td>بله</td><td>نوع منطقی گروه ذخیره (مثل `profile`، `invoice`)</td></tr><tr><td>size</td><td>integer</td><td>body</td><td>خیر</td><td>حداکثر بایت مجاز (پیش‌فرض ۵۱۲۰ KB)</td></tr><tr><td>mimes</td><td>string</td><td>body</td><td>خیر</td><td>فرمت‌های مجاز مثل `jpeg,png,pdf`</td></tr></tbody></table>

</div>### ساختار خروجی موفق

```
{
  "status": true,
  "file": "uploads/5/profile/2025/11/png/2025-11-23-1732355844.png"
}
```

### ساختار خروجی خطا

```
{
  "status": false,
  "error": "The file field is required."
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2%E2%80%AF%D9%87%D9%88%DB%8C%D8%AA%E2%80%AF%D8%A8%D8%A7%E2%80%AFjwt%E2%80%AF%D8%A7%D8%AC" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت با JWT اجباری است (`authWithJwt` middleware).
- فرمت‌ها و حجم فایل قبل از ذخیره اعتبارسنجی می‌شود.
- مسیر فایل در خروجی شامل نام شعبه و نوع فایل است، بدون افشای داده‌های احراز هویت.
- استفاده از وقت سیستم برای پرهیز از نام‌های تکراری (مقاومت در برابر collision).

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%D8%AA%D9%88%D8%B3%D8%B7%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%A2%D9%BE%D9%84%D9%88%D8%AF%E2%80%AF%D8%A8%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- متوسط زمان آپلود برای فایل ۳ MB در LAN ≈ ۲۵۰ میلی‌ثانیه.
- فایل در storage/app/uploads ذخیره می‌شود؛ توصیه به به‌کارگیری Laravel Symlink برای public visibility.
- عدم استفاده از queue در این نسخه؛ در صورت نیاز به آنتی‌ویروس scan می‌توان queue افزود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFilluminate%5Chttp%5C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;
- use Illuminate\\Support\\Facades\\Storage;
- use App\\Http\\Middleware\\AuthWithJWT;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%B9%D8%AF%D9%85%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>عدم اعتبار فایل ارسالی یا خطای upload</td><td>Validation / Exception</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی</td><td>Middleware</td></tr><tr><td>403</td><td>کاربر مجوز upload ندارد</td><td>Policy (در صورت فعال)</td></tr><tr><td>422</td><td>فیلد اجباری ناقص یا فرمت نامعتبر</td><td>Laravel Validator</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%DA%A9%D8%A7%D9%87%D8%B4%E2%80%AF%D8%AD%D8%AF%D8%A7%DA%A9%D8%AB%D8%B1%E2%80%AF%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%D9%87%E2%80%AF%D8%A2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کاهش حداکثر اندازه آپلود در پنل کاربران عادی.
- اسکن محتوای فایل (PDF/Image) قبل از انتشار در سیستم عمومی.
- تغییر نام دایرکتوری‌ها به UUID در نسخه‌های آتی.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D8%B6%D8%A7%D9%81%D9%87%E2%80%AF%DA%A9%D8%B1%D8%AF%D9%86%E2%80%AF%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اضافه کردن پشتیبانی از storage cloud (S3، MinIO و ...)
- افزودن نوع پرونده در metadata پاسخ.
- تخصیص امتداد hash‑based برای امنیت بیشتر.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFsystem_logs%E2%80%AF%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با فیلد های: `uploader_id, branch, type, path, timestamp, ip`
- در صورت Exception، پلاک در Redis: `upload:errors:{branch}` ایجاد می‌شود.

</div>### جمع‌بندی

تابع **upload** یک endpoint سبک، امن و متن‌باز برای آپلود ساده فایل در زیرسیستم های داخلی سیستم است. با ساختار دایرکتوری پرمبنا (بر اساس branch و type) قابل گسترش برای ذخیره‌سازی امن و تفکیک‌شده می‌باشد.

<div id="bkmrk-upload-file" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /asterisk/test-connection

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/asterisk/test-connection</td><td dir="ltr">AsteriskAmiController@testConnection</td><td dir="ltr">authWithJwt</td><td dir="rtl">تست اتصال به سرویس Asterisk AMI و بررسی سلامت لینک</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **testConnection** اتصال میان سیستم API و سرور Asterisk AMI را بررسی می‌کند. در صورت موفقیت، اطلاعات وضعیت فعلی (مانند version، uptime، channel count) را بر می‌گرداند. در صورت بروز خطا، پاسخی با کد ۵۰۰ و پیام مناسب بر می‌گرداند.

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C%E2%80%AF%D8%AA%D8%A7%D8%A8%D8%B9%E2%80%AFami%3A%3Ag" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. فراخوانی تابع `Ami::getStats()` برای دریافت جزییات ارتباط.
2. در موفقیت، بازگشت پاسخ JSON با `status=true` و فیلد `data` حاوی آمار AMI.
3. در خطا، بازگشت پاسخ JSON با کد ۵۰۰ و علت در فیلد `error`.

</div>### پارامترهای ورودی

هیچ پارامتر بدنه ای ندارد؛ فقط توکن احراز هویت در هدر `Authorization: Bearer <token>`.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

```
{
  "status": true,
  "message": "Successfully connected to Asterisk AMI",
  "data": {
    "version": "Asterisk 18.10.0",
    "uptime": "02:15:48",
    "active_channels": 3,
    "timestamp": 1732363700
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%AF%D9%81%D9%82%D8%B7%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%D9%85%D8%AF%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای مدیران احراز شده با JWT.
- تلاش ناموفق بیشتر از سه مرتبه می‌تواند منجر به مسدود شدن IP شود (در لایه WAF).
- اطلاعات بازگشتی فاقد کلیدها یا رمزهای احراز AMI است.

</div>### نکات عملکردی

<div id="bkmrk-%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%AF%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%E2%89%88%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- زمان پاسخ میانگین ≈ ۵۰ میلی‌ثانیه در شبکه داخلی.
- در صورت قطع اتصال، Exception از کلاس `AMIClient` گرفته می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use Exception;
- use Illuminate\\Http\\JsonResponse;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا ناحاضر</td><td>Middleware</td></tr><tr><td>500</td><td>عدم دسترسی به سرور Asterisk یا timeout</td><td>AMIClient Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84%E2%80%AF%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%AF%D8%AA%D8%B3%D8%AA%E2%80%AF%D9%81%D9%82" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ارسال درخواست تست فقط از IP داخلی.
- عدم ذخیره خروجی در log های عمومی به‌خاطر مشخصات زیرساخت.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AF%D8%A7%D8%AE%D8%AA%DB%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر اختیاری `details` برای دریافت جزئیات بیشتر (مثل context ها و queue ها).
- افزودن cache ۵ ثانیه‌ای برای کاهش فشار بر AMI در سیستم‌های پرترافیک.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C%3A%E2%80%AFoperator_id" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فیلدهای: `operator_id, ip, timestamp, status`
- ثبت در `system_logs` با نوع `asterisk_connection_test`

</div>### جمع‌بندی

تابع **testConnection** پایه‌ای‌ترین نقطهٔ بررسی سلامت ارتباط Asterisk AMI است و در صورت موفقیت، اطمینان از فعال بودن سرورهای تماس را به API می‌دهد.

<div id="bkmrk-asterisk-test-connection" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /asterisk/channels/active

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/asterisk/channels/active</td><td dir="ltr">AsteriskAmiController@getActiveChannels</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت فهرست کانال‌های فعال در سرور Asterisk AMI</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **getActiveChannels** با استفاده از کلاس `AmiClient` به سرویس AMI متصل شده و با فرمان `CoreShowChannels` فهرست تمام کانال‌های فعال در حال مکالمه را واکشی می‌کند. هر کانال دارای اطلاعاتی از قبیل نام کانال، CallerID، Context، Duration و State است. نتیجه در قالب JSON بازگردانده می‌شود.

<div id="bkmrk-%D8%A8%D8%B1%D9%82%D8%B1%D8%A7%D8%B1%DB%8C%E2%80%AF%DA%A9%D8%A7%D9%86%DA%A9%D8%B4%D9%86%E2%80%AF%D8%A8%D8%A7%E2%80%AFam" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. برقراری کانکشن با AMI از طریق `Ami::getConnection()`.
2. ارسال دستور `CoreShowChannels` و دریافت پاسخ.
3. پارْس داده‌ها و ساخت لیستی از channel های فعال (Active Call Sessions).
4. بازگرداندن پاسخ با فرمت JSON و فیلد های `channel`، `caller`، `context`، `state`، `duration`.

</div>### پارامترهای ورودی

درخواست فاقد پارامتر بدنه است. احراز هویت JWT در هدر `Authorization` الزامی می‌باشد.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

```
{
  "status": true,
  "count": 2,
  "channels": [
    {
      "channel": "SIP/2001-0000004e",
      "caller": "09132223344",
      "context": "from-internal",
      "state": "Up",
      "duration": "00:02:34"
    },
    {
      "channel": "SIP/2002-0000004f",
      "caller": "02133112233",
      "context": "from-outgoing",
      "state": "Ring",
      "duration": "00:00:15"
    }
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA%E2%80%AF%D8%AA%D9%85%D8%A7%D8%B3%E2%80%8C%D9%87%D8%A7%E2%80%AF%D9%81%D9%82%D8%B7%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اطلاعات تماس‌ها فقط برای کاربران دارای نقش مدیر قابل مشاهده است.
- خروجی محدود به ۳۰ کانال اول برای جلوگیری از overload.
- هیچ اطلاعات صوتی یا Recording در خروجی وجود ندارد.

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%AF%D8%AF%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ در شبکه‌های محلی: ۴۵ میلی‌ثانیه.
- در سرورهای پرترافیک، نتیجه از cache Redis (کلید `ami:channels:active`) با اعتبار ۵ ثانیه برگردانده می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use Illuminate\\Support\\Facades\\Cache;
- use Illuminate\\Http\\JsonResponse;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر</td><td>Middleware</td></tr><tr><td>504</td><td>Timeout در پاسخ AMI</td><td>AmiClient</td></tr><tr><td>500</td><td>خطای داخلی سرور Asterisk</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AF%D8%B1%E2%80%AF%D9%85%D8%AD%DB%8C%D8%B7%E2%80%AFproduction%E2%80%AF%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در محیط production اطلاعات تماس را mask کنید (مثلاً فقط ۴ رقم آخر).
- دسترسی endpoint را به Role "telecom\_admin" محدود کنید.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AF%3Ffilt" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `?filter=state:Up` برای فیلتر بر اساس وضعیت تماس.
- نمایش مدت مکالمه با فرمت Jalali در پاسخ.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFsystem_logs%E2%80%AF%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با فیلد های `operator_id`، `count`، `timestamp`
- در صورت خطا، ثبت در `asterisk_errors.log`

</div>### جمع‌بندی

تابع **getActiveChannels** برای نمایش بلادرنگ لیست تماس‌های جاری در سیستم است و یکی از پایه‌ای‌ترین API‌ های مانیتورینگ مرکز تماس به‌شمار می‌رود.

<div id="bkmrk-asterisk-get-active-channels" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /asterisk/channel/status

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/asterisk/channel/status</td><td dir="ltr">AsteriskAmiController@getChannelStatus</td><td dir="ltr">authWithJwt</td><td dir="rtl">بررسی وضعیت یک کانال مشخص در Asterisk AMI براساس نام کانال</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **getChannelStatus** به سرور AMI وصل می‌شود و وضعیت دقیق یک Channel مشخص را برمی‌گرداند. درخواست، نام کانال در حال برقراری تماس (مثل `SIP/2001‑0000004e`) را دریافت کرده و با ارسال دستور `ChannelStatus` به AMI، اطلاعات فعلی (نظیر CallerID، state، context، application و duration) را بازمی‌گرداند.

<div id="bkmrk-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFchann" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. خواندن پارامتر `channel` از query string.
2. فراخوانی `Ami::sendAction('ChannelStatus', ['Channel' => $channel])`.
3. در صورت یافتن کانال فعال، پاسخ ساخته می‌شود با فیلدهای State، CallerID، Duration و Application.
4. در صورت نبود کانال یا خطا، پاسخ با status=false و کد ۴۰۴ بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>channel</td><td>Query</td><td>string</td><td>بله</td><td>نام کانال مثلاً `SIP/2001‑0000004e`</td></tr></tbody></table>

</div>### ساختار خروجی موفق

```
{
  "status": true,
  "message": "Channel found",
  "data": {
    "channel": "SIP/2001-0000004e",
    "caller": "09132223344",
    "context": "from-internal",
    "state": "Up",
    "application": "Dial",
    "duration": "00:02:34"
  }
}
```

### ساختار خروجی خطا

```
{
  "status": false,
  "code": 404,
  "error": "Channel not found or inactive"
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%AF%D8%A8%D9%87%E2%80%AF%D8%A7%DB%8C%D9%86%E2%80%AF%D9%85%D8%B3%DB%8C%D8%B1%E2%80%AF%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی به این مسیر فقط برای کاربران دارای نقش مدیریت تلفنی فعال می‌باشد.
- در پاسخ هیچ دادهٔ حساس مثل authkey یا secret خطوط نمایش داده نمی‌شود.
- درخواست‌های بیش از ۳ در هر ۵ ثانیه rate‑limit می‌شوند.

</div>### نکات عملکردی

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%AA%D8%A7%D9%94%D8%AE%DB%8C%D8%B1%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%3A" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین تأخیر پاسخ: ~۴۰ میلی‌ثانیه.
- در صورت Load بالا، از cache با TTL=2 ثانیه برای کانال استفاده می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use Illuminate\\Http\\Request;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر</td><td>Middleware</td></tr><tr><td>404</td><td>کانال پیدا نشد</td><td>AMI Response</td></tr><tr><td>500</td><td>خطای ارتباط با AMI</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFchannel%E2%80%AF%D8%B1%D8%A7%E2%80%AF%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پارامتر `channel` را ورودی user‑validated قرار دهید تا از injection در AMI Action جلوگیری شود.
- فقط در محیط داخلی به AMI دسترسی دهید.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AF%3Ffiel" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `?fields=state,duration` برای واکنش سریع‌تر.
- اتصال real‑time با socket برای به‌روزرسانی‌های وضعیت کانال.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%AC%D8%AF%D9%88%D9%84%E2%80%AFsystem_l" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در جدول `system_logs` با کلید `ami_channel_status`.
- فیلدها: `operator_id, channel, result, timestamp`.

</div>### جمع‌بندی

**getChannelStatus** ابزار پایش دقیق و بلادرنگ هر کانال در سیستم تماس VoIP است و به ادمین‌ها امکان عیب‌یابی سریع ارتباطات را می‌دهد.

<div id="bkmrk-asterisk-get-channel-status" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/call/make

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/call/make</td><td dir="ltr">AsteriskAmiController@makeCall</td><td dir="ltr">authWithJwt</td><td dir="rtl">برقراری تماس خروجی از طریق سرور Asterisk AMI به یک شماره تلفن</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **makeCall** یک درخواست خروجی به AMI ارسال می‌کند تا میان «داخلی اپراتور» و «شماره مقصد» تماس ایجاد شود. با Action `Originate` و پارامترهای Channel، Exten، Context و Priority، سیستم تماس را آغاز می‌کند. در صورت موفقیت، داده `uniqueid` و اطلاعات پایه تماس برگردانده می‌شود.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C%E2%80%AFso" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهای `source` (داخلی مبدأ)، `destination` (شماره مقصد)، `context`، `priority`.
2. اعتبارسنجی ورودی‌ها و بررسی فرمت شماره تلفن.
3. ایجاد Action به‌صورت:  
    `Originate Channel=SIP/{source}, Exten={destination}, Context=from-internal, Priority=1, CallerID={cid}`
4. ارسال به AMI از طریق `Ami::sendAction()`.
5. بررسی پاسخ و بازگرداندن وضعیت تماس با کد ۲۰۰ در صورت موفقیت.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>source</td><td>Body</td><td>string</td><td>بله</td><td>شماره داخلی مبدأ (مثلاً 2001)</td></tr><tr><td>destination</td><td>Body</td><td>string</td><td>بله</td><td>شماره مقصد جهت تماس (مانند 09132223344)</td></tr><tr><td>context</td><td>Body</td><td>string</td><td>خیر</td><td>Context Asterisk (پیش‌فرض: from‑internal)</td></tr><tr><td>priority</td><td>Body</td><td>integer</td><td>خیر</td><td>اولویت دستور در Dial Plan (پیش‌فرض: 1)</td></tr><tr><td>caller\_id</td><td>Body</td><td>string</td><td>خیر</td><td>شناسه نمایش تماس CallerID</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "Call initiated successfully",
  "data": {
    "uniqueid": "1732360572.382",
    "source": "SIP/2001",
    "destination": "09132223344",
    "context": "from-internal",
    "channel_state": "Ring"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2%E2%80%AF%D9%87%D9%88%DB%8C%D8%AA%E2%80%AFjwt%E2%80%AF%D9%88%E2%80%AF%D9%85%D8%AC%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- احراز هویت JWT و مجوز role «telecom\_operator» اجباری است.
- شماره‌های غیرمجاز (خارج از pattern داخلی یا لیست سفید) Rejected می‌شوند.
- برای هر Operator حداکثر ۳ تماس هم‌زمان مجاز است.

</div>### عملکرد سیستم

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF%E2%80%AF%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان ایجاد تماس موفق: ۵۰ تا ۹۰ میلی‌ثانیه.
- Timeout در ارسال Action به AMI : ۵ ثانیه.
- در صورت Fail، Log در `ami_outbound_failures.log` ثبت می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use Illuminate\\Http\\Request;
- use Illuminate\\Support\\Facades\\Validator;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%88%D8%B1%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>ورودی نامعتبر (شماره داخلی یا تلفن)</td><td>Validator</td></tr><tr><td>403</td><td>دسترسی مجاز برای برقراری تماس ندارید</td><td>Middleware</td></tr><tr><td>504</td><td>Timeout در AMI یا عدم پاسخ</td><td>AmiClient</td></tr><tr><td>500</td><td>خطای نامشخص در ارسال Originate</td><td>Exception</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D8%B3%E2%80%8C%D9%87%D8%A7%DB%8C%E2%80%AF%D8%AE%D8%A7%D8%B1%D8%AC%DB%8C%E2%80%AF%D8%B1%D8%A7%E2%80%AF%D8%A8%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تماس‌های خارجی را با Prefix اختصاصی (مثلاً 90XX) محدود کنید.
- درخواستی از IPهای غیرمجاز Rejected شود.
- JWT را در AccessLog ثبت نکنید.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C%E2%80%AF%D8%A7%D8%B2%E2%80%AFqueue%E2%80%AF%D8%AE%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پشتیبانی از queue خودکار در صورت مشغول بودن Channel.
- ارسال webhook به اپلیکیشن CRM پس از اتصال کامل.
- افزودن پارامتر `record=true` برای ضبط تماس.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B3%D8%AA%D9%88%D8%B1%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%AC%D8%AF%D8%A7%D9%88%D9%84%E2%80%AFa" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت دستور در جداول `ami_calls` با فیلدهای `operator_id, source, destination, uniqueid, timestamp`.
- تاریخچه در `system_logs` با کلید `asterisk_make_call` ذخیره می‌شود.

</div>### جمع‌بندی

متد **makeCall** رابطی امن و بلادرنگ برای شروع تماس‌های خروجی از داخلی اپراتور به شماره‌های بیرونی از طریق AMI است و به‌عنوان هستهٔ ماژول Call Management نقش اصلی دارد.

<div id="bkmrk-asterisk-make-call" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/call/hangup

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/call/hangup</td><td dir="ltr">AsteriskAmiController@hangupCall</td><td dir="ltr">authWithJwt</td><td dir="rtl">قطع تماس فعال در سیستم Asterisk با شناسهٔ کانال یا uniqueid</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **hangupCall** جهت پایان دادن به تماسی است که در حال برقراری است. پارامتر ورودی می‌تواند `channel` یا `uniqueid` باشد. در صورت هماهنگ بودن یکی از این دو، دستور AMI با Action `Hangup` به سرور ارسال شده و در صورت موفقیت، پیغام تأیید برمی‌گردد.

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C%E2%80%AF%D8%AF%D8%B1%D8%AE" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی پارامترهای درخواست (`channel` یا `uniqueid`).
2. در صورت عدم ورود هیچکدام، پاسخ 400 برگردانده می‌شود.
3. ایجاد Action `Hangup` و ارسال به AMI از طریق `Ami::sendAction()`.
4. بررسی پاسخ در صورت موفقیت (`Response: Success`).
5. ثبت رویداد در سیستم ممیزی.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>channel</td><td>Body</td><td>string</td><td>خیر\*</td><td>نام کانال (مثلاً `SIP/2001‑0000004e`)</td></tr><tr><td>uniqueid</td><td>Body</td><td>string</td><td>خیر\*</td><td>شناسه منحصربه‌فرد تماس</td></tr><tr><td>reason</td><td>Body</td><td>string</td><td>خیر</td><td>متن علت پایان تماس برای لاگ</td></tr></tbody></table>

</div>\* حداقل یکی از دو پارامتر `channel` یا `uniqueid` باید ارسال شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

```
{
  "status": true,
  "message": "Call hangup successful",
  "data": {
    "channel": "SIP/2001-0000004e",
    "uniqueid": "1732360572.382",
    "terminated_at": "2025-11-23T14:42:58+03:30"
  }
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7%E2%80%AF%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86%E2%80%AF%D8%A8%D8%A7%E2%80%AF%D9%86%D9%82%D8%B4%E2%80%AF%C2%AB" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربران با نقش «telecom\_admin» یا «supervisor» حق قطع تماس دارند.
- تمام درخواست‌ها احراز هویت با JWT می‌شوند.
- تلاش‌های ناموفق در `ami_hangup_attempts` ثبت می‌گردند.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%AF%DA%A9%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ کامل: حدود ۲۰–۵۰ میلی‌ثانیه.
- Timeout در ارتباط با AMI ۴ ثانیه در نظر گرفته شده است.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use Illuminate\\Http\\Request;
- use Carbon\\Carbon;
- use Exception;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%86%D8%A8%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>نبود پارامتر مورد نیاز: channel یا uniqueid</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر</td><td>Middleware</td></tr><tr><td>403</td><td>مجوز قطع تماس ندارید</td><td>Authorization</td></tr><tr><td>404</td><td>کانال مورد نظر یافت نشد</td><td>AMI Response</td></tr><tr><td>504</td><td>Timeout در ارتباط با AMI</td><td>AmiClient</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA%E2%80%AF%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%B1%D9%88%DB%8C%D8%AF%D8%A7%D8%AF%E2%80%AF%D9%82%D8%B7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مدیریت ثبت رویداد قطع تماس در سیستم لاگ مرکزی (`Kibana` یا `ELK`).
- پنهان‌کردن channel واقعی در پاسخ در محیط production.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%85%DA%A9%D8%A7%D9%86%E2%80%AF%D9%82%D8%B7%D8%B9%E2%80%AF%DA%AF%D8%B1%D9%88%D9%87%DB%8C%E2%80%AF%D8%AA%D9%85%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- امکان قطع گروهی تماس‌ها (`hangup_all=true`).
- افزودن پارامتر `force=true` برای قطع تماس‌های بلاتکلیف.
- ثبت duration در بلاک data در خروجی.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFsystem_logs%E2%80%AF%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با کلید `asterisk_hangup_call`.
- ذخیره فیلدهای `operator_id`، `channel`، `uniqueid`، `reason`.

</div>### جمع‌بندی

**hangupCall** روشی ایمن و بلادرنگ برای قطع تماس فعال در مرکز تماس است. این تابع هستهٔ اصلی پایان مدیریت تماس‌ها در بخش Call Management به‌شمار می‌رود.

<div id="bkmrk-asterisk-hangup-call" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/sms/send

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/sms/send</td><td dir="ltr">AsteriskAmiController@sendSms</td><td dir="ltr">authWithJwt</td><td dir="rtl">ارسال پیامک از طریق ماژول <span dir="ltr">Asterisk AMI</span> و ثبت در سیستم پیام های خروجی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **sendSms** برای ارسال پیامک از ماژول GSM متصل به Asterisk کاربرد دارد. پارامترهای `number` و `message` اعتبارسنجی می‌شوند و در صورت دارا بودن device مجاز، دستور AMI با Action `DongleSendSms` به سرور ارسال می‌شود. پس از ارسال موفق، رویداد وب‌سوکت در کانال `sms_notification` منتشر و لاگ در جدول `ami_sms_logs` ثبت می‌شود.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی پارامترهای ورودی (`number` و `message`).
2. تشخیص دستگاه ارسال (`device`) و بررسی اتصال به AMI.
3. ساخت دستور AMI از نوع `DongleSendSms`.
4. ارسال و دریافت نتیجه (بررسی `Response: Success`).
5. ثبت رویداد در وب‌سوکت و ذخیره در پایگاه داده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>number</td><td>Body</td><td>string</td><td>بله</td><td>شماره گیرنده به فرمت IR مثلاً `09132223344`</td></tr><tr><td>message</td><td>Body</td><td>string</td><td>بله</td><td>متن پیامک (UTF-8)</td></tr><tr><td>device</td><td>Body</td><td>string</td><td>خیر</td><td>نام دونگل یا ماژول ارسال مثلاً `dongle0`</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "SMS sent successfully",
  "data": {
    "id": "1732361012_912223344",
    "recipient": "09132223344",
    "device": "dongle0",
    "status": "sent",
    "timestamp": "2025-11-23T15:03:12+03:30"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF%E2%80%AF%D8%AA%D9%88%DA%A9%D9%86%E2%80%AFjwt%E2%80%AF%D9%85%D8%B9%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیازمند توکن JWT معتبر و نقش `telecom_operator` یا `system_admin` است.
- ارسال به شماره‌های غیرمجاز (لیست سیاه) بلافاصله رد می‌شود.
- هر درخواست در جدول `ami_sms_logs` ذخیره می‌شود (شامل operator\_id و status).

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%A7%D8%B1%D8%B3%D8%A7%D9%84%3A%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان ارسال: ۲۰–۸۰ میلی‌ثانیه (بسته به پاسخ ماژول).
- پشتیبانی از queue داخلی در صورت در صف بودن ماژول.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use App\\Services\\NotificationService;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامترهای ورودی نامعتبر</td><td>Validation</td></tr><tr><td>403</td><td>عدم مجوز ارسال پیامک</td><td>Authorization</td></tr><tr><td>500</td><td>خطای پاسخ AMI</td><td>AMI Service</td></tr><tr><td>504</td><td>Timeout در دریافت پاسخ از Asterisk</td><td>Gateway</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84%E2%80%AFrate%E2%80%AFlimit%E2%80%AF%DB%B5%E2%80%AF%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال Rate Limit ۵ درخواست در ۵ ثانیه برای هر کاربر.
- رمزنگاری متن پیامک در Database (اختیاری برای پیام‌های حساس).

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C%E2%80%AF%D8%A7%D8%B2%E2%80%AF%D8%A7%D8%B1%D8%B3%D8%A7%D9%84%E2%80%AF%D8%A7%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پشتیبانی از ارسال انبوه (Bulk SMS).
- افزودن پارامتر `schedule_at` برای ارسال زمان‌بندی‌شده.
- دریافت Delivery Reports (DLR) از ماژول.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFsystem_logs%E2%80%AF%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `system_logs` با کلید `asterisk_send_sms`.
- ذخیره‌ی فیلدها: `operator_id`، `device`، `recipient`، `status`.

</div>### جمع‌بندی

**sendSms** درگاه اصلی ارسال پیامک سیستم Asterisk است که امکان کنترل و مانیتورینگ بی‌درنگ پیام‌های خروجی را فراهم می‌کند. قابل گسترش برای ارسال انبوه، زمان‌بندی و گزارش تحویل می‌باشد.

<div id="bkmrk-asterisk-send-sms" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/ussd/send

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/ussd/send</td><td dir="ltr">AsteriskAmiController@sendUssd</td><td dir="ltr">authWithJwt</td><td dir="rtl">ارسال دستور USSD در ماژول Asterisk و دریافت پاسخ از شبکه مخابراتی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **sendUssd** دستورات کوتاه USSD را به کمک پروتکل AMI به دستگاه GSM ارسال می‌کند. ابتدا مقدار پارامتر `code` بررسی شده و فرمت آن با الگوی USSD تطبیق می‌یابد (مثلاً <span dir="ltr">\*1\*123#</span>). در صورت صحت، دستور با Action `DongleSendUssd` به AMI فرستاده می‌شود و در پایگاه داده به عنوان درخواست ثبت می‌شود. پاسخ خام AMI بر اساس رویداد `DongleUSSD` دریافت شده، در جدول `ami_ussd_logs` ذخیره و از طریق وب‌سوکت در کانال `ussd_notification` به کاربر منتشر می‌شود.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی پارامترهای `code` و `device`.
2. بررسی اتصال به AMI و دسترس بودن دستگاه مورد نظر.
3. ارسال دستور USSD از طریق Action `DongleSendUssd`.
4. ثبت در `ami_ussd_logs`، تماس با وب‌سوکت برای اعلان.
5. بازگرداندن نتیجه به صورت JSON با پاسخ وضعیت شبکه.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>code</td><td>Body</td><td>string</td><td>بله</td><td>کد USSD مثلاً `*140*1#`</td></tr><tr><td>device</td><td>Body</td><td>string</td><td>خیر</td><td>شناسه دستگاه (مثل `dongle1`)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "USSD command sent successfully",
  "data": {
    "id": "USSD-1732362009",
    "code": "*140*1#",
    "device": "dongle1",
    "response": "Your remaining balance is 74,250 IRR",
    "timestamp": "2025-11-23T15:10:09+03:30"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D9%86%E2%80%AF%D9%85%D8%B3%DB%8C%D8%B1%E2%80%AF%D9%81%D9%82%D8%B7%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%DA%A9%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- این مسیر فقط برای کاربران با نقش `telecom_operator` یا `system_admin` قابل دسترسی است.
- در صورت ارسال کدهای USSD خطرناک (مانند ریست تنظیمات سیم‌کارت)، درخواست رد و در `system_logs` ثبت می‌شود.
- کلیه درخواست‌ها و پاسخ‌های شبکه در `ami_ussd_logs` مانیتور می‌شوند.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%AA%D8%A8%D8%A7%D8%AF%D9%84%E2%80%AF%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان تبادل با شبکه: ۱۰۰–۳۰۰ میلی‌ثانیه.
- پاسخ USSD در اکثر شبکه‌ها در کمتر از ۲ ثانیه دریافت می‌شود.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use App\\Services\\NotificationService;
- use Illuminate\\Support\\Facades\\Redis;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%88%D8%B1%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>ورودی نامعتبر یا کد USSD غیراستاندارد</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر</td><td>Authentication</td></tr><tr><td>403</td><td>عدم مجوز ارسال USSD</td><td>Authorization</td></tr><tr><td>500</td><td>عدم پاسخ از AMI</td><td>AMI Gateway</td></tr><tr><td>504</td><td>Timeout در پاسخ شبکه USSD</td><td>Network</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%B3%D8%B7%D8%AD%E2%80%AF%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%AF%D8%A8%D8%B1%E2%80%AF%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت سطح دسترسی بر اساس شماره سیم کارت (IMEI).
- اعمال Rate Limit ۱ درخواست در هر ۳ ثانیه به ازای هر دستگاه.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFauto_" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `auto_reply` برای تعامل چندمرحله‌ای با USSD.
- ثبت تاریخی از پرس‌وجوهای سیم‌کارت برای مصارف آمارگیری.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFami_ussd_logs" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `ami_ussd_logs` با فیلدهای `code`، `device`، `response`، `operator_id`.
- ثبت رویداد در `system_logs` با کلید `asterisk_send_ussd`.

</div>### جمع‌بندی

**sendUssd** امکان مدیریت دستورات USSD را از طریق Asterisk فراهم می‌آورد و برای عملیات‌هایی مانند بررسی اعتبار، خرید بسته یا مدیریت سیم‌کارت استفاده می‌شود. این ماژول در سطح enterprise طراحی شده و دارای لاگ، وب‌سوکت و امنیت چندسطحی است.

<div id="bkmrk-asterisk-send-ussd" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/action/execute

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/action/execute</td><td dir="ltr">AsteriskAmiController@executeAction</td><td dir="ltr">authWithJwt</td><td dir="rtl">اجرای دستور دلخواه AMI با نام <span dir="ltr">Action</span> و پارامترهای متغیر برای مدیران سیستم</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **executeAction** یک رابط عمومی برای ارسال دستورات خام به سرور <span dir="ltr">Asterisk AMI</span> است. پارامتر اصلی ورودی آن `action` می‌باشد که نوع دستور AMI را مشخص می‌کند (برای مثال <span dir="ltr">CoreShowChannels</span>، <span dir="ltr">DongleShowDevices</span>، <span dir="ltr">Reload</span>). تمام آرگومان‌های اختیاری در فیلد `arguments` به صورت آرایه کلید–مقدار ارسال می‌شوند. تابع پس از ارسال دستور به AMI، پاسخ خام (متن و وضعیت دستور) را برمی‌گرداند و در صورت فعال بودن حالت debug، در لاگ `ami_action_logs` ثبت می‌نماید.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C%E2%80%AF%D9%88%D8%AC%D9%88%D8%AF%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی وجود پارامتر `action`.
2. بررسی حق دسترسی کاربر (فقط admin/developer).
3. ارسال درخواست به AMI با متد Service `Ami::action()`.
4. دریافت پاسخ و تبدیل به آرایه قابل خواندن.
5. ثبت در لاگ در صورت فعال بودن گزینه‌ی debug.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>action</td><td>Body</td><td>string</td><td>بله</td><td>نام دستور AMI مثلاً `CoreShowChannels`</td></tr><tr><td>arguments</td><td>Body</td><td>object</td><td>خیر</td><td>پارامترهای ورودی دستور به صورت کلید–مقدار</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "AMI Action executed successfully",
  "data": {
    "response": "Success",
    "message_id": "1732362901.954",
    "details": {
      "Action": "CoreShowChannels",
      "Result": [
        "Channel: SIP/301-000004F2",
        "CallerID: 09132223344",
        "State: Up"
      ]
    }
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%AC%D8%A7%D8%B2%E2%80%AF%D8%AA%D9%86%D9%87%D8%A7%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مجاز تنها برای کاربران با نقش `system_admin` یا `developer`.
- اجرای هرگونه Action غیردر لیست سفید (`AMI_WHITEACTIONS`) ممنوع و موجب بازگشت کد ۴۰۳ می‌شود.
- امکان فعال/غیرفعال سازی انتشار رویداد خروجی در وب‌سوکت وجود دارد.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%AFami%E2%80%AF%3A%E2%80%AF%DB%B5" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین پاسخ AMI : ۵۰–۱۳۰ میلی‌ثانیه بسته به نوع Action.
- در صورت عددموفقیّت، تابع در ۵۰۰ بازگشت داده می‌شود به همراه خطای خام AMI.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cami" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\Ami;
- use App\\Models\\AmiActionLog;
- use Illuminate\\Support\\Facades\\Auth;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر `action` ارسال نشده است</td><td>Validation</td></tr><tr><td>403</td><td>عدم دسترسی به دستور درخواستی</td><td>Authorization</td></tr><tr><td>422</td><td>Action نامعتبر یا غیرفعال در AMI</td><td>AMI Validation</td></tr><tr><td>500</td><td>خطای داخلی AMI در حین اجرا</td><td>AMI Service</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AA%D8%B9%D8%B1%DB%8C%D9%81%E2%80%AF%D9%84%DB%8C%D8%B3%D8%AA%E2%80%AF%D8%B3%D9%81%DB%8C%D8%AF%E2%80%AF%D8%A7%D8%B2%E2%80%AFa" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تعریف لیست سفید از Actions مجاز در کانفیگ سیستم.
- ثبت IP کاربر در لاگ در هر درخواست.
- محدودسازی مقدار آرگومان‌ها به ۵ کلید جهت پرهیز از استفاده غیرمجاز.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFdry_r" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `dry_run` برای شبیه‌سازی بدون اجرای واقعی.
- گزارش مصور از پاسخ AMI در پنل مدیریت.
- افزودن histogram زمان پاسخ Actions برای تحلیل پرفورمنس.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AFami_action_lo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در `ami_action_logs` شامل `user_id`، `action`، `payload` و پاسخ.
- در صورت فعال بودن debug، پاسخ کامل AMI ذخیره می‌شود.

</div>### جمع‌بندی

**executeAction** ابزاری انعطاف‌پذیر برای مدیران سیستم است تا هر فرمان AMI را با کنترل امنیت و لاگ‌گیری کامل اجرا کنند. قابلیت استفاده برای Debug، مدیریت داخلی و توسعه‌ی ابزارهای مانیتور بر پایه‌ی Asterisk را دارد.

<div id="bkmrk-asterisk-action-execute" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /asterisk/websocket/test

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/api/v2/asterisk/websocket/test</td><td dir="ltr">AsteriskAmiController@testWebSocketConnection</td><td dir="ltr">authWithJwt</td><td dir="rtl">تست و بررسی سلامت اتصال وب‌سوکت به سیستم AMI و محاسبه تاخیر (Ping/Pong Latency)</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **testWebSocketConnection** به منظور بررسی در دسترس بودن و پایداری ارتباطات بلادرنگ با وب‌سوکت داخلی ماژول Asterisk طراحی شده است. فرایند به صورت زیر انجام می‌شود:

<div id="bkmrk-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84%E2%80%AF%D9%BE%DB%8C%D9%86%DA%AF%E2%80%AF%28%D9%BE%DB%8C%D8%A7%D9%85%E2%80%AFpin" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. ارسال پینگ (پیام <span dir="ltr">ping:test</span>) به کانال داخلی `asterisk_socket`.
2. دریافت پاسخ از سرویس (پیام <span dir="ltr">pong</span>) و محاسبه‌ی اختلاف زمان بین ارسال و دریافت.
3. بازگرداندن نتیجه به صورت JSON همراه با فیلد `latency_ms` برحسب میلی‌ثانیه.
4. در صورت خطا در اتصال وب‌سوکت، کد ۵۰۴ برگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>none</td><td>-</td><td>-</td><td>-</td><td>این مسیر نیازی به ورودی ندارد.</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "WebSocket connection healthy",
  "data": {
    "ping_sent": "2025-11-23T15:20:42.002+03:30",
    "pong_received": "2025-11-23T15:20:42.135+03:30",
    "latency_ms": 133
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%DB%8C%D9%86%E2%80%AF%D9%85%D8%AA%D8%AF%E2%80%AF%D8%B5%D8%B1%D9%81%D8%A7%D9%8B%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- این متد صرفاً برای کاربران با نقش `system_monitor`، `system_admin` یا `developer` در دسترس است.
- در صورت تشخیص تعداد درخواست‌های متوالی (بیش از ۵ بار در ۳۰ ثانیه)، به مدت ۶۰ ثانیه از دسترسی به این مسیر جلوگیری می‌شود (Throttle Policy).
- هیچ اطلاعات حساس از سرویس در پاسخ برگردانده نمی‌شود.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%AA%D8%A7%D8%AE%DB%8C%D8%B1%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%A7%D8%B1%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین تاخیر در ارتباط با وب‌سوکت: ۱۰۰–۲۰۰ میلی‌ثانیه.
- تست صرفاً به صورت بلادرنگ اجرا شده و از کش استفاده نمی‌کند.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cweb" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\WebSocketGateway;
- use App\\Helpers\\Functions;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی</td><td>Authentication</td></tr><tr><td>403</td><td>کاربر فاقد مجوز برای دسترسی به تست وب‌سوکت</td><td>Authorization</td></tr><tr><td>504</td><td>عدم پاسخ از وب‌سوکت در بازه‌ی ۲ ثانیه</td><td>Connection Timeout</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%81%D8%B9%D8%A7%D9%84%E2%80%AF%D8%B3%D8%A7%D8%B2%DB%8C%E2%80%AFtls%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فعال سازی TLS برای کانال وب‌سوکت `wss://` جهت رمزنگاری پینگ/پُنگ.
- افزودن Signaling Token منحصربه‌فرد برای تست‌های برخط.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%85%DA%A9%D8%A7%D9%86%E2%80%AF%D9%86%D9%85%D8%A7%DB%8C%D8%B4%E2%80%AF%D9%86%D9%85%D9%88%D8%AF%D8%A7%D8%B1%E2%80%AF%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- امکان نمایش نمودار زمان پاسخ (Log Latency Chart) در داشبورد DevOps.
- افزودن پارامتر اختیاری `count` برای اجرای تست تکرارشونده (مانند ۵ بار پیاپی).
- ذخیره میانگین تاخیر در Redis برای تحلیل پایداری شبکه.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%AC%D8%AF%D9%88%D9%84%E2%80%AFami_webs" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در جدول `ami_websocket_logs` به‌صورت روزانه با فیلدهای `operator_id`، `latency`، `ip_address`.
- نمایش تاریخچه پینگ‌ها در پنل پشتیبان زیر منوی Monitoring &gt; AMI Ping.

</div>### جمع‌بندی

**testWebSocketConnection** ابزاری کاملاً سبک برای اطمینان از سلامت وب‌سوکت و کاهش خطاهای زمانی در ارتباطات بلادرنگ AMI می‌باشد. عدم وابستگی به کش، امنیت سطح نقش و ثبت ممیزی بلادرنگ باعث می‌شود که این مسیر برای پایش زنده زیرساخت ارتباطی سیستم بسیار کارآمد باشد.

<div id="bkmrk-asterisk-websocket-test" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /asterisk/notification/test

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/asterisk/notification/test</td><td dir="ltr">AsteriskAmiController@sendTestNotification</td><td dir="ltr">authWithJwt</td><td dir="rtl">ارسال نوتیفیکیشن تستی دلخواه برای بررسی سلامت سیستم انتشار رویدادهای بلادرنگ</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **sendTestNotification** برای تست درستی و پایداری سرویس نوتیفیکیشن داخلی سیستم طراحی شده است. کاربر می‌تواند نوع نوتیفیکیشن (`type`) و محتوای آنی (`data`) را در درخواست ارسال کند تا سرویس، پیام را از طریق کانال بلادرنگ (معمولاً Redis Pub/Sub یا WebSocket Gateway) به تمام Listenerهای فعال منتقل کند. سرویس علاوه بر انتشار رویداد، عملیات را در جدول `ami_notifications_log` با مشخصات ارسال‌کننده ثبت می‌نماید.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C%E2%80%AF%D9%88%D8%AC%D9%88%D8%AF%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی وجود پارامترهای `type` و `data`.
2. ساخت Payload شامل نوع و داده‌ی رویداد.
3. انتشار رویداد در کانال آزمایشی (پیش‌فرض: `test_notification_channel`).
4. ثبت در پایگاه داده `ami_notifications_log` به همراه `user_id`.
5. بازگرداندن نتیجه برای تأیید انتشار.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>type</td><td>Body</td><td>string</td><td>بله</td><td>نوع نوتیفیکیشن (مثلاً `alert`، `info`، `success`)</td></tr><tr><td>data</td><td>Body</td><td>object</td><td>بله</td><td>محتوای متنی و داده‌ای که داخل پیام منتشر می‌شود</td></tr><tr><td>channel</td><td>Body</td><td>string</td><td>خیر</td><td>نام کانال انتشار (در صورت عدم ارسال، پیش‌فرض `test_notification_channel`)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "message": "Notification broadcasted successfully",
  "data": {
    "id": "TEST-NOTIF-1732364015",
    "type": "success",
    "channel": "test_notification_channel",
    "payload": {
      "text": "Ping from AsteriskAmiController",
      "from": "admin"
    },
    "timestamp": "2025-11-23T15:40:15.570+03:30"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%AF%D8%AA%D9%86%D9%87%D8%A7%E2%80%AF%D8%A8%D8%B1%D8%A7%DB%8C%E2%80%AF%DA%A9%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی تنها برای کاربران با نقش `developer` یا `tester` مجاز است.
- هیچ پیامی به کاربران نهایی منتشر نمی‌شود، مگر در کانال آزمایشی خاص.
- پارامتر `data` قبل از انتشار در JSON Encode می‌شود تا از تزریق داده جلوگیری گردد.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%A7%D9%86%D8%AA%D8%B4%D8%A7%D8%B1%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان انتشار در شبکه Redis/WebSocket: ۵۰–۸۰ میلی‌ثانیه.
- ثبت در لاگ در همان تراکنش انجام می‌شود (Atomic Operation).

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFapp%5Cservices%5Cnot" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use App\\Services\\NotificationService;
- use App\\Models\\AmiNotificationLog;
- use Illuminate\\Support\\Facades\\Auth;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%A7%D8%B1%D8%B3%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>ارسال پارامترهای ناقص (نبود type یا data)</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی</td><td>Authentication</td></tr><tr><td>403</td><td>سطح دسترسی ناکافی برای تست نوتیفیکیشن</td><td>Authorization</td></tr><tr><td>500</td><td>خطای داخلی در ارسال به سرویس WebSocket/Redis</td><td>Notification Gateway</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AF%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر احراز هویت دو مرحله‌ای برای فعال سازی حالت DevTest.
- محدود کردن تعداد broadcast ها به ۳۰ در دقیقه به ازای هر کاربر.
- رمزنگاری محتوای data در سطح برنامه در صورت شامل بودن داده‌های حساس.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%E2%80%AFprior" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامتر `priority` برای تعیین اولویت نوتیفیکیشن (LOW/MEDIUM/HIGH).
- ایجاد سیستم Ack/Nack جهت تأیید دریافت توسط Listenerها.
- افزودن پنل گرافیکی در داشبورد Admin برای تست زنده رویدادها.

</div>### ممیزی و لاگ‌ها

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%AC%D8%AF%D9%88%D9%84%E2%80%AFami_noti" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت در جدول `ami_notifications_log` با فیلدهای `user_id`، `channel`، `payload`، `status`.
- ذخیره زمان ارسال در `sent_at` (به صورت timestamp UTC).

</div>### جمع‌بندی

**sendTestNotification** به عنوان ابزار پایش و رفع اشکال بلادرنگ در زیرساخت ارتباطی Asterisk استفاده می‌شود. این مسیر به توسعه‌دهندگان اجازه می‌دهد توان ارسال، انتشار، و ثبت رویدادها را به‌صورت متمرکز و امن سنجش کنند و بنابراین، جزئی کلیدی از پایش زنده‌ی سامانه NotifyCenter است.

<div id="bkmrk-asterisk-notification-test" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /upload/s3

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/api/v2/upload/s3</td><td dir="ltr">S3Controller@uploadFile</td><td dir="ltr">authWithJwt</td><td dir="rtl">آپلود فایل به فضای ابری (S3 یا Liara Storage) با کنترل داینامیک مسیر و پسوند</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **uploadFile** به منظور آپلود ایمن فایل در محیط S3‑Compatible Storage (Liara) طراحی شده است. نحوه‌ی عملکرد:

<div id="bkmrk-%D8%AA%D8%B9%DB%8C%DB%8C%D9%86%E2%80%AF%D9%85%D8%B3%DB%8C%D8%B1%E2%80%AF%28path%29%E2%80%AF%D8%A8%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. تعیین مسیر (`path`) بر اساس `location`، `type`، سال و ماه فعلی.
2. پیکربندی فایل (اندازه، فرمت‌های مجاز، مسیر نهایی).
3. اعتبارسنجی نوع و حجم فایل ارسالی بر مبنای `mimes` و `max`.
4. تشخیص پسوند فایل به‌صورت پله‌ای (بررسی فرم، نام فایل، و آخرین نقطه).
5. تعیین نام نهایی (`fileName`) بر اساس نام فرستاده‌شده یا زمان سیستم.
6. آپلود فایل در Disk با کلید `liara` از طریق Storage Facade لاراول.
7. بازگرداندن پاسخ JSON با وضعیت و مسیر ذخیره‌شده در صورت موفقیت.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>file</td><td>Body(form-data)</td><td>File</td><td>بله</td><td>فایل جهت آپلود</td></tr><tr><td>type</td><td>Body</td><td>string</td><td>خیر</td><td>نوع دسته‌بندی (مثلاً `documents` یا `images`)</td></tr><tr><td>location</td><td>Body</td><td>string</td><td>خیر</td><td>پیشوند دایرکتوری مثلاً `company/profile`</td></tr><tr><td>path</td><td>Body</td><td>string</td><td>خیر</td><td>مسیر سفارشی برای ذخیره (در صورت ارسال، بر location اولویت دارد)</td></tr><tr><td>size</td><td>Body</td><td>integer</td><td>خیر</td><td>حداکثر حجم (کیلوبایت) پیش‌فرض ۵۱۲۰ (۵ مگابایت)</td></tr><tr><td>mimes</td><td>Body</td><td>string</td><td>خیر</td><td>لیست انواع مجاز فایل (جداشده با کاما)</td></tr><tr><td>name</td><td>Body</td><td>string</td><td>خیر</td><td>نام دلخواه برای فایل بدون پسوند (در صورت عدم ارسال ، به‌صورت تاریخ سیستمی تولید می‌شود)</td></tr><tr><td>extension</td><td>Body</td><td>string</td><td>خیر</td><td>پسوند دلخواه در صورت عدم تشخیص اتوماتیک</td></tr><tr><td>branch</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه‌ی شعبه جهت درج در مسیر پیش‌فرض</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "file": "uploads/5/images/2025/11/2025-11-23-1732365001.jpg"
}
```

در صورت اشتباه در آپلود:

```
{
  "status": false,
  "time": 1732365001
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1%E2%80%AF%D9%88%E2%80%AF%D9%86%D8%A7%D9%85%E2%80%AF%D9%81%D8%A7%DB%8C%D9%84%E2%80%AF%D8%AA%D9%88%D8%B3%D8%B7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مسیر و نام فایل توسط کاربر قابل تنظیم است؛ بنابراین اعتبارسنجی کامل ورودی‌ها ضروری است.
- دسترسی به Endpoint محدود به کاربران Authenticate با توکن JWT.
- برای جلوگیری از آپلود فایل بدافزار باید Scan Side‑process فعال باشد.
- بهتر است Storage Bucket روی Private ACL تنظیم شود و دسترسی فایل‌ها فقط از طریق Signed URL باشد.

</div>### عملکرد

<div id="bkmrk-%D9%85%D8%AA%D9%88%D8%B3%D8%B7%E2%80%AF%D8%B2%D9%85%D8%A7%D9%86%E2%80%AF%D8%A2%D9%BE%D9%84%D9%88%D8%AF%E2%80%AF%D9%81%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- متوسط زمان آپلود فایل ۱ مگابایتی ≈ ۴۰۰ میلی‌ثانیه در Liara Storage.
- ایجاد نام منحصربه‌فرد با `Carbon::now()+microtime` برای جلوگیری از تداخل.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFilluminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\Storage;
- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر فایل ناقص یا مغایرت در فرمت/حجم</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>Authentication</td></tr><tr><td>500</td><td>خطای شبکه در هنگام تماس با S3 Storage</td><td>Liara/S3 Adapter</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87%E2%80%AF%D8%A7%D8%B2%E2%80%AFstorage%E2%80%AFp" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از Storage Policy مجزا برای هر ماژول.
- اضافه کردن پارامتر `checksum` برای تأیید تمامیت فایل.
- ذخیره فایل‌های کاربران در پوشه اختصاصی (`user_{id}`).

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AF%D8%A2%D9%BE%D9%84%D9%88%D8%AF%E2%80%AF%DA%86%D9%86%D8%AF%E2%80%AF%D9%81%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن آپلود چند فایل (چندباره در Batch).
- پشتیبانی از فایل رمزی و Chunked Upload برای فایل‌های بزرگ‌تر از ۱۰۰ MB.
- امکان بازگرداندن Public URL در پاسخ API (در صورت فعال بودن ACL عمومی).

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA%E2%80%AF%D9%84%D8%A7%DA%AF%E2%80%AF%D8%A2%D9%BE%D9%84%D9%88%D8%AF%D9%87%D8%A7%DB%8C%E2%80%AF%D9%86%D8%A7%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ آپلودهای ناموفق به کمک `Storage::put` Logs در جدول `system_reports`.
- شامل فیلدهای `operator_id`، `filename`، `ip_address` در گزارش.

</div>### جمع‌بندی

**uploadFile** از هسته‌های اصلی زیرساخت مدیریت فایل سیستم است که با پشتیبانی از مسیر دینامیک، اعتبارسنجی پله‌ای پسوند، و اتصال امن به S3 Storage می‌تواند فرآیند آپلود را با پایداری و امنیت بالا انجام دهد. ساختار طراحی آن مطابق استاندارد Enterprise V1.1 است و قابلیت گسترش برای Multi‑Tenant را نیز دارد.

<div id="bkmrk-upload-s3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# DELETE /media/s3

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">DELETE</td><td dir="ltr">/api/v2/media/s3</td><td dir="ltr">S3Controller@deleteMedia</td><td dir="ltr">authWithJwt</td><td dir="rtl">حذف فایل(ها) از Liara S3 و پاک کردن رکورد در پایگاه داده media</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **deleteMedia** به منظور حذف فایل‌های ذخیره‌شده در فضای S3‑Compatible (Liara) و پاکسازی رکورد‌های مرتبط از جدول `media` طراحی شده است.

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C%E2%80%AF%D9%88%D8%AC%D9%88%D8%AF%E2%80%AF%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی وجود پارامترهای `related_id`، `id` و `path` در درخواست.
2. جستجوی مدیاهای مطابق با یکی از شرایط: 
    - بر اساس `path`
    - بر اساس `id`
    - بر اساس `related_category` و `related_id` (همراه با `related_type`)
3. بررسی وجود فایل در Storage (`disk:liara`)، در صورت وجود حذف فیزیکی.
4. ثبت پیام برای هر فایل حذف شده یا یافت نشده در آرایه `deleted`.
5. حذف رکورد مدیا از جدول `media`.
6. برگرداندن نتیجه عملیات به همراه زمان و آرایه جزئیات حذف.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Query/Body</td><td>integer</td><td>خیر\*</td><td>شناسه مدیا (در صورت عدم ارسال path یا related\_id)</td></tr><tr><td>path</td><td>Query/Body</td><td>string</td><td>خیر\*</td><td>مسیر کامل فایل در S3 (در صورت عدم ارسال id یا related\_id)</td></tr><tr><td>related\_id</td><td>Query/Body</td><td>integer</td><td>خیر\*</td><td>شناسه شیء مرتبط (در صورت عدم ارسال id یا path)</td></tr><tr><td>related\_type</td><td>Query/Body</td><td>string</td><td>خیر</td><td>نوع آیتم مرتبط (جهت استفاده با related\_id)</td></tr></tbody></table>

</div>\* حداقل یکی از پارامترهای `id`، `path` یا `related_id` باید ارسال شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732365500,
  "deleted": [
    "12 - uploads/profile/images/2025/11/img123.jpg - File deleted successfully",
    "15 - uploads/profile/images/2025/11/img999.jpg - File not found"
  ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%E2%80%AF%D8%A8%D9%87%E2%80%AF%D9%85%D8%B3%DB%8C%D8%B1%E2%80%AF%D9%85%D8%AD%D8%AF%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی به مسیر محدود به کاربران Authenticate با توکن JWT معتبر است.
- اطلاعات ورودی باید در سطح تأیید کاربر (Authorization) کنترل شود تا امکان حذف فایل‌های غیر‌مرتبط نباشد.
- از آن‌جا که نام فایل/مسیر ورودی می‌تواند دلخواه ارسال شود، جلوگیری از Directory Traversal ضروری است.

</div>### عملکرد

<div id="bkmrk-%DA%86%DA%A9%E2%80%AF%D9%88%D8%AC%D9%88%D8%AF%E2%80%AF%D9%81%D8%A7%DB%8C%D9%84%E2%80%AF%28storag" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- چک وجود فایل (`Storage::exists`) قبل از حذف، تا حدود ۱–۲ میلی‌ثانیه هزینه دارد.
- حذف فایل‌های زیاد به صورت Loop انجام می‌شود و برای Batch های بالا، ممکن است زمان پاسخ افزایش یابد.

</div>### Dependencies

<div id="bkmrk-use%E2%80%AFilluminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\Storage;
- use Illuminate\\Support\\Facades\\DB;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D8%B9%D8%AF%D9%85%E2%80%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>عدم ارسال پارامترهای الزامی</td><td>Validation</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی</td><td>Authentication</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C%E2%80%AF%D9%85%D8%A7%D9%84%DA%A9%DB%8C%D8%AA%E2%80%AF%D9%81%D8%A7%DB%8C%D9%84%E2%80%AF%28o" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بررسی مالکیت فایل (Owner Check) قبل از حذف.
- لاگ کامل حذف در جدول Audit Logs به همراه IP، UserID و زمان.
- غیرفعال‌سازی حذف مستقیم از S3 برای کاربر نهایی، تنها از طریق API Server.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF%E2%80%AF%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA%E2%80%AF%D8%AD%D8%B0%D9%81%E2%80%AF%DA%AF%D8%B1%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ایجاد قابلیت حذف گروهی (بر اساس لیست IDها یا مسیرها) با یک درخواست.
- امکان Soft‑Delete (فقط علامت‌گذاری به عنوان حذف شده در DB، بدون حذف فیزیکی).
- نمایش پیش‌نمایش فایل‌های موجود و تأیید حذف از سمت کلاینت.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%BE%DB%8C%D8%B4%D9%86%D9%87%D8%A7%D8%AF%E2%80%AF%D8%AB%D8%A8%D8%AA%E2%80%AF%D9%87%D8%B1%E2%80%AF%D8%AD%D8%B0%D9%81%E2%80%AF%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پیشنهاد ثبت هر حذف در جدول `media_deletion_logs` با جزئیات پارامترهای ارسالی.

</div>### جمع‌بندی

**deleteMedia** یک مسیر حذف ایمن برای مدیریت فایل‌های فضای S3 در سامانه است که هم حذف فیزیکی فایل را انجام می‌دهد و هم پاکسازی بانک اطلاعاتی را؛ در صورت پیاده‌سازی لاگ و کنترل مالکیت، این فرایند می‌تواند ضد‌گلوله شود.

<div id="bkmrk-delete-s3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel/attendance

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel/attendance</td><td dir="ltr">OfficialController@attendancePersonnel</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت لیست حضور و غیاب پرسنل با امکان فیلتر پیشرفته و جزئیات مالی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

متد **attendancePersonnel** پس از تأیید احراز هویت، اطلاعات حضور و غیاب پرسنل را با پارامترهای فیلتر متنوع برگردانده و در خروجی جزئیات مالی مرتبط را بهمراه اسناد ثبت شده نمایش می‌دهد.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%AF%D8%A7%D8%AF%D9%87-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D8%A7%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت داده ورودی از `Request` شامل فیلترها، تاریخ‌ها و شناسه پرسنل.
2. جستجوی رکورد پرسنل در جدول مرتبط.
3. بر اساس شرایط زمان‌بندی یا کلید جستجوی پیشرفته (`advanced`)، استخراج حضورها از دیتابیس.
4. ساخت آرایه `Items` شامل اطلاعات هر رویداد: 
    - شماره سریال و شناسه سیستم
    - تاریخ و ساعت ثبت حضور
    - جزئیات عنوان، نوع سند، مبلغ بستانکاری/بدهکاری، تشخیص وضعیت
5. مرتب‌سازی اقلام بر اساس زمان به صورت صعودی.
6. در صورت نیاز، افزودن سند افتتاحیه از جدول `financial_pasts` به ابتدای لیست.
7. بازگرداندن خروجی همراه با جزئیات پرسنل، محدوده فیلتر شده، و داده‌ها.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Query</td><td>integer</td><td>بله</td><td>شناسه پرسنل</td></tr><tr><td>advanced</td><td>Body(JSON)</td><td>object</td><td>خیر</td><td>شیء فیلتر پیشرفته شامل تاریخ شروع/پایان، شناسه سند، وضعیت، و ...</td></tr><tr><td>draw</td><td>Body(JSON)</td><td>integer</td><td>خیر</td><td>شماره درخواست برای DataTables</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "draw": 1,
  "recordsTotal": 100,
  "recordsFiltered": 10,
  "filtered": {
    "from": "2025/11/01",
    "to": "2025/11/23"
  },
  "details": { ...personnelObject },
  "data": [
    {
      "serial_id": 12345,
      "system_serial": 6789,
      "datetime": "1404/08/01 08:15:00",
      "details": {
        "title": { "html": "نام پرسنل | توضیحات سند", "text": "..." },
        "type": { "subject": "pay", "title": "سند مالی" }
      },
      "credit": 0,
      "debit": 500000,
      "diagnosis": "debtor"
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D9%84%D8%B2%D8%A7%D9%85-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AA%D9%88%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- الزام استفاده از توکن JWT معتبر برای دسترسی به مسیر.
- کنترل مجوز روی شناسه پرسنل ارسال شده جهت جلوگیری از مشاهده اطلاعات سایر کاربران.
- ولیدیشن صحیح داده‌های ورودی (بخصوص تاریخ‌ها و کلید فیلتر).

</div>### عملکرد

<div id="bkmrk-%D9%85%D8%B1%D8%AA%D8%A8%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A8%D8%A7-%D8%AA%D8%A7%D8%A8%D8%B9-us" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- مرتب‌سازی با تابع `usort` بر اساس تاریخ انجام می‌شود که برای لیست بزرگ باید بهینه‌سازی شود.
- افزودن سند افتتاحیه از دیتابیس قبل از پاسخ نهایی یک Query اضافی ایجاد می‌کند.

</div>### Dependencies

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Morilog\\Jalali\\Jalalian;
- use Carbon\\Carbon;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-404-%D9%BE%D8%B1%D8%B3%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>404</td><td>پرسنل یافت نشد</td><td>Query DB</td></tr><tr><td>401</td><td>توکن نامعتبر</td><td>AuthWithJwt</td></tr><tr><td>400</td><td>پارامترهای ورودی نامعتبر</td><td>Validation</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-%D9%85%D8%B4%D8%A7%D9%87%D8%AF%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال محدودیت مشاهده حضور و غیاب به سرپرستان یا بخش HR.
- ثبت تمامی درخواست‌های مشاهده در جدول لاگ.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت صفحه‌بندی و سرچ سمت سرور برای دیتاهای بزرگ.
- امکان فیلتر بر اساس نوع رویداد حضور (ورود، خروج، مرخصی و ...).

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B2%D9%85%D8%A7%D9%86-%D9%88-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت زمان و شناسه کاربر مشاهده کننده، همراه IP.
- ثبت مجموع رکوردهای دیده شده در هر درخواست.

</div>### جمع‌بندی

**attendancePersonnel** مسیر پایه برای مشاهده وضعیت حضور پرسنل با جزئیات مالی است. اگر کنترل سطح دسترسی و ثبت رویدادها به‌درستی اجرا شود، مسیر قابلیت استفاده در محیط‌های حساس را دارد.

<div id="bkmrk-get-personnel-attendance" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel/attendance

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel/attendance</td><td dir="ltr">OfficialController@attendancePersonnelStore</td><td dir="ltr">authWithJwt</td><td dir="rtl">ثبت حضور و غیاب روزانه‌ی پرسنل یا اصلاح رکوردهای ثبت‌شده</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **attendancePersonnelStore** برای ثبت یا به‌روزرسانی سوابق حضور و غیاب پرسنل استفاده می‌شود. ورودی شامل شناسه‌ی پرسنل، بازه‌ی تاریخ، وضعیت کاری، و نوع حضور است. در حالت کلی:

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهای ارسالی از درخواست و بررسی وجود کاربر هدف در جدول `personnel`.
2. در صورت ثبت اولیه، رکورد جدیدی در جدول `personnel_attendance` با تاریخ روز و ساعت فعلی درج می‌شود.
3. در حالت ویرایش، داده‌ی موجود با اطلاعات جدید (مانند ساعت ورود/خروج) به‌روزرسانی می‌شود.
4. در صورت ارسال وضعیت مرخصی یا تعطیلی، رکورد در جدول مجزای `personnel_permissions` ثبت می‌گردد.
5. در انتها خروجی شامل وضعیت عملیات و شناسه‌ی رکورد مربوطه بازگردانده می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>personnel\_id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه‌ی پرسنل</td></tr><tr><td>date</td><td>Body</td><td>string (YYYY-MM-DD)</td><td>بله</td><td>تاریخ حضور (به‌صورت شمسی یا میلادی مطابق تنظیم سیستم)</td></tr><tr><td>status</td><td>Body</td><td>string</td><td>بله</td><td>نوع وضعیت: `present`، `absent`، `leave`، یا `holiday`</td></tr><tr><td>clock\_in</td><td>Body</td><td>string (HH:MM)</td><td>خیر</td><td>ساعت ورود</td></tr><tr><td>clock\_out</td><td>Body</td><td>string (HH:MM)</td><td>خیر</td><td>ساعت خروج</td></tr><tr><td>description</td><td>Body</td><td>string</td><td>خیر</td><td>توضیحات تکمیلی</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732365532,
  "data": {
    "id": 1125,
    "personnel_id": 19,
    "date": "1404/08/23",
    "status": "present",
    "clock_in": "08:04",
    "clock_out": null,
    "description": "ثبت اتوماتیک حضور"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2%DA%AF%D8%B0%D8%A7%D8%B1%DB%8C-%D8%AA%D9%88%DA%A9%D9%86%E2%80%AFjwt-%D8%AC%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمزگذاری توکن JWT جهت تأیید دسترسی به اطلاعات پرسنل.
- کاربران تنها به رکوردهای پرسنل سازمان خود دسترسی دارند (بر اساس فیلد `branch_id`).
- ثبت یا تغییر داده نیازمند نقش HR یا Manager است.

</div>### عملکرد

<div id="bkmrk-%D9%87%D8%B1-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AB%D8%A8%D8%AA-%DB%8C%D8%A7-%D9%88%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر عملیات ثبت یا ویرایش تنها یک Query اصلی دارد (INSERT یا UPDATE).
- در صورت فعال بودن Audit Log، یک Query اضافی در جدول `attendance_logs` ثبت می‌شود.

</div>### Dependencies

<div id="bkmrk-use-illuminate%5Csuppo" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- use Illuminate\\Support\\Facades\\DB;
- use Carbon\\Carbon;
- use App\\Models\\PersonnelAttendance;

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-404-%D9%BE%D8%B1%D8%B3%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>404</td><td>پرسنل یافت نشد</td><td>Validation</td></tr><tr><td>422</td><td>تاریخ یا وضعیت نامعتبر است</td><td>Validation</td></tr><tr><td>401</td><td>توکن احراز هویت منقضی شده</td><td>AuthWithJwt</td></tr><tr><td>500</td><td>اشکال در ثبت رکورد در دیتابیس</td><td>DB Transaction</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AFrate%E2%80%AFlimit%E2%80%AF%D8%A8%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن Rate Limit برای درخواست‌های ثبت اتوماتیک (مثلاً کارت یا RFID).
- ولیدیت فیلد تاریخ در سمت سرور بر اساس Time Zone دستگاه.
- ثبت IP و کلید دستگاه ثبت‌کننده (در بازه‌های KPI).

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86%E2%80%AFendpoint%E2%80%AF%D9%85%D8%AC%D8%B2%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن Endpoint مجزا برای ثبت گروهی (برای واحد منابع انسانی).
- محاسبه اتومات تاخیرات و اضافه‌کاری در همین متد در نسخه‌های بعدی.
- افزودن سیستم Cross‑Validation بین شیفت کاری و رکورد حضور.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%87%D8%B1%E2%80%AF%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%E2%80%AF%D8%AF%D8%B1%E2%80%AF%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%E2%80%AF%D8%AD%D8%B6" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر تغییر در رکورد حضور در جدول `attendance_logs` ذخیره می‌شود با فیلدهای: 
    - `personnel_id`
    - `action` (ثبت، ویرایش، حذف)
    - `user_id` اپراتور
    - `timestamp`

</div>### جمع‌بندی

**attendancePersonnelStore** وظیفه‌ی ثبت و به‌روزرسانی سوابق روزانه حضور کارکنان را با سطح دسترسی کنترل‌شده بر عهده دارد. این مسیر پایه‌ی زیرساخت KPI و محاسبات حقوق و دستمزد پرسنل محسوب می‌شود.

<div id="bkmrk-post-personnel-attendance" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel/attendance/permission

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel/attendance/permission</td><td dir="ltr">OfficialController@attendancePersonnelPermission</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت نوع و وضعیت مجوز حضور و غیاب برای پرسنل در شعبهٔ فعلی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

این تابع تعیین می‌کند که کاربر جاری در هنگام ثبت تردد در چه شرایطی مجاز به انجام حضور و غیاب است. تنظیمات بسته به مقدار ذخیره‌شده در کلید `ATTENDANCE` جدول `office_config` مشخص می‌شوند.

<div id="bkmrk-%D9%88%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-%D8%AA%D9%86%D8%B8%DB%8C%D9%85-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. واخوانی مقدار تنظیم حضور و غیاب شعبه جاری از جدول `office_config`.
2. در صورت نبود تنظیم، پاسخ خطای ۴۰۹ با پیام «تنظیمات تعریف نشده است» بازگردانده می‌شود.
3. در غیر این صورت، مقدار کلید بررسی و متن نمایشی فارسی آن تنظیم می‌گردد (مثلاً full → «تصویر / موقعیت»).
4. اگر نوع تنظیم `face` یا `full` باشد، داده چهرهٔ کاربر از جدول `attendance_face_encoding` واکشی می‌شود تا مشخص گردد آیا اجازهٔ ثبت چهره دارد یا نه.
5. در نهایت پاسخ JSON شامل نوع، وضعیت مجوز و جزئیات ذخیره می‌شود.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>Authorization</td><td>Header</td><td>Bearer JWT</td><td>بله</td><td>توکن کاربر برای احراز هویت</td></tr><tr><td>branch</td><td>Request</td><td>integer</td><td>بله</td><td>شناسه دفتر جاری جهت تشخیص تنظیمات حضور</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "payload": {
    "type": {
      "fa": "تصویر / موقعیت",
      "en": "full"
    },
    "permission": true,
    "details": {
      "base_image": "base64Encoded...",
      "status": 1
    }
  },
  "meta": {
    "timestamp": 1732365700
  }
}
```

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B9%D8%AF%D9%85-%D8%AA%D8%B9%D8%B1%DB%8C%D9%81-%D8%AA%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">در صورت عدم تعریف تنظیم:</div>```
{
  "error": {
    "code": 1000,
    "message": "تنظیمات تعریف نشده است"
  },
  "meta": { "timestamp": 1732365700 }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%D8%A8%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی محدود به کاربران احراز‌شده با JWT معتبر.
- اطلاع از تنظیمات سایر شعب صرفاً برای مدیران مجاز مجاز است.
- تابع مستقیماً دادهٔ بیومتریک را باز نمی‌گرداند، بلکه صفر و یک مجوز را تعیین می‌کند.

</div>### عملکرد و بهینه‌سازی

<div id="bkmrk-%D8%AF%D9%88-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%B3%D8%A8%DA%A9-%D8%A8%D9%87-%D8%AC%D8%AF%D9%88%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دو کوئری سبک به جدول‌های `office_config` و `attendance_face_encoding`.
- زمان پاسخ معمول کمتر از ۵۰ میلی‌ثانیه.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- Morilog\\Jalali\\Jalalian

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-jwt-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>JWT نامعتبر یا منقضی</td><td>AuthWithJwt</td></tr><tr><td>409</td><td>تنظیمات حضور برای دفتر تعریف نشده</td><td>DB query</td></tr><tr><td>500</td><td>اشکال داخلی یا استثناء دیتابیس</td><td>Exception handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D8%A8%D8%A7%D8%B2%DA%AF%D8%B1%D8%AF%D8%A7%D9%86%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- جلوگیری از بازگرداندن مستقیم تصویر پایه در خروجی عمومی.
- رمزنگاری کلید `base_image` هنگام ذخیره در DB.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%D8%A2%D8%AE%D8%B1%DB%8C%D9%86-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن تاریخ آخرین به‌روزرسانی تنظیمات دفتر.
- نمایش سطح اعتماد سیستم تشخیص چهره در پاسخ.
- کش‌کردن نتیجه برای هر شعبه به‌مدت ۵ دقیقه.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D9%88-%D8%AF%D9%81" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت شناسه کاربر و دفتر در جدول `system_audit_logs` به همراه زمان درخواست.
- نوع درخواست: **attendance\_permission\_check**

</div>### جمع‌بندی

**attendancePersonnelPermission** پایه‌ای‌ترین متد زیرساختی برای سیستم حضور و غیاب است که مشخص می‌کند آیا کاربر می‌تواند بر اساس نوع تعریف‌شده دفتر (موقعیت، تصویر یا هر دو) اقدام به ثبت تردد کند یا خیر.

<div id="bkmrk-get-personnel-attendance-permission" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel/list</td><td dir="ltr">OfficialController@personnelList</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت لیست کامل پرسنل با امکان فیلتر بر اساس وضعیت، شعبه، نقش و بازه زمانی</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع **personnelList** مجموعه‌ای از رکوردهای پرسنل را از پایگاه داده استخراج می‌کند و فیلترهایی که در درخواست آمده را اعمال می‌نماید.

<div id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%B3%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. پارسه کردن ورودی‌ها (branch, status, category, search, from, to).
2. افزودن شرط‌های `where` براساس پارامترها (مثلاً فیلتر شعبه فقط اگر ارسال شده باشد).
3. پیوستن جداول مرتبط برای افزودن نقش، دسته و سقف مالی.
4. مرتب‌سازی نتایج بر اساس اولویت و نام.
5. بازگرداندن داده با ساختار JSON شامل متادیتا.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>branch</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه شعبه مورد نظر</td></tr><tr><td>status</td><td>Body</td><td>integer</td><td>خیر</td><td>وضعیت کاربر (۰ یا ۱)</td></tr><tr><td>category</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه دسته‌بندی پرسنل</td></tr><tr><td>search</td><td>Body</td><td>string</td><td>خیر</td><td>عبارت جستجو در نام یا کد</td></tr><tr><td>from</td><td>Body</td><td>string</td><td>خیر</td><td>تاریخ شروع بازه (YYYY-MM-DD)</td></tr><tr><td>to</td><td>Body</td><td>string</td><td>خیر</td><td>تاریخ پایان بازه (YYYY-MM-DD)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "draw": 1,
  "recordsTotal": 25,
  "recordsFiltered": 25,
  "data": [
    {
      "id": 101,
      "first_name": "نگین",
      "last_name": "محمدی",
      "branch": "تهران",
      "status": "active",
      "category": "پشتیبانی",
      "financial_ceiling": 40000000
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران احراز شده با JWT و دارای نقش HR یا مدیر.
- کنترل صحت سطح دسترسی‌ها در Middleware انجام می‌شود.

</div>### عملکرد

<div id="bkmrk-optimized-query-%D8%A8%D8%A7-i" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Optimized query با Index روی ستون‌های branch و status.
- Limit/Pagination برای جلوگیری از بارگذاری بیش از حد.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن معتبر نیست یا منقضی شده</td><td>AuthWithJwt</td></tr><tr><td>403</td><td>عدم دسترسی</td><td>Role Check</td></tr><tr><td>500</td><td>اشکال داخلی</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%DA%A9%D8%A7%D9%85%D9%84-%D9%84%D8%A7%DA%AF-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%D9%87%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت کامل لاگ جستجوها برای ممیزی.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%DA%AF%D8%B1%D9%81%D8%AA%D9%86-%D8%A8%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- امکان خروجی گرفتن به Excel و PDF.
- افزودن فیلتر پیشرفته براساس نقش.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت پارامترهای جستجو و تعداد نتایج در جدول audit.

</div>### جمع‌بندی

**personnelList** ابزاری قدرتمند برای مدیران و HR جهت مدیریت و پایش پرسنل با فیلترهای متنوع است.

<div id="bkmrk-post-personnel-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel/bill

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel/bill</td><td dir="ltr">OfficialController@personnelBill</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازیابی و نمایش صورت‌حساب مالی پرسنل با جزییات اسناد و مانده</td></tr></tbody></table>

</div>### منطق عملکرد تابع

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

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D8%AC%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت پارامترهای جستجو از `$Data->advanced` و اعمال فیلتر بازه تاریخ یا شماره سند.
2. واخوانی فاکتورهای پرداخت (pay) از جدول `pays` با شرایط مربوط به پرسنل (functor/object).
3. تشخیص نوع سند: سند عملیات رفرنس، سند کارمزدی، سند دستی، یا عملیات دریافت/پرداخت.
4. ساخت آبجکت‌ جزئیات شامل عنوان، نوع سند، مبلغ بدهکار/بستانکار و تشخیص وضعیت (diagnosis).
5. مرتب‌سازی آیتم‌ها بر اساس زمان.
6. افزودن سند افتتاحیه از جدول `financial_pasts` اگر موجود بود و شرایط اجازه می‌داد.
7. بازگرداندن پاسخ شامل داده‌ها، جزئیات شخص، و اطلاعات فیلتر.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه پرسنل</td></tr><tr><td>Data.advanced.from</td><td>Body</td><td>string</td><td>خیر</td><td>تاریخ شروع (YYYY-MM-DD)</td></tr><tr><td>Data.advanced.to</td><td>Body</td><td>string</td><td>خیر</td><td>تاریخ پایان (YYYY-MM-DD)</td></tr><tr><td>Data.advanced.r</td><td>Body</td><td>mixed</td><td>خیر</td><td>شماره/سریال سند</td></tr><tr><td>Data.draw</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه درخواست برای DataTables</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "draw": 1,
  "recordsTotal": 100,
  "recordsFiltered": 10,
  "filtered": {
    "from": "2025-05-01",
    "to": "2025-06-01"
  },
  "details": {
    "id": 23,
    "first_name": "مریم",
    "last_name": "اکبری",
    "branch": "اصفهان"
  },
  "data": [
    {
      "serial_id": "5100",
      "system_serial": 23,
      "datetime": "1404/02/15 00:00:00",
      "details": {
        "title": {
          "html": "سند افتتاحیه",
          "text": "سند افتتاحیه"
        },
        "type": {
          "subject": "financial_past",
          "title": "افتتاحیه"
        }
      },
      "credit": 2000000,
      "debit": 0,
      "diagnosis": "creditor"
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران احراز شده با JWT.
- اطمینان از اینکه `id` درخواست‌شده متعلق به پرسنل مجاز است.

</div>### عملکرد

<div id="bkmrk-%DA%86%D9%86%D8%AF%DB%8C%D9%86-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%A8%D9%87-%D8%AC%D8%AF%D8%A7%D9%88%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- چندین کوئری به جداول pays، manual\_documents، financial\_pasts با ایندکس مناسب.
- مرتب‌سازی آرایه‌ها در PHP — بهینه‌سازی با usort.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- Morilog\\Jalali\\Jalalian

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-404-%D9%BE%D8%B1%D8%B3%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 90%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>404</td><td>پرسنل یافت نشد</td><td>DB query</td></tr><tr><td>500</td><td>خطای داخلی دیتابیس/پردازش</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-i" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعتبارسنجی پارامتر `id` در سطح سرویس و کنترلر.
- عدم بازگرداندن اطلاعات حساس تراکنش‌ها به کاربران غیرمرتبط.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت صفحه‌بندی نتایج برای درخواست‌های بزرگ.
- گزارش PDF خروجی صورت‌حساب.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%87%D9%85%D9%87-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%D9%87%D8%A7%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت همه درخواست‌های صورت‌حساب در جدول audit با شناسه کاربر و زمان درخواست.

</div>### جمع‌بندی

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

<div id="bkmrk-post-personnel-bill" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel/operation

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; margin: 15px auto; width: 96%; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel/operation</td><td dir="ltr">OfficialController@operationPersonnel</td><td dir="ltr">authWithJwt</td><td dir="rtl">اجرای عملیات مدیریتی روی پرونده پرسنل (بروزرسانی، تغییر وضعیت، مسدودسازی موقت)</td></tr></tbody></table>

</div>### منطق عملکرد تابع

این متد عملیاتی را که از طریق کلید `action` مشخص می‌شود، روی پرسنل اجرا می‌کند:

<div id="bkmrk-update%3A-%D8%A2%D9%85%D8%A7%D8%AF%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- **update**: آماده‌سازی برای بروزرسانی اطلاعات پرسنل (در نسخه فعلی فاقد منطق داخلی).
- **status**: تغییر فیلد `status` اپراتور مشخص‌شده.
- **blocking**: ثبت زمان مسدودسازی تا لحظه‌ای معین روی فیلد `blocked_up` اپراتور.

</div>در پایان، نتیجه اجرای عملیات به‌صورت JSON بازگردانده می‌شود.

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>action</td><td>Body</td><td>string</td><td>بله</td><td>نوع عملیات (update, status, blocking)</td></tr><tr><td>id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه اپراتور هدف</td></tr><tr><td>status</td><td>Body</td><td>integer</td><td>خیر</td><td>مقدار وضعیت جدید (برای action=status)</td></tr><tr><td>duration</td><td>Body</td><td>integer</td><td>خیر</td><td>مدت زمان مسدودسازی به دقیقه (پیش‌فرض: 15 دقیقه، برای action=blocking)</td></tr></tbody></table>

</div>### ساختار خروجی

```
// موفق
{
  "status": true,
  "time": 1732365890
}

// خطا
{
  "status": false,
  "time": 1732365890,
  "message": "500 : SQLSTATE[...]", 
  "trace": [ ... ]
}
```

<div id="bkmrk--2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D9%81%D9%82%D8%B7-%D8%AA%D9%88%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اجرای عملیات فقط توسط ادمین‌ها یا کاربران دارای مجوز مدیریت پرسنل مجاز است.
- ورودی‌ها باید اعتبارسنجی شوند تا از تغییر ناخواسته اطلاعات جلوگیری شود.

</div>### عملکرد

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85%DB%8C-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7-%DB%8C%DA%A9-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تمامی تغییرات با یک کوئری ساده `UPDATE` روی جدول `operators` انجام می‌شود.
- پاسخ معمولاً کمتر از 50ms تولید می‌شود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- Exception handling (PHP native)

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-500-%D8%AE%D8%B7%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>500</td><td>خطای عمومی پایگاه داده یا منطق</td><td>Exception</td></tr><tr><td>400</td><td>ورودی نامعتبر</td><td>Validation layer</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%DA%A9%D8%A7%D9%85%D9%84-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ کامل عملیات انجام‌شده همراه با کاربر اجراکننده.
- محدود کردن مدت مسدودسازی به حداکثر معقول (مثلاً 24 ساعت).

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%AA%DA%A9%D9%85%DB%8C%D9%84-%D9%85%D9%86%D8%B7%D9%82-action%3Dup" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تکمیل منطق action=update برای بروزرسانی خودکار اطلاعات.
- افزودن پاسخ استاندارد همراه با نتیجه دقیق هر عملیات.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%87%D8%B1-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D8%AF%D8%B1-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%DB%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- هر تغییر در وضعیت یا مسدودسازی باید در جدول logهای امنیتی ذخیره شود.

</div>### جمع‌بندی

**operationPersonnel** ابزار مدیریتی پرسنل است که امکان تغییر سریع وضعیت یا اعمال محدودیت را فراهم می‌کند و باید با سیاست‌های امنیتی سختگیرانه اجرا شود.

<div id="bkmrk-post-personnel-operation" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel/access-list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 96%; margin: 15px auto; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel/access-list</td><td dir="ltr">OfficialController@personnelAccessList</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت ساختار کامل سطوح دسترسی پرسنل و ماژول‌ها برای تنظیم مجوز</td></tr></tbody></table>

</div>### منطق عملکرد تابع

این تابع مجموعه‌ای از ماژول‌ها و بخش‌های قابل‌دسترسی در پنل کاربری را به‌صورت درختی بازمی‌گرداند. داده‌ها به شکل آرایه‌ای سازمان‌یافته شامل بخش‌های اصلی (personnel، organizational، salary، modules، contents و ...) و زیربخش‌های هر کدام هستند. هر زیربخش دارای `id` و `title` است تا در تنظیمات مدیریت دسترسی (permissions) استفاده شود.

<div id="bkmrk-%D8%AA%D8%B4%DA%A9%DB%8C%D9%84-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87%E2%80%8C%D8%A7%DB%8C-%D8%A7%D8%B2-%DA%AF%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. تشکیل آرایه‌ای از گروه‌های اصلی (مثل منابع انسانی، مالی، پروژه).
2. درج زیرماژول‌ها به صورت children در هر گروه.
3. بازگرداندن داده با کلید `status=true` و زمان درخواست (`time()`).

</div>### پارامترهای ورودی

این متد هیچ ورودی مستقیم از کاربر نمی‌پذیرد، اما نیاز به احراز هویت JWT دارد تا نقش کاربر مشخص شود.

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="width: 95%; margin: 15px auto; text-align: center; border-collapse: collapse;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>Authorization</td><td>Header</td><td>string</td><td>بله</td><td>توکن JWT معتبر برای تعیین هویت اپراتور</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732366012,
  "data": {
    "personnel": {
      "id": "personnel",
      "title": "پرسنل",
      "children": {
        "profile": {"title": "پروفایل کاربر", "id": "profile"},
        "attendance": {"title": "حضور و غیاب", "id": "attendance"},
        "document": {"title": "مدارک پرسنلی", "id": "document"},
        "bill": {"title": "صورت حساب پرسنل", "id": "bill"}
      }
    },
    "organizational": {"id": "organizational","title": "دپارتمان سازمانی","children": false},
    "salary": {"id": "salary","title": "حقوق و دستمزد","children": false},
    "modules": {
      "id": "modules",
      "title": "ماژول ها",
      "children": {
        "lottery": {"title": "ماژول لاتاری", "id": "lottery"},
        "lottery_registrations": {"title": "ثبت نام های ماژول لاتاری", "id": "lottery_registrations"}
      }
    }
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%81%D9%82%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%A7%D9%86%DB%8C-%DA%A9%D9%87-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فقط کاربرانی که توکن معتبر JWT دارند می‌توانند این لیست را مشاهده کنند.
- در پاسخ هیچ داده حساس (مانند سطح دسترسی فردی یا تاریخچه مجوزها) وجود ندارد.
- خروجی صرفاً ساختار دسترسی عمومی است که برای UIها استفاده می‌شود.

</div>### عملکرد

<div id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%AB%D8%A7%D8%A8%D8%AA-%28static" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- داده‌ها ثابت (static) هستند و از دیتابیس واکشی نمی‌شوند.
- می‌توان آن را cache کرد (مثلاً Redis با TTL 24 ساعت).
- زمان پاسخ: کمتر از 5ms.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB (برای سایر توابع مرتبط)
- Carbon\\Carbon (برای timestamp در خروجی)

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%B9%D8%AF%D9%85-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>عدم احراز هویت</td><td>Middleware</td></tr><tr><td>500</td><td>خطای سیستم در هنگام تولید ساختار دسترسی</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D9%86%D8%AA%DB%8C%D8%AC%D9%87-%D8%AF%D8%B1-%D8%AD%D8%A7%D9%81%D8%B8%D9%87" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ذخیره نتیجه در حافظه cache برای جلوگیری از فراخوانی بی‌مورد کنترلر.
- افزودن کش جداگانه برای هر نقش (Role-based caching).

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D9%85%D8%AC%D9%88%D8%B2%D9%87%D8%A7-%D8%A8%D9%87-role" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اتصال مجوزها به Roleهای واقعی در دیتابیس برای کنترل دسترسی پویا.
- افزودن سطح خواندن/نوشتن برای زیرماژول‌ها.
- پشتیبانی از چندزبانگی titles.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D8%A7%DB%8C%D9%86-%D9%85%D8%AA%D8%AF-%D9%86%DB%8C%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- فراخوانی این متد نیازی به ثبت لاگ ندارد چون فقط داده استاتیک بازمی‌گرداند.

</div>### جمع‌بندی

**personnelAccessList** ساختار کامل ماژول‌ها و صفحات قابل‌دسترسی سیستم را تولید می‌کند و پایه‌ای برای سیستم مدیریت مجوزهاست. به دلیل استاتیک بودن داده، کارایی بالا و ریسک امنیتی پایین دارد.

<div id="bkmrk-get-personnel-access-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel</td><td dir="ltr">OfficialController@personnelIndex</td><td dir="ltr">authWithJwt</td><td dir="rtl">دریافت فهرست یا اطلاعات کلی همه پرسنل برای نمایش در داشبورد مدیریت</td></tr></tbody></table>

</div>### منطق عملکرد تابع

این متد بدون نیاز به ورودی جزئی، لیست کامل پرسنل یا اطلاعات پایه آن‌ها را از دیتابیس واکشی می‌کند. بسته به نقش و سطح دسترسی کاربر، ممکن است فیلترهایی روی لیست اعمال شود (مثلاً فقط پرسنل فعال یا پرسنل زیرمجموعه یک شعبه خاص).

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%86%D9%82%D8%B4-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%A7%D8%B2-%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی نقش کاربر از روی JWT برای تعیین محدوده دسترسی به داده‌ها.
2. واکشی رکوردها از جدول `operators` یا جداول مرتبط.
3. بارگذاری داده‌های مرتبط مانند وضعیت، شعبه، نقش، اطلاعات تماس.
4. بازگرداندن خروجی JSON با کلید `data` و متادیتا.

</div>### پارامترهای ورودی

این متد پارامتر ورودی از کاربر ندارد، اما نیاز به درخواست GET همراه با توکن JWT معتبر دارد.

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>Authorization</td><td>Header</td><td>string</td><td>بله</td><td>توکن JWT معتبر</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732366138,
  "data": [
    {
      "id": 1,
      "name": "علی رضایی",
      "username": "alireza",
      "branch": "اصفهان",
      "status": 1,
      "role": "مدیر سیستم",
      "phone": "09123456789",
      "email": "alireza@example.com",
      "created_at": "1404/02/15"
    },
    ...
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%AD%D8%B3%D8%A7%D8%B3-%D9%85%D8%A7%D9%86%D9%86%D8%AF-%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اطلاعات حساس مانند ایمیل و شماره تماس فقط به کاربران دارای نقش مدیر نمایش داده می‌شود.
- خروجی باید بر اساس سطح دسترسی کاربر فیلتر شود.

</div>### عملکرد

<div id="bkmrk-%D9%BE%D8%A7%D8%B3%D8%AE-%D9%85%D8%AA%DA%A9%DB%8C-%D8%A8%D9%87-%DB%8C%DA%A9-%DA%A9%D9%88%D8%A6%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پاسخ متکی به یک کوئری اصلی SELECT با امکان استفاده از ایندکس روی فیلد status و branch.
- زمان پاسخ معمول: 40–70ms با کش مناسب.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن JWT نامعتبر یا منقضی شده</td><td>Middleware</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B9%D9%85%D8%A7%D9%84-mask-%D8%B1%D9%88%DB%8C-%D8%A8%D8%AE%D8%B4%DB%8C-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- اعمال mask روی بخشی از شماره تماس در خروجی عمومی.
- ثبت لاگ همه درخواست‌های مشاهده لیست پرسنل.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن امکان فیلتر بر اساس نقش، شعبه و وضعیت.
- افزودن قابلیت pagination برای لیست‌های بزرگ.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%AF%D8%B1%D8%AE%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت شناسه کاربر درخواست‌کننده و زمان مشاهده لیست.

</div>### جمع‌بندی

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

<div id="bkmrk-get-personnel-index" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel</td><td dir="ltr">OfficialController@personnelStore</td><td dir="ltr">authWithJwt</td><td dir="rtl">ثبت اطلاعات و ایجاد کاربر (پرسنل) جدید در سیستم</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `personnelStore` ورودی JSON شامل جزئیات پرسنل را دریافت و صحت‌سنجی می‌کند، سپس رکورد جدیدی در دیتابیس ایجاد می‌کند. بعد از ذخیره، شناسه و اطلاعات پایه کاربر جدید در خروجی برگردانده می‌شود.

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%86%D9%82%D8%B4-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%AC%D8%A7%D8%B1%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی نقش کاربر جاری (دسترسی ایجاد پرسنل).
2. اعتبارسنجی فیلدهای ضروری مثل نام، نام کاربری، شعبه، نقش.
3. درج رکورد در جدول `operators` به همراه هش کردن رمز عبور.
4. اختصاص نقش و تنظیمات پیش‌فرض دسترسی.
5. بازگرداندن خروجی موفق شامل شناسه و زمان ایجاد.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>name</td><td>Body</td><td>string</td><td>بله</td><td>نام کامل پرسنل</td></tr><tr><td>username</td><td>Body</td><td>string</td><td>بله</td><td>نام کاربری (یونیک)</td></tr><tr><td>password</td><td>Body</td><td>string</td><td>بله</td><td>رمز عبور (پیش از ذخیره هش می‌شود)</td></tr><tr><td>branch</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه شعبه</td></tr><tr><td>role</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه نقش سازمانی</td></tr><tr><td>email</td><td>Body</td><td>string</td><td>خیر</td><td>ایمیل پرسنل</td></tr><tr><td>phone</td><td>Body</td><td>string</td><td>خیر</td><td>شماره تماس</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732366188,
  "data": {
    "id": 152,
    "name": "علیرضا ایرانپور",
    "username": "airanpour",
    "branch": "اصفهان",
    "role": "کارشناس فنی",
    "created_at": "1404/08/03 12:45:21"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%B1%D9%85%D8%B2-%D8%B9%D8%A8%D9%88%D8%B1-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%A8%D8%A7-%D8%A7%D9%84%DA%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- رمز عبور باید با الگوریتم امن مثل bcrypt هش شود.
- تمام ورودی‌ها باید در سمت سرور اعتبارسنجی شوند.
- دسترسی ایجاد فقط برای نقش‌های Admin یا HR فعال باشد.

</div>### عملکرد

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AF%D8%B1%D8%AC-%D8%B3%D8%B1%DB%8C%D8%B9-%D8%A7%D8%B3%D8%AA-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات درج سریع است (کمتر از 50ms در DB با ایندکس بر روی username).
- نیاز به کوئری اضافی برای بررسی تکراری بودن username.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- Hash Facade

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%88%D8%B1%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>ورودی ناقص یا نامعتبر</td><td>Validation</td></tr><tr><td>409</td><td>نام کاربری تکراری</td><td>DB Unique Constraint</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-rate-limi" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- استفاده از rate limit برای جلوگیری از ثبت انبوه.
- گزارش همه عملیات ایجاد به سیستم لاگ مرکزی.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%D8%A2%D9%BE%D9%84%D9%88%D8%AF-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت آپلود عکس پروفایل در هنگام ایجاد.
- افزودن فیلد‌های سفارشی مانند وضعیت استخدام یا تاریخ شروع.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%A7%DB%8C%D8%AC%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت شناسه کاربر ایجادکننده و زمان دقیق ایجاد پرسنل.

</div>### جمع‌بندی

**personnelStore** نقطه ورود ایجاد پرسنل جدید است و باید با کنترل‌های امنیتی و اعتبارسنجی دقیق همراه باشد.

<div id="bkmrk-post-personnel-store" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# PUT /personnel

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">PUT</td><td dir="ltr">/personnel</td><td dir="ltr">OfficialController@personnelUpdate</td><td dir="ltr">authWithJwt</td><td dir="rtl">ویرایش اطلاعات پرسنل موجود (نام، نقش، شعبه، وضعیت و ...)</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `personnelUpdate` ابتدا بررسی می‌کند که کاربر جاری اجازه ویرایش دارد. سپس داده‌های جدید را از درخواست دریافت کرده، اعتبارسنجی می‌کند و رکورد مربوطه را در جدول `operators` یا جداول مرتبط به‌روزرسانی می‌نماید. در پایان، خروجی موفقیت‌آمیز برمی‌گرداند.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-jwt-%D9%88-%D9%86%D9%82%D8%B4" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی JWT و نقش کاربر.
2. بررسی وجود شناسه (`id`) پرسنل.
3. دریافت فیلدهای جدید مانند نام، نقش، شعبه، وضعیت.
4. اجرای کوئری `UPDATE` متناسب با فیلدهای ارسالی.
5. بازگرداندن پیام موفقیت همراه با زمان به‌روزرسانی.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه پرسنل هدف</td></tr><tr><td>name</td><td>Body</td><td>string</td><td>خیر</td><td>نام کامل جدید</td></tr><tr><td>username</td><td>Body</td><td>string</td><td>خیر</td><td>نام کاربری جدید</td></tr><tr><td>password</td><td>Body</td><td>string</td><td>خیر</td><td>رمز عبور جدید (هش می‌شود)</td></tr><tr><td>branch</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه شعبه جدید</td></tr><tr><td>role</td><td>Body</td><td>integer</td><td>خیر</td><td>شناسه نقش جدید</td></tr><tr><td>status</td><td>Body</td><td>integer</td><td>خیر</td><td>وضعیت فعال/غیرفعال</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732366220,
  "data": {
    "id": 152,
    "name": "علیرضا ایرانپور",
    "branch": "اصفهان",
    "role": "کارشناس فنی",
    "status": 1,
    "updated_at": "1404/08/03 13:02:31"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%D8%A8%D9%87-%D9%86%D9%82%D8%B4%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی محدود به نقش‌های Admin و HR.
- همه ورودی‌ها باید اعتبارسنجی و پاکسازی شوند.
- رمز عبور فقط در صورت ارسال هش می‌شود.

</div>### عملکرد

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%E2%80%8C%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%85" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- به‌روز‌رسانی رکورد معمولاً &lt; 50ms زمان می‌برد.
- در به‌روزرسانی‌های زیاد، استفاده از تراکنش DB توصیه می‌شود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- Hash Facade (در صورت تغییر رمز)

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%88%D8%B1%D9%88%D8%AF" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>ورودی ناقص یا نامعتبر</td><td>Validation</td></tr><tr><td>404</td><td>پرسنل یافت نشد</td><td>DB Lookup</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF-%D9%87%D9%85%D9%87-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-%D8%A8%D9%87-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ورود همه تغییرات به لاگ ممیزی.
- ارسال نوتیفیکیشن به کاربر در صورت تغییر رمز یا نقش.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-partial-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- پشتیبانی از Partial Update (PATCH) برای کاهش حجم داده.
- امکان تغییر هم‌زمان دسترسی‌ها.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%AE%D8%B5-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت شناسه شخص تغییر‌دهنده و تاریخ دقیق تغییر.

</div>### جمع‌بندی

**personnelUpdate** برای به‌روزرسانی امن و سریع اطلاعات پرسنل استفاده می‌شود و باید با کنترل دقیق مجوز اجرا گردد.

<div id="bkmrk-put-personnel-update" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel/shift/list

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel/shift/list</td><td dir="ltr">OfficialController@shiftWorkList</td><td dir="ltr">authWithJwt</td><td dir="rtl">نمایش لیست همه شیفت‌های موجود و تعریف‌شده سیستم</td></tr></tbody></table>

</div>### منطق عملکرد تابع

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

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-get-%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت درخواست GET از کلاینت با توکن معتبر JWT.
2. اجرای کوئری روی جدول `shift_works` یا مدل مشابه برای استخراج لیست شیفت‌ها.
3. مرتب‌سازی خروجی بر اساس شماره یا عنوان شیفت.
4. بازگشت پاسخ JSON شامل آرایه‌ای از شیفت‌ها.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>search</td><td>Query String</td><td>string</td><td>خیر</td><td>عبارت فیلتر عنوان یا توضیح شیفت</td></tr><tr><td>status</td><td>Query String</td><td>integer</td><td>خیر</td><td>۱ برای فعال، ۰ برای غیرفعال</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732369262,
  "data": [
    {
      "id": 1,
      "title": "شیفت صبح",
      "start": "07:00",
      "end": "15:00",
      "status": 1,
      "description": "کار اداری صبح",
      "created_at": "1404/03/12 10:11:22"
    },
    {
      "id": 2,
      "title": "شیفت عصر",
      "start": "15:00",
      "end": "23:00",
      "status": 1,
      "description": "شیفت عصرگاهی"
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- نیاز به احراز هویت از طریق JWT دارد.
- صرفاً داده‌های مجاز طبق نقش کاربر بازگردانده می‌شوند.

</div>### عملکرد

<div id="bkmrk-%D9%85%DB%8C%D8%A7%D9%86%DA%AF%DB%8C%D9%86-%D8%B2%D9%85%D8%A7%D9%86-%D9%BE%D8%A7%D8%B3%D8%AE%E2%80%8C%DA%AF%D9%88" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- میانگین زمان پاسخ‌گویی کمتر از 25ms در کوئری لوکال.
- در تولید زیاد داده پیشنهاد می‌شود از cache layer با TTL=3600 استفاده شود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-401-%D8%AA%D9%88%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>401</td><td>توکن نامعتبر یا منقضی‌شده</td><td>Middleware</td></tr><tr><td>404</td><td>هیچ شیفتی یافت نشد</td><td>DB Query</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%DA%A9%D8%B1%D8%AF%D9%86-%D9%86%D8%B1%D8%AE-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- محدود کردن نرخ درخواست (Rate Limit = 30 req/min).
- ثبت لاگ درخواست‌ها برای ممیزی تغییرات سیستم شیفت.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-%D9%81%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پارامترهای فیلتر زمانی (startFrom / endTo).
- اضافه‌کردن خروجی خلاصه برای کارایی بالاتر در فهرست‌های UI.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%84%D8%A7%DA%AF-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%AF%DB%8C%D8%B1%D8%A7%D9%86-%D8%A8%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- لاگ دسترسی مدیران با شناسه کاربری، زمان و IP ثبت شود.

</div>### جمع‌بندی

**shiftWorkList** نقطهٔ ورودی دریافت لیست کامل شیفت‌هاست. با بازگرداندن جزئیات دقیق زمان‌ها، این مسیر پایهٔ مدیریت و تنظیم شیفت‌های پرسنل در سامانه است.

<div id="bkmrk-get-personnel-shift-list" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# GET /personnel/shift

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">GET</td><td dir="ltr">/personnel/shift</td><td dir="ltr">OfficialController@shiftWorkIndex</td><td dir="ltr">authWithJwt</td><td dir="rtl">نمایش لیست و جزئیات شیفت‌های فعال به همراه پرسنل مربوطه</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `shiftWorkIndex` برای واکشی نمای کلی از شیفت‌های در حال اجرا طراحی شده است. داده‌های جدول شیفت (مثلاً `shift_works` یا مشابه) را دریافت کرده، سپس نام پرسنل منتسب، زمان‌بندی شروع و پایان، نوع شیفت، و وضعیت فعال یا غیرفعال را بازمی‌گرداند.

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%AA%D9%88%DA%A9%D9%86-jwt-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. اعتبارسنجی توکن JWT و تشخیص نقش کاربر.
2. واخوانی داده‌ها از جدول شیفت‌ها با روابط مربوط به پرسنل (LEFT JOIN).
3. در صورت ارسال شناسهٔ خاص، فقط شیفت موردنظر بازگردانده می‌شود.
4. بازگشت پاسخ JSON شامل لیست شیفت‌ها یا ویژگی‌های یک شیفت خاص.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Query String</td><td>integer</td><td>خیر</td><td>در صورت وجود، فقط شیفت موردنظر بازگردانده می‌شود</td></tr><tr><td>status</td><td>Query String</td><td>integer</td><td>خیر</td><td>فیلتر بر اساس وضعیت فعال (۱) یا غیرفعال (۰)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732369405,
  "data": [
    {
      "id": 12,
      "title": "شیفت صبح کارکنان بخش رزرو",
      "start_time": "07:00",
      "end_time": "15:00",
      "personnel": [
        {"id": 88, "name": "رضا قنبری"},
        {"id": 95, "name": "الهام رضایی"}
      ],
      "status": 1,
      "created_at": "1404/08/01 09:12:13"
    },
    {
      "id": 13,
      "title": "شیفت عصر سیستم فروش",
      "start_time": "15:00",
      "end_time": "23:00",
      "personnel": [],
      "status": 0
    }
  ]
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%A7%D8%B2-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D8%A7%D8%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- از احراز هویت JWT استفاده می‌شود.
- فقط نقش‌های Admin و HR اجازه مشاهده کل داده را دارند؛ سایر نقش‌ها فقط شیفت خود را می‌بینند.

</div>### عملکرد

<div id="bkmrk-%D8%B2%D9%85%D8%A7%D9%86-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%AF%D8%B1-%D8%AF%DB%8C%D8%AA%D8%A7%D8%B3%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- زمان واکشی در دیتاست متوسط کمتر از 40ms.
- قابلیت کش‌کردن خروجی تا ۱۰ دقیقه برای کاهش بار پایگاه‌داده.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر جستجوی نامعتبر</td><td>Validation</td></tr><tr><td>404</td><td>شیفت مورد نظر یافت نشد</td><td>DB Lookup</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%B3%D8%B7%D8%AD-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%A8%D8%B1-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- کنترل سطح دسترسی بر اساس نقش کاربر در JWT.
- غیرفعال کردن endpoint در زمان تعمیرات پایگاه‌داده یا انتقال داده‌ها.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%28" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن فیلتر تاریخ (از – تا) برای دیدن شیفت‌های گذشته.
- امکان جستجوی پرسنل در شیفت‌ها.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%DA%A9%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت کاربر درخواست‌کننده و تاریخ بازدید در سیستم ممیزی برای تحلیل ترافیک مدیریتی.

</div>### جمع‌بندی

**shiftWorkIndex** برای نمایش وضعیت فعلی شیفت‌ها در سیستم طراحی شده و پایهٔ داشبورد مدیریتی بخش پرسنل محسوب می‌شود.

<div id="bkmrk-get-personnel-shift" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /personnel/shift

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">POST</td><td dir="ltr">/personnel/shift</td><td dir="ltr">OfficialController@shiftWorkStore</td><td dir="ltr">authWithJwt</td><td dir="rtl">ایجاد و ثبت شیفت کاری جدید برای پرسنل</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `shiftWorkStore`‌ مسئول درج رکورد جدید شیفت در پایگاه‌داده است. پس از اعتبارسنجی مقادیر ورودی و بررسی تداخل زمانی با شیفت‌های قبلی، اطلاعات ثبت و پاسخ JSON بازگردانده می‌شود.

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B5%D8%AD%D8%AA-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی صحت احراز هویت JWT و نقش کاربر (Admin یا HR).
2. اعتبارسنجی وجود پارامترهای ضروری (عنوان، زمان شروع، زمان پایان).
3. اطمینان از نبود تداخل زمانی با سایر شیفت‌ها.
4. ایجاد رکورد جدید در جدول `shift_works` با وضعیت فعال.
5. برگرداندن پاسخ شامل شناسه و جزئیات شیفت تازه‌ثبت‌شده.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>title</td><td>Body</td><td>string</td><td>بله</td><td>عنوان شیفت (مثلاً "شیفت صبح")</td></tr><tr><td>start</td><td>Body</td><td>string</td><td>بله</td><td>ساعت شروع کار (در قالب HH:mm)</td></tr><tr><td>end</td><td>Body</td><td>string</td><td>بله</td><td>ساعت پایان کار (در قالب HH:mm)</td></tr><tr><td>status</td><td>Body</td><td>integer</td><td>خیر</td><td>۱ برای فعال، ۰ برای غیرفعال</td></tr><tr><td>description</td><td>Body</td><td>string</td><td>خیر</td><td>توضیح اختیاری درباره شیفت</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732369602,
  "data": {
    "id": 24,
    "title": "شیفت عصر سیستم فروش",
    "start": "15:00",
    "end": "23:00",
    "status": 1,
    "description": "شیفت عصر اینترنال تیم فروش",
    "created_at": "1404/08/03 14:05:21"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%AA%D9%86%D9%87%D8%A7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی تنها برای کاربران با سطح HR یا Admin مجاز است.
- تلاش برای تعریف شیفت تکراری در همان ساعت باید با خطای Validation رد شود.

</div>### عملکرد

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AF%D8%B1%D8%AC-%DA%A9%D9%85%D8%AA%D8%B1-%D8%A7%D8%B2-%DB%B3" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات درج کمتر از ۳۰ms زمان دارد (در داده‌های لوکال).
- ایندکس بر فیلدهای `start` و `end` جهت اجتناب از تداخل زمانی پیشنهاد می‌شود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر ورودی ناقص یا نامعتبر</td><td>Validation</td></tr><tr><td>409</td><td>تداخل زمانی با شیفت دیگر</td><td>Business Logic</td></tr><tr><td>500</td><td>خطای عمومی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%B4%DB%8C%D9%81%D8%AA-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ ایجاد شیفت با نام مدیر و IP درخواست‌دهنده.
- ضبط تاریخچه تغییر در جدول `shift_logs`.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%B4%D8%AA%DB%8C%D8%A8%D8%A7%D9%86%DB%8C-%D8%A7%D8%B2-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن پشتیبانی از بازه‌های چندروزه (شیفت‌های بلند).
- تعیین رنگ یا برچسب مشخص برای هر شیفت جهت نمایش در UI.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D9%87%D9%85%D9%87-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-s" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- همه عملیات در جدول `system_audit` ثبت می‌شود (action: 'shift:add').

</div>### جمع‌بندی

**shiftWorkStore** هسته فرآیند ایجاد شیفت کاری است. این مسیر توسط تیم منابع انسانی برای تعریف ساختار کاری واحدها استفاده می‌شود و جزئیات ضروری هر شیفت را ذخیره می‌کند.

<div id="bkmrk-post-personnel-shift" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# PUT /personnel/shift

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">PUT</td><td dir="ltr">/personnel/shift</td><td dir="ltr">OfficialController@shiftWorkUpdate</td><td dir="ltr">authWithJwt</td><td dir="rtl">ویرایش اطلاعات یک شیفت کاری موجود</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `shiftWorkUpdate` برای به‌روزرسانی داده‌های شیفت قبلی طراحی شده است. این تابع بررسی می‌کند که شیفت با `id` ارسالی وجود دارد و سپس اطلاعات جدید را در همان رکورد ذخیره می‌کند.

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%88%D8%AC%D9%88%D8%AF-%D9%85%D8%AC%D9%88%D8%B2-%D8%AF%D8%B3%D8%AA%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. بررسی وجود مجوز دسترسی (Admin / HR).
2. یافتن رکورد شیفت بر اساس شناسه ورودی.
3. اعتبارسنجی ورودی‌ها و کنترل تداخل زمانی احتمالی با سایر شیفت‌ها.
4. اعمال تغییرات در دیتابیس و تولید پاسخ استاندارد JSON.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه شیفت موردنظر برای ویرایش</td></tr><tr><td>title</td><td>Body</td><td>string</td><td>خیر</td><td>عنوان جدید برای شیفت</td></tr><tr><td>start</td><td>Body</td><td>string</td><td>خیر</td><td>ساعت شروع جدید (HH:mm)</td></tr><tr><td>end</td><td>Body</td><td>string</td><td>خیر</td><td>ساعت پایان جدید (HH:mm)</td></tr><tr><td>status</td><td>Body</td><td>integer</td><td>خیر</td><td>۱ برای فعال، ۰ برای غیرفعال</td></tr><tr><td>description</td><td>Body</td><td>string</td><td>خیر</td><td>توضیح اختیاری</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732369760,
  "data": {
    "id": 24,
    "title": "شیفت عصر سیستم فروش",
    "start": "16:00",
    "end": "23:00",
    "status": 1,
    "description": "تغییر ساعت شروع به ۱۶",
    "updated_at": "1404/08/03 15:21:43"
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AA%D9%86%D9%87%D8%A7-hr-%D9%88-admin-%D9%85%D8%AC%D8%A7%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- تنها HR و Admin مجاز به ویرایش شیفت هستند.
- در صورت تداخل زمانی، بروزرسانی انجام نمی‌شود و خروجی ۴۰۹ بازگردانده می‌شود.

</div>### عملکرد

<div id="bkmrk-%D8%B2%D9%85%D8%A7%D9%86-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%A8%D9%87%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- زمان اجرای کوئری به‌روزرسانی در حدود ۴۰ms است.
- بهره‌گیری از ایندکس روی فیلدهای `start` و `end` توصیه می‌شود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر الزامی ارسال نشده</td><td>Validation</td></tr><tr><td>404</td><td>شیفت یافت نشد</td><td>Database</td></tr><tr><td>409</td><td>تداخل زمانی با شیفت دیگر</td><td>Logic Checker</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%D9%88-ip-%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ کاربر و IP برای بروزرسانی‌های شیفت.
- نمایش هشدار تغییر در رابط کاربری به مدیر مسئول.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-%22%D9%86%D8%B3%D8%AE%D9%87%E2%80%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن قابلیت "نسخه‌سازی" تغییرات شیفت برای بازگشت به حالت قبلی.
- افزودن اعتبارسنجی خودکار نسبت به بازه‌های کاری قبلی پرسنل.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-system_audit" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در جدول `system_audit`، عملیات ویرایش با event='shift:update' ثبت شود.

</div>### جمع‌بندی

**shiftWorkUpdate** مسیر استاندارد به‌روزرسانی شیفت‌هاست و دقت بالایی در کنترل تداخل و اعتبار داده‌ها دارد. این متد هسته مدیریت اصلاحات شیفت در ماژول منابع انسانی است.

<div id="bkmrk-put-personnel-shift" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# DELETE /personnel/shift

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">DELETE</td><td dir="ltr">/personnel/shift</td><td dir="ltr">OfficialController@shiftWorkDestroy</td><td dir="ltr">authWithJwt</td><td dir="rtl">حذف (یا غیرفعال‌سازی) یک شیفت کاری موجود</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `shiftWorkDestroy`‌ بسته به نوع درخواست دریافتی، یا شیفت را به‌صورت کامل از جدول حذف می‌کند یا با تغییر وضعیت آن به حالت غیرفعال، حذف منطقی انجام می‌دهد. این کنترل ضمن بررسی نقش کاربر، اطمینان حاصل می‌کند که شیفت درحال استفاده (مثلاً تخصیص‌داده‌شده به پرسنل) قابل حذف نیست.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%DB%8C%D9%81%D8%AA-%D8%A7%D8%B2" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. دریافت شناسه شیفت از پارامتر ورودی.
2. بررسی وجود شیفت در دیتابیس.
3. در صورت فعال بودن پرچم `force` → حذف فیزیکی رکورد.
4. در غیر این صورت → تغییر فیلد `status` به ۰ (غیرفعال).
5. بازگرداندن پاسخ JSON بر اساس نتیجه عملیات.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>id</td><td>Body</td><td>integer</td><td>بله</td><td>شناسه شیفت قابل حذف</td></tr><tr><td>force</td><td>Body</td><td>boolean</td><td>خیر</td><td>در صورت true، رکورد به‌صورت دائمی حذف می‌شود</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732369925,
  "data": {
    "id": 24,
    "deleted_type": "soft",
    "message": "شیفت با موفقیت غیرفعال شد."
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای کاربران دارای نقش HR یا Admin مجاز است.
- در صورت ارتباط شیفت با رکوردهای دیگر (مثلاً time‑table پرسنل)، حذف مسدود می‌شود.

</div>### عملکرد

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AD%D8%B0%D9%81-%D9%85%D9%86%D8%B7%D9%82%DB%8C-%D8%B2%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- عملیات حذف منطقی زیر ۲۰ms (در حذف نرم) و حذف فیزیکی ~۳۰ms زمان می‌برد.
- Transaction Database برای اطمینان از Atomic بودن به‌کار می‌رود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- App\\Models\\ShiftWork

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-404-%D8%B4%DB%8C%D9%81%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>404</td><td>شیفت یافت نشد</td><td>Database</td></tr><tr><td>409</td><td>شیفت دارای وابستگی است (قابل حذف نیست)</td><td>Business Rule</td></tr><tr><td>500</td><td>خطای داخلی سرور</td><td>Exception Handler</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%D9%84%D8%A7%DA%AF-%D9%86%D9%88%D8%B9-%D8%AD%D8%B0%D9%81-%D9%88-%D8%B4%D9%86" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت لاگ نوع حذف و شناسه کاربر حذف‌کننده در `shift_audit_log`.
- در حذف دائمی، ارسال هشدار تأیید دوم در رابط کاربری.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-endpoint-%D8%A8%D8%B1%D8%A7%DB%8C" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن Endpoint برای بازیابی شیفت‌های حذف‌شده (Recycle Bin).
- بازطراحی حذف به‌صورت asynchronous با صف پردازش در Redis Queue.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-system_audit" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- در جدول `system_audit` ثبت می‌شود با event='shift:delete'.
- فیلدهای کلیدی: user\_id, shift\_id, delete\_type, ip

</div>### جمع‌بندی

**shiftWorkDestroy** مسیر رسمی حذف شیفت است که قابلیت حذف منطقی و دائمی را فراهم می‌کند. حذف ایمن با کنترل وابستگی، و ورود لاگ کامل در سامانه حسابرسی از ویژگی‌های کلیدی آن محسوب می‌شود.

<div id="bkmrk-delete-personnel-shift" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# * PATCH /personnel/shift/reset

<div id="bkmrk-" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 96%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td><td>Purpose</td></tr><tr><td dir="ltr">PATCH</td><td dir="ltr">/personnel/shift/reset</td><td dir="ltr">OfficialController@shiftWorkReset</td><td dir="ltr">authWithJwt</td><td dir="rtl">بازنشانی کلیه‌ی شیفت‌ها به وضعیت اولیه</td></tr></tbody></table>

</div>### منطق عملکرد تابع

تابع `shiftWorkReset` کلیه رکوردهای شیفت را از نظر وضعیت، زمان‌بندی و تخصیص‌ها بازنشانی می‌کند. معمولاً جهت پاکسازی داده‌های تستی یا آماده‌سازی دوره جدید کاری به‌کار می‌رود.

<div id="bkmrk-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-jwt-%D9%88-%D8%A8%D8%B1%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">1. احراز هویت JWT و بررسی نقش HR یا Admin.
2. ثبت عملیات در جدول ممیزی (system\_audit).
3. به‌روزرسانی فیلدهای `status`، `start`، `end` و برگرداندن به مقادیر پیش‌فرض.
4. در صورت وجود وابستگی (مانند حضور کارمندان)، ابتدا درج لاگ هشدار در audit.
5. بازگرداندن نتیجه عملیات همراه با تعداد رکوردهای تغییر یافته.

</div>### پارامترهای ورودی

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%85%D8%AD%D9%84-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 95%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>نام</td><td>محل</td><td>نوع</td><td>الزامی</td><td>توضیح</td></tr><tr><td>scope</td><td>Body</td><td>string</td><td>خیر</td><td>محدوده بازنشانی (مثلاً `all` یا `current_month`)</td></tr><tr><td>confirm</td><td>Body</td><td>boolean</td><td>بله</td><td>تأیید نهایی برای انجام بازنشانی (پیش‌فرض false)</td></tr></tbody></table>

</div>### ساختار خروجی

```
{
  "status": true,
  "time": 1732370189,
  "data": {
    "reset_scope": "all",
    "affected_rows": 46,
    "message": "شیفت‌ها با موفقیت به وضعیت اولیه بازگردانده شدند."
  }
}
```

<div id="bkmrk--1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>### نکات امنیتی

<div id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%AF%DB%8C%D8%B1" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- دسترسی فقط برای مدیران HR سطح بالا مجاز است.
- در صورت عدم مقداردهی پارامتر `confirm=true`، هیچ تغییری اعمال نمی‌شود.
- تمامی عملیات reset در لاگ ممیزی ثبت می‌شود.

</div>### عملکرد

<div id="bkmrk-%D8%A8%D8%A7%D8%B2%D9%86%D8%B4%D8%A7%D9%86%DB%8C-%D8%AD%D8%AF%D9%88%D8%AF-%DB%B2%DB%B0%DB%B0-%D8%B1%DA%A9" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- بازنشانی حدود ۲۰۰ رکورد در کمتر از ۶۰ms انجام می‌شود.
- transaction-level lock جهت جلوگیری از تداخل بین درخواست‌های هم‌زمان به‌کار می‌رود.

</div>### Dependencies

<div id="bkmrk-illuminate%5Csupport%5Cf" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- Illuminate\\Support\\Facades\\DB
- Carbon\\Carbon
- App\\Models\\ShiftWork

</div>### کدهای خطا

<div id="bkmrk-%DA%A9%D8%AF-%D8%B4%D8%B1%D8%AD-%D9%85%D9%86%D8%A8%D8%B9-400-%D9%BE%D8%A7%D8%B1%D8%A7" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"><table border="1" cellpadding="6" style="border-collapse: collapse; width: 90%; margin: 15px auto; text-align: center;"><tbody><tr style="background: #f9f9f9; font-weight: bold;"><td>کد</td><td>شرح</td><td>منبع</td></tr><tr><td>400</td><td>پارامتر تأیید (`confirm`) ارسال نشده</td><td>Validation</td></tr><tr><td>403</td><td>دسترسی غیرمجاز برای بازنشانی</td><td>Middleware</td></tr><tr><td>500</td><td>خطای داخلی پایگاه داده</td><td>Server Error</td></tr></tbody></table>

</div>### پیشنهادهای امنیتی

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D8%AA%D8%A3%DB%8C%DB%8C%D8%AF-%D8%AF%D9%88-%D9%85%D8%B1%D8%AD%D9%84" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن تأیید دو مرحله‌ای (مثلاً OTP مدیریتی پیش از اجرای reset).
- محدودسازی درخواست به شبکه داخلی سازمانی.

</div>### پیشنهادهای توسعه‌ای

<div id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DA%AF%D8%B2%DB%8C%D9%86%D9%87-reset-s" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- افزودن گزینه reset selective برای انتخاب بخشی از شیفت‌ها.
- به‌کارگیری Job Queue برای بازنشانی حجیم در پس‌زمینه.

</div>### ممیزی و ثبت وقایع

<div id="bkmrk-%D8%AB%D8%A8%D8%AA-%DA%A9%D8%A7%D9%85%D9%84-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AF%D8%B1-s" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;">- ثبت کامل عملیات در `system_audit` با event='shift:reset'.
- شامل: شناسه کاربر، بازه‌ی بازنشانی، IP، timestamp.

</div>### جمع‌بندی

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

<div id="bkmrk-patch-personnel-shift-reset" style="font-family: Vazir, Tahoma; direction: rtl; text-align: justify; line-height: 1.9;"></div>

# POST /api/v2/tasks/category/operation

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/api/v2/tasks/category/operation</td><td style="direction: ltr; padding: 10px;">OfficialController@operationTasksCategory</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد مدیریت کامل (CRUD) دسته‌بندی‌های تسک را بر عهده دارد و بر اساس پارامتر `action` رفتار متفاوتی نشان می‌دهد:

<div id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-store-%28%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF%29" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **عملیات Store (ایجاد):**
    - یک رکورد جدید در جدول `tasks_categories` ایجاد می‌کند.
    - فیلد `user` با شناسه اپراتور جاری و فیلد `branch` با شناسه شعبه جاری پر می‌شود.
    - اطلاعات ظاهری (عنوان، رنگ، آیکون) از آرایه `data` دریافت می‌شود.
- **عملیات Update (ویرایش):**
    - رکورد مربوط به `id` ارسالی را در جدول `tasks_categories` پیدا کرده و فیلدهای `title`، `color` و `icon` را به‌روزرسانی می‌کند.
- **عملیات Delete (حذف کامل و آبشاری):**
    - ابتدا خود دسته‌بندی را از جدول `tasks_categories` حذف می‌کند.
    - سپس تمام تسک‌های زیرمجموعه این دسته‌بندی را از جدول `tasks_items` حذف می‌کند.
    - در نهایت، تمامی یادداشت‌ها (Notes) مرتبط با آن تسک‌های حذف شده را نیز از جدول `tasks_notes` پاک می‌کند تا دیتای یتیم (Orphan Data) باقی نماند.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">action</td><td>String</td><td>بله</td><td>نوع عملیات: `store`, `update`, `delete`.</td></tr><tr><td style="direction: ltr;">id</td><td>Integer</td><td>شرطی</td><td>برای `update` و `delete` الزامی است.</td></tr><tr><td style="direction: ltr;">data</td><td>Array</td><td>شرطی</td><td>برای `store` و `update` الزامی است. حاوی اطلاعات دسته‌بندی.</td></tr></tbody></table>

</div></div>#### ساختار آرایه `data`:

```
{
    "title": "عنوان دسته‌بندی",
    "color": "#FF0000",  // کد رنگ HEX
    "icon": "fa-list"    // کلاس آیکون (مثلاً FontAwesome)
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی (Response)

در صورت موفقیت عملیات:

```
{
    "status": true,
    "time": 1732615200
}
```

در صورت بروز خطا (Exception):

```
{
    "status": false,
    "time": 1732615200,
    "message": "Error message description",
    "trace": [ ... ]
}
```

# GET /api/v2/tasks/categories/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/tasks/categories/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listTasksCategories</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست دسته‌بندی‌های تسک را در دو گروه مجزا ("my" و "other") واکشی می‌کند. منطق تفکیک و پردازش داده‌ها به شرح زیر است:

<div id="bkmrk-%D8%A8%D8%AE%D8%B4-%22my-categories%22-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **بخش "My Categories" (دسته‌بندی‌های من):**
    - تمامی رکوردهایی از جدول `tasks_categories` انتخاب می‌شوند که فیلد `user` آن‌ها برابر با شناسه اپراتور جاری باشد.
    - تعداد تسک‌های فعال (status=1) در هر دسته‌بندی شمارش می‌شود.
    - اگر پارامتر `tasks` در درخواست ارسال شده باشد، متد `getTasks` با آرگومان `own` فراخوانی شده و لیست تسک‌های زیرمجموعه نیز بارگذاری می‌شود.
- **بخش "Other Categories" (سایر دسته‌بندی‌های مرتبط):**
    - دسته‌بندی‌هایی را پیدا می‌کند که اپراتور جاری سازنده آن‌ها نیست، اما در تسک‌های زیرمجموعه آن‌ها (`tasks_items`) به عنوان مسئول (Operator) تعیین شده است.
    - از دستور `JSON_CONTAINS` روی فیلد `operators` در جدول `tasks_items` برای یافتن مشارکت اپراتور استفاده می‌شود.
    - اگر پارامتر `tasks` ارسال شود، متد `getTasks` با آرگومان `other` فراخوانی می‌شود تا فقط تسک‌های مرتبط با کاربر را برگرداند.
- **Hydration داده‌ها (User &amp; Operators):**
    - برای نمایش اطلاعات کاربر سازنده (`user`)، از متد استاتیک `StaticController::getOperators` استفاده می‌شود. از آنجا که این متد آرایه برمی‌گرداند، در خروجی این کنترلر مستقیماً ایندکس `[0]` آن استخراج و در فیلد `user` قرار می‌گیرد.
    - در بخش تسک‌ها (اگر لود شوند)، فیلد `operators` حاوی آرایه‌ای کامل از خروجی `getOperators` است.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">branch</td><td>Integer</td><td>بله</td><td>شناسه شعبه فعال (معمولاً از طریق میدل‌ور تزریق می‌شود).</td></tr><tr><td style="direction: ltr;">tasks</td><td>Boolean/Int</td><td>خیر</td><td>اگر مقدار داشته باشد (مثلاً 1)، لیست تسک‌های هر دسته‌بندی نیز در خروجی (`tasks`) بارگذاری می‌شود (Eager Loading دستی).</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

**نکته مهم در مورد ساختار داده‌ها:** فیلد `user` در آبجکت دسته‌بندی، یک آبجکت تکی است (چون ایندکس 0 دریافت می‌شود)، اما فیلد `operators` در داخل تسک‌ها، یک آرایه از آبجکت‌هاست.

```
{
    "status": true,
    "time": 1732615000,
    "data": {
        "my": [
            {
                "id": 5,
                "user": {
                    "id": 10,
                    "text": "1001 - نام نام‌خانوادگی",
                    "query": {
                        "id": 10,
                        "sex": "male",
                        "first_name": "نام",
                        "last_name": "نام‌خانوادگی",
                        "telegram": "username",
                        "group": "support",
                        "avatar": "path/to/img.jpg",
                        "position": "manager",
                        "personnel_id": 1001
                    }
                },
                "title": "توسعه فنی",
                "color": "#FF5733",
                "icon": "fa-code",
                "status": 1,
                "created": "2024-01-01T12:00:00.000000Z",
                "counter": 3,
                "tasks": [ // فقط اگر tasks=1 ارسال شده باشد پر می‌شود
                    {
                        "id": 101,
                        "user": { ... }, // آبجکت سازنده تسک (مشابه ساختار user بالا)
                        "category": {
                            "id": 5,
                            "title": "توسعه فنی"
                        },
                        "title": "بررسی باگ لاگین",
                        "connection": false, // یا آبجکت کانکشن اگر وجود داشته باشد
                        "priority": 1,
                        "order": 0,
                        "description": "توضیحات تسک...",
                        "deadline": "1403-10-20",
                        "operators": [ // آرایه اپراتورهای مسئول
                             {
                                "id": 12,
                                "text": "1002 - همکار فنی",
                                "query": { ... }
                             }
                        ],
                        "status": 1,
                        "created": "2024-11-01T10:00:00.000000Z"
                    }
                ]
            }
        ],
        "other": [
            {
                "id": 8,
                "user": { ... }, // کاربری که دسته‌بندی را ساخته (نه کاربر جاری)
                "title": "امور مالی",
                "color": "#33FF57",
                "icon": "fa-money",
                "status": 1,
                "created": "2024-02-15T14:30:00.000000Z",
                "counter": 1, // تعداد تسک‌هایی که من در آن‌ها مشارکت دارم
                "tasks": false // اگر tasks ارسال نشود، false برمی‌گردد
            }
        ]
    }
}
```

# GET /api/v2/tasks/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/tasks/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listTasks</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div>### معماری و منطق پردازش (Processing Logic &amp; Architecture)

این متد لیست وظایف را با مکانیزم **Lazy Loading دستی** آماده‌سازی می‌کند. پس از دریافت رکوردهای خام از جدول `tasks_items`، سیستم طی یک فرآیند Post-Processing روی هر آیتم حلقه زده و توابع Helper استاتیک را فراخوانی می‌کند:

<div id="bkmrk-operator-resolution-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. **Operator Resolution (متد `StaticController::getOperators`):**شناسه‌های کاربران (چه سازنده و چه اپراتورهای مسئول) به این تابع ارسال می‌شوند. تابع ابتدا ورودی را نرمال‌سازی کرده (تبدیل رشته JSON یا عدد به آرایه)، سپس با کوئری `whereIn` از جدول `operators` اطلاعات را واکشی می‌کند. خروجی شامل یک رشته فرمت‌شده (`text`) برای نمایش سریع و یک آبجکت `query` حاوی تمام فیلدهای هویتی و سازمانی کاربر است.
2. **Connection Hydration (متد `OfficialController::getConnection`):**اگر تسک به یک موجودیت خارجی (مثل رفرنس فروش) متصل باشد، این تابع اجرا می‌شود. برای نوع `reference`، سیستم ابتدا سعی می‌کند اطلاعات مالی را از **Redis** بخواند. در صورت عدم وجود در کش (Cache Miss)، متد `TradeController::financial` اجرا شده و نتیجه در Redis کش می‌شود تا سربار محاسبات مالی در فراخوانی‌های بعدی کاهش یابد.

</div>### ساختار پاسخ (Response Structure)

خروجی زیر دقیقاً بر اساس توابع `getOperators` و `getConnection` که ارائه کردید بازسازی شده است:

```
{
  "status": true,
  "time": 1732047000,
  "data": [
    {
      "id": 105,
      "title": "پیگیری رفرنس ۱۲۵۰",
      "category": {
        "id": 12,
        "title": "مالی و حسابداری"
      },
      
      // خروجی دقیق getOperators برای فیلد user (سازنده)
      "user": [
        {
          "id": 50,
          "text": "10050 - علی محمدی",
          "query": {
            "id": 50,
            "sex": "male",
            "first_name": "علی",
            "last_name": "محمدی",
            "telegram": "ali_dev",
            "group": "IT",
            "avatar": "path/to/avatar.jpg",
            "position": "مدیر فنی",
            "personnel_id": "10050"
          }
        }
      ],

      // خروجی دقیق getOperators برای فیلد operators (مسئولین انجام)
      "operators": [
        {
          "id": 51,
          "text": "10051 - رضا علوی",
          "query": {
            "id": 51,
            "sex": "male",
            "first_name": "رضا",
            "last_name": "علوی",
            "telegram": "reza_acc",
            "group": "Accounting",
            "avatar": null,
            "position": "حسابدار ارشد",
            "personnel_id": "10051"
          }
        }
      ],

      // خروجی دقیق getConnection (در صورتی که تسک به رفرنس متصل باشد)
      "connection": [
        {
          "id": 2050,
          "type": "reference",
          "title": "تور کیش - هتل داریوش", // خوانده شده از Redis
          "financial": {
             // آبجکت بازگشتی از TradeController::financial
             "total_price": 50000000,
             "paid": 20000000,
             "remaining": 30000000,
             "status": "debtor"
          }
        }
      ],

      "priority": 1,
      "order": 0,
      "description": "لطفا مانده حساب این رفرنس با مشتری چک شود.",
      "deadline": "2025-02-20",
      "status": 1,
      "created": "2025-02-18 10:30:00"
    }
  ]
}
```

<div id="bkmrk-route-info-tasks-list" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /api/v2/tasks/task/operation

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/api/v2/tasks/task/operation</td><td style="direction: ltr; padding: 10px;">OfficialController@operationTask</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این متد یک رابط چندمنظوره برای مدیریت چرخه حیات تسک‌ها است که بر اساس پارامتر `action` عملیات‌های مختلفی را روی دیتابیس اجرا می‌کند:

<div id="bkmrk-store-%28%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%AA%D8%B3%DA%A9%29%3A-%D8%B4" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **store (ایجاد تسک):**
    - شناسایی اپراتورهای مسئول و ارسال نوتیفیکیشن تلگرام جهت اطلاع‌رسانی.
    - ذخیره اطلاعات اصلی تسک در جدول `tasks_items`.
    - در صورت وجود چک‌لیست، آیتم‌های آن به صورت مجزا در جدول `tasks_item_checklist` ثبت می‌شوند.
- **store\_note (ثبت یادداشت):**
    - ذخیره یادداشت جدید در جدول `tasks_notes` متصل به تسک.
    - ارسال اعلان به تمامی افراد درگیر در تسک (شامل سازنده و مسئولین) به جز شخصی که یادداشت را ثبت کرده است.
- **update (ویرایش تسک):**
    - بروزرسانی فیلدهای اطلاعاتی تسک و ارسال اعلان تغییرات به کاربران مرتبط.
    - **مدیریت چک‌لیست:**
        - آیتم‌های جدید (بدون شناسه) به لیست اضافه می‌شوند.
        - آیتم‌های موجود (با شناسه)، وضعیت انجام کار (`done_at` و `done_by`) آن‌ها بروزرسانی می‌شود.
    - حذف آیتم‌های چک‌لیست مشخص شده در آرایه `deleted_checklist`.
- **update\_order (تغییر اولویت نمایش):**
    - دریافت لیستی از تسک‌ها و بروزرسانی ترتیب (Order) آن‌ها در دیتابیس جهت نمایش مرتب‌سازی شده.
- **update\_status (تغییر وضعیت):**
    - تغییر وضعیت تسک (به عنوان مثال: انجام شده/در حال انجام) و اطلاع‌رسانی به ذینفعان.
    - بازگرداندن تعداد به‌روز شده‌ی تسک‌های فعال کاربر جهت بروزرسانی UI.
- **delete (حذف):**
    - حذف فیزیکی رکورد تسک و تمامی یادداشت‌های وابسته به آن از سیستم.

</div>### پارامترهای ورودی (Body Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">action</td><td>String</td><td>بله</td><td>تعیین نوع عملیات: `store`, `update`, `delete`, `store_note`, `update_order`, `update_status`</td></tr><tr><td style="direction: ltr;">id</td><td>Integer</td><td>شرطی</td><td>شناسه تسک (در متدهای ویرایشی و حذفی الزامی است).</td></tr><tr><td style="direction: ltr;">data</td><td>Array</td><td>بله</td><td>حاوی جزئیات عملیات (طبق ساختار زیر).</td></tr></tbody></table>

</div>#### ساختار آبجکت `data`:

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D9%88-%D9%88%DB%8C%D8%B1%D8%A7%DB%8C%D8%B4-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **برای ایجاد و ویرایش (store/update):**```
    {
      "category": int,
      "title": string,
      "priority": int,
      "description": string,
      "deadline": string (nullable),
      "operators": [int, int], // لیست ID مسئولین
      "checklist": [
          { "id": int (optional), "content": string, "done": boolean }
      ]
    }
            
    ```
- **برای یادداشت (store\_note):** `{ "note": "متن یادداشت" }`
- **برای وضعیت (update\_status):** `{ "status": int }`

</div>### نمونه خروجی (Response)

**عملیات موفق (Success):**

```
{
    "status": true,
    "time": 1732048123,
    "tasks": 12 // (فقط در update_status موجود است)
}
```

**بروز خطا (Error):**

```
{
    "status": false,
    "time": 1732048123,
    "message": "Error description...",
    "trace": [...]
}
```

<div id="bkmrk-route-info-task-op" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# GET /api/v2/tasks/task/details

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/tasks/task/details</td><td style="direction: ltr; padding: 10px;">OfficialController@getTasksDetails</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد جزئیات تکمیلی یک تسک مشخص را که شامل **یادداشت‌ها (Notes)** و **چک‌لیست‌ها (Checklists)** است، بازیابی می‌کند. منطق عملکرد به شرح زیر است:

<div id="bkmrk-%DB%8C%D8%A7%D8%AF%D8%AF%D8%A7%D8%B4%D8%AA%E2%80%8C%D9%87%D8%A7-%28notes%29%3A-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **یادداشت‌ها (Notes):**
    - اطلاعات از جدول `tasks_notes` بر اساس شناسه تسک دریافت می‌شود.
    - اطلاعات کاربر نویسنده یادداشت (`user`) با فراخوانی `StaticController::getOperators` هیدراته می‌شود. از آنجا که ایندکس `[0]` انتخاب شده، خروجی کاربر یک **آبجکت** است.
- **چک‌لیست (Checklist):**
    - آیتم‌ها از جدول `tasks_item_checklist` واکشی می‌شوند.
    - وضعیت انجام کار (`done`) به صورت Boolean محاسبه می‌شود.
    - اگر آیتم انجام شده باشد (`done\_at` نال نباشد)، اطلاعات کاربری که آن را انجام داده (`done\_by`) نیز به صورت یک آبجکت کامل کاربر برگردانده می‌شود. در غیر این صورت، مقدار `false` برمی‌گردد.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">id</td><td>Integer</td><td>بله</td><td>شناسه تسک (Task ID) که جزئیات آن مورد نیاز است.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

خروجی شامل آبجکت `payload` است که دو بخش اصلی `notes` و `checklist` را در بر می‌گیرد.

```
{
    "payload": {
        "notes": [
            {
                "user": {
                    "id": 10,
                    "text": "1001 - نام کاربر",
                    "query": { ... } // جزئیات کامل کاربر
                },
                "note": "این یک یادداشت نمونه برای تسک است.",
                "created": "2024-11-25T10:00:00.000000Z"
            }
        ],
        "checklist": [
            {
                "id": 55,
                "operator": { // کاربری که آیتم چک‌لیست را ایجاد کرده
                    "id": 10,
                    "text": "1001 - ایجاد کننده",
                    "query": { ... }
                },
                "content": "بررسی مستندات فنی",
                "done": true,
                "done_by": { // کاربری که آیتم را تیک زده (انجام داده)
                    "id": 12,
                    "text": "1002 - انجام دهنده",
                    "query": { ... }
                },
                "done_at": "2024-11-26 14:30:00",
                "created_at": "2024-11-25T09:00:00.000000Z"
            },
            {
                "id": 56,
                "operator": { ... },
                "content": "تست نهایی روی سرور استیج",
                "done": false,
                "done_by": false, // چون انجام نشده
                "done_at": false, // چون انجام نشده
                "created_at": "2024-11-25T09:05:00.000000Z"
            }
        ]
    },
    "meta": {
        "timestamp": 1732616000
    }
}
```

# GET /api/v2/support/departments/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/support/departments/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listDepartmentsSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست دپارتمان‌های پشتیبانی فعال را جهت نمایش به کاربر برمی‌گرداند. نکات فنی و منطقی این روت عبارتند از:

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D9%88-%D9%85%D8%B1%D8%AA%D8%A8%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C%3A-%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **فیلتر و مرتب‌سازی:** تنها دپارتمان‌هایی که وضعیت فعال دارند (`status = 1`) از جدول `support_departments` انتخاب شده و بر اساس فیلد `order` مرتب می‌شوند.
- **جایگزینی پویا نام برند (Brand Injection):**
    - در هنگام پردازش لیست، سیستم بررسی می‌کند که آیا کلمه "آژانس" در عنوان فارسی (`title_fa`) وجود دارد یا خیر.
    - اگر وجود داشته باشد، نام برند آژانس جاری (`brand_fa`) از جدول `offices` (بر اساس `branch` موجود در درخواست) استخراج شده و جایگزین کلمه "آژانس" می‌شود.
    - مثال: اگر عنوان دپارتمان "امور مالی آژانس" باشد و نام برند "تراول‌سیتی" باشد، خروجی به "امور مالی تراول‌سیتی" تغییر می‌کند.
- **ساختار چندزبانه:** عنوان‌ها در قالب یک آبجکت شامل کلیدهای `fa` و `en` بازگردانده می‌شوند.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">branch</td><td>Integer</td><td>بله</td><td>شناسه شعبه فعال (جهت یافتن نام برند برای جایگزینی در عنوان‌ها). معمولاً از طریق میدل‌ور تزریق می‌شود.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732617000,
    "data": [
        {
            "id": 1,
            "allowed_response": 1, // سطح دسترسی پاسخ‌دهی (مثلا: 1 برای همه)
            "description": "سوالات مربوط به پروازهای داخلی و خارجی",
            "title": {
                "fa": "فروش پرواز", // بدون تغییر چون کلمه 'آژانس' ندارد
                "en": "Flight Sales"
            }
        },
        {
            "id": 2,
            "allowed_response": 0,
            "description": "پیگیری موارد مالی و حسابداری",
            "title": {
                "fa": "امور مالی آسمان سیر", // 'آژانس' با 'آسمان سیر' جایگزین شده است
                "en": "Finance Department"
            }
        }
    ]
}
```

# GET /api/v2/support/department/members

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/support/department/members</td><td style="direction: ltr; padding: 10px;">OfficialController@listDepartmentMembersSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

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

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%3A-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **بررسی اعتبار:** ابتدا وجود دپارتمان بر اساس شناسه ورودی بررسی می‌شود. اگر دپارتمان یافت نشود یا لیست اعضای آن (`members`) خالی باشد، یک خطای JSON با کد `1000` بازگردانده می‌شود.
- **استخراج اعضا:** فیلد `members` در دیتابیس به صورت یک آرایه JSON ذخیره شده است. سیستم این آرایه را دیکد کرده و روی شناسه‌های پرسنلی (IDs) حلقه می‌زند.
- **هیدراته کردن اطلاعات کاربر:** برای هر شناسه عددی، متد `StaticController::getOperators` فراخوانی می‌شود. از آنجا که کد از ایندکس `[0]` استفاده می‌کند، هر آیتم در آرایه خروجی یک **آبجکت** کامل کاربر است (نه آرایه تو در تو).

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">department</td><td>Integer</td><td>بله</td><td>شناسه دپارتمان مورد نظر که لیست اعضای آن درخواست شده است.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

خروجی شامل آرایه‌ای از آبجکت‌های کاربر در فیلد `items` است.

```
{
    "items": [
        {
            "id": 15,
            "text": "1005 - نام و نام خانوادگی",
            "query": {
                "id": 15,
                "personnel_id": 1005,
                "first_name": "نام",
                "last_name": "نام خانوادگی",
                "branch": "[1]",
                "status": 1,
                // سایر فیلدهای جدول کاربران
                ...
            }
        },
        {
            "id": 22,
            "text": "1008 - کاربر دوم",
            "query": { ... }
        }
    ],
    "meta": {
        "timestamp": 1732618000
    }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>#### نمونه خطا (در صورت نبود دپارتمان یا عضو):

```
{
    "error": {
        "code": 1000,
        "message": "در این دپارتمان عضوی وجود ندارد و یا اینکه دپارتمان اشتباه ارسال شده است."
    },
    "meta": {
        "timestamp": 1732618005
    }
}
```

# GET /api/v2/support/department/questions/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/support/department/questions/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listQuestionsDepartmentSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست سوالات پیش‌فرض (یا موضوعات تیکت) مربوط به یک دپارتمان خاص را برمی‌گرداند. کوئری دیتابیس شامل فیلترهای زیر است:

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%AF%D9%BE%D8%A7%D8%B1%D8%AA%D9%85%D8%A7%D9%86%3A-%D8%B1%DA%A9%D9%88%D8%B1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **فیلتر دپارتمان:** رکوردهایی که فیلد `department` آن‌ها برابر با ورودی کاربر باشد.
- **فیلتر سطح (رابطه):** تنها سوالات سطح اول (Root) انتخاب می‌شوند (شرط `whereNull('relationship')`). این یعنی زیرمجموعه‌ها یا پاسخ‌ها در این لیست نمی‌آیند.
- **فیلتر وضعیت:** تنها رکوردهای فعال (`status = 1`) انتخاب می‌شوند.

</div>**نکته:** خروجی مستقیماً تمام ستون‌های جدول `support_questions` را بدون تغییر ساختار برمی‌گرداند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">department</td><td>Integer</td><td>بله</td><td>شناسه دپارتمان مورد نظر برای دریافت لیست سوالات آن.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732619000,
    "data": [
        {
            "id": 101,
            "department": 5,
            "title": "مشکل در صدور بلیط",
            "relationship": null, // چون whereNull اعمال شده
            "status": 1,
            "created_at": "2024-01-01 12:00:00",
            "updated_at": "2024-01-01 12:00:00"
        },
        {
            "id": 102,
            "department": 5,
            "title": "درخواست استرداد وجه",
            "relationship": null,
            "status": 1,
            "created_at": "2024-01-02 10:30:00",
            "updated_at": "2024-01-02 10:30:00"
        }
    ]
}
```

# GET /api/v2/support/tickets

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/support/tickets</td><td style="direction: ltr; padding: 10px;">OfficialController@indexTicketsSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست تیکت‌های پشتیبانی را بر اساس نقش کاربر (درخواست‌کننده یا پاسخ‌دهنده) مدیریت می‌کند. نکات کلیدی منطق آن عبارتند از:

<div id="bkmrk-%D8%AF%D8%A7%D9%85%D9%86%D9%87-%D9%86%D9%85%D8%A7%DB%8C%D8%B4-%28scope%29%3A" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **دامنه نمایش (Scope):** بر اساس فیلتر `ticket_scope`، تیکت‌ها در سه حالت قابل دریافت هستند: 
    - `sent`: تیکت‌هایی که کاربر جاری ایجاد کرده است (`requester = operatorId`). نوع این تیکت‌ها در خروجی `send` درج می‌شود.
    - `inbox`: تیکت‌هایی که به دپارتمانی ارجاع شده که کاربر جاری عضو آن است (بررسی عضویت با `JSON_CONTAINS` روی ستون `members` دپارتمان). نوع این تیکت‌ها در خروجی `receive` درج می‌شود.
    - `all`: ادغام هر دو حالت بالا.
- **شمارنده پیام‌های خوانده نشده (Unread Counter):** برای هر تیکت، تعداد یادداشت‌هایی که خوانده نشده‌اند (`read\_by` نال است) و توسط کاربر جاری نوشته نشده‌اند، شمرده می‌شود. شرط اضافی `NOT JSON_CONTAINS` بررسی می‌کند که نویسنده پیام جزو اعضای دپارتمان نباشد (برای تشخیص سمت مقابل مکالمه).
- **آمار کلی (Waiting):** در بخش `payload`، تعداد کل تیکت‌های باز (`status = 1`) که به نحوی به کاربر مربوط می‌شوند، برگردانده می‌شود.
- **اطلاعات کاربر:** فیلدهای `requester` و `operator` با استفاده از متد `StaticController::getOperators` هیدراته شده و چون از ایندکس `[0]` استفاده شده، به صورت آبجکت برگردانده می‌شوند.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">search\[length\]</td><td>Integer</td><td>خیر</td><td>تعداد رکورد در هر صفحه (پیش‌فرض: 20).</td></tr><tr><td style="direction: ltr;">search\[current\]</td><td>Integer</td><td>خیر</td><td>شماره صفحه جاری (پیش‌فرض: 1).</td></tr><tr><td style="direction: ltr;">filters\[ticket\_scope\]</td><td>String</td><td>خیر</td><td>دامنه جستجو: `all` (پیش‌فرض)، `sent`، یا `inbox`.</td></tr><tr><td style="direction: ltr;">filters\[status\]</td><td>Integer</td><td>خیر</td><td>فیلتر بر اساس وضعیت دپارتمان (پیش‌فرض: 1).</td></tr><tr><td style="direction: ltr;">filters\[score\]</td><td>Integer</td><td>خیر</td><td>فیلتر بر اساس امتیاز تیکت.</td></tr><tr><td style="direction: ltr;">filters\[department\]</td><td>Integer</td><td>خیر</td><td>فیلتر بر اساس شناسه دپارتمان.</td></tr><tr><td style="direction: ltr;">filters\[operator\]</td><td>Integer</td><td>خیر</td><td>فیلتر بر اساس شناسه اپراتور پاسخ‌دهنده (فقط در حالت inbox و all تأثیر دارد).</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "items": [
        {
            "id": 505,
            "type": "send", // تیکت ارسال شده توسط کاربر
            "department": {
                "id": 3,
                "title": {
                    "fa": "پشتیبانی فنی",
                    "en": "Technical Support"
                }
            },
            "requester": { // اطلاعات کامل کاربر درخواست دهنده
                "id": 10,
                "text": "1001 - نام کاربر",
                "query": { ... }
            },
            "operator": false, // هنوز اپراتوری پاسخ نداده
            "score": 0,
            "status": 1,
            "unread": 1, // تعداد پیام‌های جدید
            "created_at": "2024-11-20 14:00:00"
        },
        {
            "id": 498,
            "type": "receive", // تیکت دریافتی (کاربر عضو دپارتمان است)
            "department": {
                "id": 2,
                "title": {
                    "fa": "امور مالی",
                    "en": "Finance"
                }
            },
            "requester": {
                "id": 55,
                "text": "1055 - مشتری نمونه",
                "query": { ... }
            },
            "operator": { // اپراتوری که تیکت را هندل می‌کند
                "id": 10,
                "text": "1001 - نام کاربر",
                "query": { ... }
            },
            "score": 5,
            "status": 2,
            "unread": 0,
            "created_at": "2024-11-18 09:30:00"
        }
    ],
    "payload": {
        "wating": 5 // تعداد کل تیکت‌های باز مرتبط
    },
    "meta": {
        "total_records": 25,
        "total_pages": 2,
        "current_page": 1,
        "per_page": 20,
        "last_page": 2,
        "next_page": 2,
        "prev_page": 0,
        "from": 1,
        "to": 20,
        "timestamp": 1732620000
    }
}
```

# POST /api/v2/support/ticket/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/api/v2/support/ticket/update</td><td style="direction: ltr; padding: 10px;">OfficialController@updateTicketSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای بروزرسانی اطلاعات مختلف یک تیکت استفاده می‌شود. ساختار کد به صورت `if/else if` است، به این معنی که در هر درخواست تنها **یکی** از عملیات زیر بر اساس اولویت پارامتر ارسالی انجام می‌شود:

<div id="bkmrk-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%28status%29" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. **تغییر وضعیت (Status):** اگر پارامتر `status` ارسال شود: 
    - وضعیت تیکت آپدیت می‌شود.
    - یک **یادداشت خودکار** (System Note) متناسب با وضعیت جدید (مثلاً: "مورد شما در حال بررسی می‌باشد...") توسط اپراتور جاری در تیکت درج می‌شود.
    - اگر تیکت هنوز اپراتوری نداشت (`operator IS NULL`)، اپراتور جاری به عنوان مسئول تیکت ثبت می‌شود.
2. **ثبت امتیاز (Score):** اگر پارامتر `score` ارسال شود، امتیاز کاربر به تیکت ثبت می‌شود.
3. **ارجاع به دپارتمان (Department):** اگر پارامتر `department` ارسال شود، تیکت به دپارتمان دیگری منتقل می‌شود.
4. **تخصیص کارشناس (Operator):** اگر پارامتر `operator` ارسال شود: 
    - مسئول تیکت به شناسه ارسالی تغییر می‌کند.
    - یک اعلان (Notification) تلگرامی برای کارشناس انتخاب شده ارسال می‌شود که حاوی لینک مشاهده تیکت است.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">ticket</td><td>Integer</td><td>بله</td><td>شناسه تیکت مورد نظر برای ویرایش.</td></tr><tr><td style="direction: ltr;">status</td><td>Integer</td><td>اختیاری\*</td><td>وضعیت جدید تیکت (1 تا 5). در صورت ارسال، اولویت با این فیلد است.</td></tr><tr><td style="direction: ltr;">score</td><td>Integer</td><td>اختیاری\*</td><td>امتیاز دهی به تیکت (معمولاً 1 تا 5).</td></tr><tr><td style="direction: ltr;">department</td><td>Integer</td><td>اختیاری\*</td><td>شناسه دپارتمان جدید جهت انتقال تیکت.</td></tr><tr><td style="direction: ltr;">operator</td><td>Integer</td><td>اختیاری\*</td><td>شناسه کارشناس جهت ارجاع دستی تیکت.</td></tr></tbody></table>

</div></div>\* نکته: در هر درخواست باید فقط یکی از پارامترهای اختیاری ارسال شود تا عملیات مربوطه انجام گیرد.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div>  
</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732621500
}
```

# PATCH /api/v2/support/ticket/notice

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">PATCH</td><td style="direction: ltr; padding: 10px;">/api/v2/support/ticket/notice</td><td style="direction: ltr; padding: 10px;">OfficialController@noticeNoteTicketSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای ارسال دستی اعلان (Notification) به **درخواست‌کننده تیکت** (Requester) جهت اطلاع‌رسانی درباره پاسخ داده شدن به تیکت استفاده می‌شود. فرآیند به شرح زیر است:

<div id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D8%A7%DB%8C%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%3A-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **شناسایی کاربر:** ابتدا اطلاعات تماس کاربر درخواست‌کننده (موبایل، تلگرام و شعبه) از جدول `operators` بر اساس شناسه تیکت استخراج می‌شود.
- **تعیین دامنه لینک:** سیستم بررسی می‌کند که کاربر متعلق به کدام شعبه است. اگر شعبه داشته باشد، دامنه (Domain) اختصاصی آن شعبه از جدول `offices` خوانده می‌شود؛ در غیر این صورت دامنه پیش‌فرض `erp.savosh.com` استفاده می‌شود.
- **کانال‌های ارسال (بر اساس Type):**
    - **تلگرام:** اگر `type` برابر 1 یا 2 باشد و کاربر شناسه تلگرام داشته باشد، پیام حاوی لینک "مشاهده تیکت" ارسال می‌شود.
    - **پیامک (SMS):** اگر `type` برابر 2 باشد و کاربر شماره موبایل داشته باشد، پیامک اطلاع‌رسانی ارسال می‌شود.
- **لینک دهی:** شناسه تیکت در لینک ارسالی با عدد 10,000 جمع می‌شود (فرمت نمایش عمومی).

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" class="align-center" style="width: 100%; border-collapse: collapse; text-align: right; border-width: 1px;"><thead><tr style="background: #f4f4f4;"><th class="align-center" style="padding: 8px;">نام پارامتر</th><th class="align-center" style="padding: 8px;">نوع</th><th class="align-center" style="padding: 8px;">الزامی؟</th><th class="align-center" style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td class="align-center" style="direction: ltr;">ticket</td><td class="align-center">Integer</td><td class="align-center">بله</td><td class="align-center">شناسه تیکت مورد نظر.</td></tr><tr><td class="align-center" style="direction: ltr;">type</td><td class="align-center">Integer</td><td class="align-center">بله</td><td class="align-center">نوع اطلاع‌رسانی:   
`1`: فقط تلگرام   
`2`: تلگرام و پیامک (SMS)</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

در صورت موفقیت، کد وضعیت **201 (Created)** بدون بدنه (Body) بازگردانده می‌شود.

```
HTTP/1.1 201 Created
```

در صورت بروز خطای سیستمی:

```
{
    "error": {
        "code": 0,
        "message": "Error description...",
        "trace": [...]
    },
    "meta": {
        "timestamp": 1732622000
    }
}
```

# GET /api/v2/support/ticket/notes/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/support/ticket/notes/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listNotesTicketSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه بازگرداندن جزئیات کامل یک تیکت و لیست گفتگوهای (Notes) آن را بر عهده دارد. همچنین به صورت همزمان عملیات "خوانده شدن" پیام‌ها را انجام می‌دهد. نکات فنی و منطقی آن عبارتند از:

<div id="bkmrk-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%28access" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **کنترل دسترسی (Access Control):** سیستم بررسی می‌کند که کاربر جاری اجازه مشاهده تیکت را دارد یا خیر. کاربر مجاز است اگر: 
    - درخواست‌کننده (Requester) تیکت باشد.
    - اپراتور (Operator) تخصیص داده شده به تیکت باشد.
    - عضو لیست `members` دپارتمان مربوطه باشد.
    - مدیر دپارتمان باشد (که در این صورت نقش مدیریتی او به عضویت عادی تبدیل شده و پردازش می‌شود).
    
    <span style="color: red; font-size: 12px;">\* در صورت عدم دسترسی، `status: false` و پیام خطا برگردانده می‌شود.</span>
- **منطق "خوانده شدن" (Mark as Read):**
    - پیام‌هایی که توسط خود کاربر نوشته شده‌اند، نادیده گرفته می‌شوند.
    - اگر کاربر جاری عضو دپارتمان باشد (پشتیبان)، سیستم فقط پیام‌هایی را به عنوان "خوانده شده" علامت می‌زند که نویسنده آن‌ها عضو دپارتمان **نباشد** (یعنی پیام‌های مشتری).
    - فیلدهای `read_by` و `read_on` در دیتابیس به‌روزرسانی می‌شوند.
- **فرمت‌دهی محتوا (HTML Content):**
    - اگر نوع پیام `attachment` باشد، بک‌اند به جای بازگرداندن صرفِ URL، کد **HTML** تولید می‌کند. 
        - برای تصاویر: تگ `<img>` درون لینک قرار می‌گیرد.
        - برای فایل‌ها: نام فایل به همراه یک آیکون SVG درون لینک قرار می‌گیرد.
    - آدرس فایل‌ها به دامین `storage.service01.ir` اشاره دارد.
- **هیدراتاسیون داده‌ها:** اطلاعات کاربران (نویسنده پیام، درخواست‌کننده، اپراتور) با استفاده از متد `StaticController::getOperators` به آبجکت کامل تبدیل می‌شود.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">ticket</td><td>Integer</td><td>بله</td><td>شناسه تیکت مورد نظر برای دریافت جزئیات و گفتگوها.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732623000,
    "data": {
        "department": {
            "id": 3,
            "type": "send", // 'send' if user is requester, else 'receive'
            "title": {
                "fa": "پشتیبانی فنی",
                "en": "Technical Support"
            }
        },
        "requester": { // آبجکت کامل کاربر درخواست دهنده
            "id": 10,
            "text": "1001 - نام کاربر",
            "query": {
                "id": 10,
                "sex": "male",
                "first_name": "Ali",
                "last_name": "Rezaei",
                "avatar": "...",
                "position": "...",
                "personnel_id": 1001
            }
        },
        "operator": false, // یا آبجکت کامل اپراتور پاسخگو
        "ticket": {
            "status": 1,
            "score": 0
        },
        "notes": [
            {
                "id": 1205,
                "type": "text",
                "operator": { ... }, // نویسنده پیام
                "note": "سلام، لطفا بررسی کنید.", // متن پیام
                "read_by": false, // یا آبجکت کاربری که پیام را خوانده
                "read_on": false, // تاریخ خوانده شدن
                "created_at": "2024-11-26 10:00:00"
            },
            {
                "id": 1206,
                "type": "text", // نوع در دیتابیس attachment است اما در خروجی text ست می‌شود (چون به HTML تبدیل شده)
                "operator": { ... },
                "note": "<a href="https://docs.airplus.app/..." rel="noopener" target="_blank"><img src="https://docs.airplus.app/..."></img></a>", // خروجی HTML
                "read_by": { ... },
                "read_on": "2024-11-26 10:05:00",
                "created_at": "2024-11-26 10:01:00"
            }
        ]
    }
}
```

#### نمونه خطا (عدم دسترسی):

```
{
    "status": false,
    "time": 1732623000,
    "message": "شما اجازه دسترسی به این گفتگو را ندارید"
}
```

# DELETE /api/v2/support/ticket/delete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">DELETE</td><td style="direction: ltr; padding: 10px;">/api/v2/support/ticket/delete</td><td style="direction: ltr; padding: 10px;">OfficialController@deleteTicketSupport</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه حذف فیزیکی و کامل یک تیکت و تمامی سوابق گفتگوهای آن را بر عهده دارد. عملیات به صورت غیرقابل بازگشت انجام می‌شود:

<div id="bkmrk-%D8%AD%D8%B0%D9%81-%D8%AA%DB%8C%DA%A9%D8%AA-%28ticket-del" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **حذف تیکت (Ticket Deletion):**
    - ابتدا رکورد اصلی تیکت با استفاده از شناسه `ticket` مستقیماً از جدول `support_tickets` حذف می‌شود.
- **پاکسازی پیام‌ها (Notes Cleanup):**
    - بلافاصله پس از حذف تیکت، تمام پیام‌ها و یادداشت‌های موجود در جدول `support_notes` که متعلق به این تیکت هستند، حذف می‌شوند تا داده‌ای در دیتابیس باقی نماند.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">ticket</td><td>Integer</td><td>بله</td><td>شناسه تیکتی که باید حذف شود.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732625000
}
```

# GET /api/v2/exams/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/exams/list</td><td style="direction: ltr; padding: 10px;">OfficialController@listExams</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست تمامی آزمون‌های فعال مرتبط با شعبه جاری را استخراج کرده و آماری از وضعیت هر آزمون ارائه می‌دهد:

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%A2%D8%B2%D9%85%D9%88%D9%86%E2%80%8C%D9%87%D8%A7%3A-%D8%AA%D9%86%D9%87%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **فیلتر آزمون‌ها:** تنها آزمون‌هایی که فیلد `status` آن‌ها برابر با 1 است و متعلق به `branch` درخواست‌کننده هستند، انتخاب می‌شوند.
- **محاسبه آمار (Statistics):** برای هر آزمون، دو مقدار محاسبه می‌شود: 
    - `requests`: تعداد کل سوالات تعریف شده برای آن آزمون (از جدول `exam_questions`).
    - `answers`: تعداد کل پاسخ‌نامه‌های ثبت شده توسط کاربران (از جدول `exam_response`).

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">branch</td><td>Integer</td><td>بله</td><td>شناسه شعبه (این مقدار معمولاً از توکن احراز هویت استخراج می‌شود).</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732626000,
    "data": [
        {
            "id": 10,
            "type": "general",
            "title": "آزمون جامع نیمسال اول",
            "requests": 25,
            "answers": 140
        },
        {
            "id": 12,
            "type": "specialized",
            "title": "آزمون تخصصی فنی",
            "requests": 10,
            "answers": 45
        }
    ]
}
```

# PATCH /api/v2/exams/answers/list/follow-up

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">PATCH</td><td style="direction: ltr; padding: 10px;">/api/v2/exams/answers/list/follow-up</td><td style="direction: ltr; padding: 10px;">OfficialController@listExamsAnswersFollowUp</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای به‌روزرسانی وضعیت «پیگیری» (Follow-up) یک رکورد خاص در جدول پاسخ‌نامه‌های آزمون (`exam_response`) استفاده می‌شود. کاربرد اصلی آن احتمالا علامت‌گذاری پاسخ‌نامه‌ها برای بررسی مجدد یا تغییر وضعیت رسیدگی به آن‌ها توسط مدیریت است.

<div id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA%3A-%D9%81" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **به‌روزرسانی وضعیت:** فیلد `follow_up` رکورد مشخص شده با `id`، به مقدار جدید ارسال شده تغییر می‌کند.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">id</td><td>Integer</td><td>بله</td><td>شناسه یکتا (Primary Key) رکورد پاسخ‌نامه در جدول `exam_response`.</td></tr><tr><td style="direction: ltr;">follow\_up</td><td>Mixed</td><td>بله</td><td>مقدار جدید برای وضعیت پیگیری (ممکن است عدد، رشته یا بولی باشد که وضعیت جدید را مشخص می‌کند).</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

در صورت موفقیت، سرور کد وضعیت **204 No Content** را بدون بازگرداندن هیچ داده‌ای (Body خالی) ارسال می‌کند.

```
HTTP/1.1 204 No Content
```

# GET /api/v2/exams/answers/evaluation

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/exams/answers/evaluation</td><td style="direction: ltr; padding: 10px;">OfficialController@listExamsEvaluation</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

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

<div id="bkmrk-%D8%B3%D9%88%D8%A7%D9%84%D8%A7%D8%AA-%D8%AA%D8%B4%D8%B1%DB%8C%D8%AD%DB%8C-%28expla" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **سوالات تشریحی (Explanatory):**
    - پاسخ‌های متنی کاربران عیناً در قالب یک آرایه لیست می‌شوند تا مدیر بتواند آن‌ها را مطالعه کند.
- **سوالات تک‌گزینه‌ای (Single Choice):**
    - تعداد دفعات انتخاب هر گزینه محاسبه می‌شود (هیستوگرام پاسخ‌ها).
    - میانگین عددی پاسخ‌ها (Average) نیز محاسبه می‌شود (مناسب برای سوالات امتیازدهی یا نظرسنجی عددی).
- **سوالات چندگزینه‌ای (Multiple Choice):**
    - پاسخ‌ها که به صورت JSON ذخیره شده‌اند (آرایه‌ای از گزینه‌های انتخاب شده)، باز شده و تعداد انتخاب شدن هر گزینه به صورت جداگانه شمارش می‌شود.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">exam</td><td>Integer</td><td>بله</td><td>شناسه آزمونی که قصد دریافت تحلیل پاسخ‌های آن را دارید.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732628000,
    "data": [
        {
            "id": 101,
            "type": "single_choice",
            "subject": "رضایت‌مندی",
            "title": "از کیفیت دوره چقدر رضایت دارید؟ (1 تا 5)",
            "options": ["1", "2", "3", "4", "5"],
            "description": null,
            "mandatory": 1,
            "score": 0,
            "order": 1,
            "relation": false,
            "average": 4,
            "counter": 50,
            "evaluation": {
                "5": 30,
                "4": 15,
                "3": 5
            }
        },
        {
            "id": 102,
            "type": "explanatory",
            "subject": "نظرات",
            "title": "پیشنهاد خود را بنویسید",
            "average": 0,
            "counter": 12,
            "evaluation": [
                {"response": "بسیار عالی بود"},
                {"response": "زمان کلاس کم بود"}
            ]
        }
    ]
}
```

# GET /api/v2/salary/annual-obligation

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/salary/annual-obligation</td><td style="direction: ltr; padding: 10px;">OfficialController@salaryAnnualObligation</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد اطلاعات مربوط به تعهدات مالی و پارامترهای محاسباتی حقوق و دستمزد (مانند پایه حقوق، سقف مالیاتی، حق مسکن و...) را برای یک سال مالی مشخص بازیابی می‌کند.

<div id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D8%B3%D8%A7%D9%84%3A-%D8%B3" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **جستجو بر اساس سال:** سیستم در جدول `salary_annual_obligation` رکوردهایی که فیلد `year` آن‌ها با ورودی کاربر مطابقت دارد را جستجو می‌کند.
- **بررسی وجود داده:**
    - اگر اطلاعاتی پیدا شود، لیست رکوردها بازگردانده می‌شود.
    - اگر رکوردی برای سال مورد نظر وجود نداشته باشد، خروجی با `status: false` و پیام "There is no data available" ارسال می‌شود.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">year</td><td>Integer</td><td>بله</td><td>سال مورد نظر برای دریافت تعهدات مالی (مثلاً 1403).</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1732629000,
    "data": [
        {
            "id": 5,
            "year": 1403,
            "min_salary": 70000000,
            "housing_allowance": 9000000,
            "description": "بخشنامه حقوق سال 1403",
            "created_at": "2024-03-20 10:00:00"
        }
    ]
}
```

# GET /api/v2/scheduled/notifications

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/api/v2/scheduled/notifications</td><td style="direction: ltr; padding: 10px;">OfficialController@listScheduledNotifications</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div></div>### تحلیل دقیق عملکرد (Deep Functionality Analysis)

این متد وظیفه استخراج و آماده‌سازی لیست اعلانات زمان‌بندی شده (Scheduled Notifications) را دارد. برخلاف یک کوئری ساده، این تابع عملیات **Sanitization** (پاک‌سازی) و **Transformation** (تغییر فرمت) را روی داده‌ها انجام می‌دهد.

**مراحل اجرای کد:**

<div id="bkmrk-database-query%3A-%D8%A7%D8%AA%D8%B5%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. **Database Query:** اتصال به جدول `scheduled_notifications` و دریافت تمام ستون‌ها. 
    - **Scope Filtering:** نتایج بر اساس مقدار `branch` فیلتر می‌شوند. این مقدار از آبجکت `$request` خوانده می‌شود (که معمولاً توسط میدل‌ور پس از احراز هویت ست شده است).
2. **Data Transformation Loop (Map):** روی تک‌تک رکوردهای دریافتی یک حلقه اجرا شده و تغییرات زیر اعمال می‌شود: 
    - **Unsetting Fields:** برای امنیت و کاهش حجم Payload، فیلدهای سیستمی زیر **حذف** می‌شوند: 
        - `created_at` (تاریخ ایجاد رکورد)
        - `updated_at` (تاریخ ویرایش رکورد)
        - `branch` (شناسه شعبه - چون همه خروجی‌ها متعلق به یک شعبه هستند، تکرار آن غیرضروری است).
    - **JSON Decoding:** فیلدهای زیر که در دیتابیس به صورت رشته (String/Text) ذخیره شده‌اند، به آبجکت یا آرایه PHP تبدیل می‌شوند تا در خروجی JSON نهایی به درستی نمایش داده شوند: 
        - `recipients`
        - `devices`
        - `mobiles`
3. **Response Formatting:** داده‌های پردازش شده در قالب استاندارد پاسخ موفقیت‌آمیز قرار می‌گیرند.
4. **Error Handling:** کل پروسه داخل بلوک `try-catch` قرار دارد. در صورت بروز هرگونه خطا (مثل قطع دیتابیس یا مشکل در Decode کردن JSON)، سیستم کرش نکرده و یک پاسخ استاندارد خطا برمی‌گرداند.

</div>### ورودی‌ها و وابستگی‌ها (Inputs &amp; Dependencies)

<div id="bkmrk-%D9%85%D9%86%D8%A8%D8%B9-%28source%29-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">منبع (Source)</th><th style="padding: 8px;">پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">توضیحات فنی</th></tr></thead><tbody><tr><td style="direction: ltr;">Request / Auth Middleware</td><td style="direction: ltr;">branch</td><td>Integer/String</td><td>این پارامتر به صورت صریح در URL دیده نمی‌شود اما متد `$request->get('branch')` برای فیلتر کردن کوئری SQL حیاتی است. این مقدار معمولاً از توکن کاربر استخراج می‌شود.</td></tr></tbody></table>

</div></div>### ساختار پاسخ‌ها (Response Structures)

#### ✅ پاسخ موفق (Success - 200 OK)

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

```
{
    "status": true,
    "time": 1732631000, // Timestamp زمان اجرای درخواست
    "data": [
        {
            "id": 15, // فیلدهای باقی‌مانده از جدول
            "title": "Title from DB", // (فرض بر وجود این فیلد در دیتابیس)
            "message": "Message Body", // (فرض بر وجود این فیلد در دیتابیس)
            // فیلدهای Decode شده:
            "recipients": ["user_group_1", "user_group_2"], // فرمت آرایه
            "devices": {"android": true, "ios": false}, // فرمت آبجکت
            "mobiles": ["0912xxxxxxx"] // فرمت آرایه
            // نکته: created_at, updated_at, branch در اینجا وجود ندارند.
        }
    ]
}
```

#### ❌ پاسخ خطا (Failure - Exception Captured)

در صورت بروز خطای سیستمی (Exception)، بلوک `catch` فعال شده و خروجی زیر را تولید می‌کند:

```
{
    "status": false,
    "time": 1732631005,
    "message": "HY000 : SQLSTATE[HY000] [2002] Connection refused", // کد خطا + متن خطا
    "trace": [ ... ] // آرایه کامل Stack Trace برای دیباگ (فقط در محیط توسعه باید نمایش داده شود)
}
```

# POST /api/v2/scheduled/notifications

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div style="text-align: center;"><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/api/v2/scheduled/notifications</td><td style="direction: ltr; padding: 10px;">OfficialController@storeScheduledNotifications</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div></div>### تحلیل دقیق عملکرد (Deep Functionality Analysis)

این متد برای ذخیره‌سازی رکوردها در جدول `scheduled_notifications` استفاده می‌شود. برخلاف بسیاری از متدها که از Eloquent Model استفاده می‌کنند، این متد از `DB::table` برای درج مستقیم (Raw Insert) استفاده می‌کند که سربار کمتری دارد.

**مراحل اجرای کد:**

<div id="bkmrk-data-serialization-%28" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. **Data Serialization (سریال‌سازی داده‌ها):**   
    ورودی‌های آرایه‌ای یا آبجکت مانند `recipients` (گیرندگان)، `devices` (دستگاه‌ها) و `mobiles` (شماره‌های همراه) قبل از ذخیره شدن در دیتابیس، توسط تابع `json_encode` به رشته JSON تبدیل می‌شوند.
2. **Auth Context Injection:**   
    شناسه شعبه (`branch`) و شناسه اپراتور ثبت‌کننده (`operator->id`) مستقیماً از آبجکت `$request` استخراج می‌شوند. این یعنی این مقادیر توسط میدل‌ور `authWithJwt` در ریکوئست تزریق شده‌اند و کاربر نمی‌تواند آنها را جعل کند.
3. **Database Insert:**   
    داده‌ها در جدول درج می‌شوند. نکته مهم این است که **هیچگونه Validation (اعتبارسنجی) صریحی** در ابتدای تابع دیده نمی‌شود. فرض بر این است که کلاینت داده‌ها را کامل ارسال کرده است. اگر پارامترهای اجباری نرسند، در سطح دیتابیس یا هنگام دسترسی به پراپرتی‌ها (مثل `$request->recipients`) خطا رخ خواهد داد.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-parameter-type-requi" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div style="text-align: center;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">Parameter</th><th style="padding: 8px;">Type</th><th style="padding: 8px;">Required</th><th style="padding: 8px;">Description</th></tr></thead><tbody><tr><td style="direction: ltr;">recipients</td><td style="direction: ltr;">Array</td><td>Yes</td><td>آرایه‌ای از شناسه‌های گیرندگان یا گروه‌های کاربری (ذخیره به صورت JSON).</td></tr><tr><td style="direction: ltr;">devices</td><td style="direction: ltr;">Object/Array</td><td>Yes</td><td>تنظیمات دستگاه‌های هدف (مثلاً `{"android": true}`).</td></tr><tr><td style="direction: ltr;">object</td><td style="direction: ltr;">String/Mixed</td><td>Yes</td><td>محتوای اصلی اعلان یا شناسه آبجکت مرتبط.</td></tr><tr><td style="direction: ltr;">type</td><td style="direction: ltr;">String</td><td>Yes</td><td>نوع اعلان (مثلاً 'system', 'alert', 'news').</td></tr><tr><td style="direction: ltr;">period</td><td style="direction: ltr;">String/Int</td><td>Yes</td><td>زمان‌بندی ارسال (فرمت آن وابسته به لاجیک کرون‌جاب سیستم است).</td></tr><tr><td style="direction: ltr;">mobiles</td><td style="direction: ltr;">Array</td><td>No</td><td>لیست شماره موبایل‌ها (اختیاری).</td></tr></tbody></table>

</div></div>### ساختار پاسخ‌ها (Response Structures)

#### ✅ Success (200 OK)

در صورت درج موفقیت‌آمیز:

```
{
    "status": true,
    "time": 1732631200
}
```

#### ❌ Error (Exception Captured)

در صورت بروز هرگونه خطا (مثلاً خطای دیتابیس یا نبود فیلد اجباری):

```
{
    "status": false,
    "time": 1732631205,
    "message": "23000 : SQLSTATE[23000]: Integrity constraint violation...",
    "trace": [ ... ] // Stack trace کامل خطا
}
```

# PUT /api/v2/scheduled/notifications

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div style="text-align: center;"><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">PUT</td><td style="direction: ltr; padding: 10px;">/api/v2/scheduled/notifications</td><td style="direction: ltr; padding: 10px;">OfficialController@updateScheduledNotifications</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div></div>### تحلیل دقیق عملکرد (Deep Functionality Analysis)

این متد یک **بروزرسانی کامل (Full Update)** روی جدول `scheduled_notifications` انجام می‌دهد. نکته حیاتی اینجاست که حتی فیلدهای سیستمی مانند `branch` و `operator` نیز بازنویسی می‌شوند.

**منطق دقیق کد:**

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%DA%AF%DB%8C%E2%80%8C%D9%87%D8%A7%DB%8C-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. **استخراج وابستگی‌های سیستمی (System Dependencies):**   
    متد مقادیر `branch` و `operator` را از آبجکت `$request` می‌خواند.   
    <span style="color: #d32f2f; font-size: 0.9em;">⚠️ نکته امنیتی:</span> این مقادیر معمولاً توسط میدل‌ور `authWithJwt` تزریق می‌شوند، اما کد کنترلر کورکورانه آن‌ها را آپدیت می‌کند (`$request->get('operator')->id`). این یعنی مالکیت رکورد به "اپراتور و شعبه جاری" تغییر می‌کند.
2. **تبدیل داده‌ها (Data Transformation):**   
    فیلدهای `recipients` و `devices` الزاماً با `json_encode` تبدیل می‌شوند. فیلد `mobiles` منطق شرطی دارد: اگر مقدار داشته باشد تبدیل به JSON می‌شود، در غیر این صورت `NULL` ذخیره می‌شود.
3. **مدیریت زمان (Timestamping):**   
    چون از `DB::table` استفاده شده، `updated_at` خودکار نیست و با `Carbon::now()` دقیقاً در لحظه آپدیت تنظیم می‌شود.

</div>### پارامترهای پردازش شده (Processed Parameters)

این جدول شامل تمام داده‌هایی است که در آرایه `$update` استفاده شده‌اند، چه توسط کاربر ارسال شوند و چه توسط سیستم تزریق شوند.

<div id="bkmrk-key-%2F-variable-sourc" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div style="text-align: center;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">Key / Variable</th><th style="padding: 8px;">Source Type</th><th style="padding: 8px;">Required</th><th style="padding: 8px;">Logic / Transformation</th></tr></thead><tbody><tr><td style="direction: ltr;">id</td><td style="direction: ltr;">Integer</td><td>Yes</td><td>در شرط `where` استفاده می‌شود.</td></tr><tr><td style="direction: ltr;">branch</td><td style="direction: ltr;">Integer</td><td>Yes\*</td><td>مستقیماً در دیتابیس ذخیره می‌شود (معمولاً تزریق شده توسط Middleware).</td></tr><tr><td style="direction: ltr;">operator</td><td style="direction: ltr;">Object (User)</td><td>Yes\*</td><td>آبجکت یوزر دریافت شده و `->id` آن استخراج و ذخیره می‌شود.</td></tr><tr><td style="direction: ltr;">recipients</td><td style="direction: ltr;">Array</td><td>Yes</td><td>توسط `json_encode` به رشته تبدیل می‌شود.</td></tr><tr><td style="direction: ltr;">devices</td><td style="direction: ltr;">Object/Array</td><td>Yes</td><td>توسط `json_encode` به رشته تبدیل می‌شود.</td></tr><tr><td style="direction: ltr;">object</td><td style="direction: ltr;">String</td><td>Yes</td><td>بدون تغییر ذخیره می‌شود.</td></tr><tr><td style="direction: ltr;">type</td><td style="direction: ltr;">String</td><td>Yes</td><td>بدون تغییر ذخیره می‌شود.</td></tr><tr><td style="direction: ltr;">period</td><td style="direction: ltr;">String/Int</td><td>Yes</td><td>بدون تغییر ذخیره می‌شود.</td></tr><tr><td style="direction: ltr;">status</td><td style="direction: ltr;">Boolean/Int</td><td>Yes</td><td>بدون تغییر ذخیره می‌شود.</td></tr><tr><td style="direction: ltr;">mobiles</td><td style="direction: ltr;">Array/Null</td><td>No</td><td>اگر وجود داشته باشد `json_encode` می‌شود، وگرنه `NULL`.</td></tr></tbody></table>

</div></div>### ساختار پاسخ‌ها (Response Structures)

#### ✅ Success (200 OK)

```
{
    "status": true,
    "time": 1732631500
}
```

#### ❌ Error (Exception Captured)

```
{
    "status": false,
    "time": 1732631505,
    "message": "...", // PHP/SQL Error Message
    "trace": [ ... ]
}
```

# POST /api/v2/banks/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div style="text-align: center;"><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px; color: #d32f2f;">POST</td><td style="direction: ltr; padding: 10px;">/api/v2/banks/list</td><td style="direction: ltr; padding: 10px;">AccountingController@banksList</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div></div>### تحلیل دقیق عملکرد (Deep Functionality Analysis)

این متد یک کوئری مستقیم روی دیتابیس اجرا می‌کند:

```
SELECT * FROM accounting_banks WHERE status = 1
```

**نکات (Fact-Based):**

<div id="bkmrk-%D9%86%D9%88%D8%B9-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C%3A-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%D8%AF%D9%82%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **نوع خروجی:** خروجی دقیقاً همان `Collection` بازگردانده شده توسط Query Builder لاراول است (آرایه‌ای از آبجکت‌های stdClass).
- **عدم فیلترینگ:** هیچ ستونی پنهان نمی‌شود (hidden) و هیچ تغییری در نام کلیدها داده نمی‌شود. تمام ستون‌های موجود در جدول دیتابیس به کلاینت ارسال می‌شوند.
- **ستون‌های قطعی:** تنها ستونی که وجودش در خودِ این تابع قطعی است، ستون `status` است (چون در شرط where استفاده شده).
- **ستون‌های استنتاجی (از کدهای مجاور):** با نگاه به متدهای دیگر همین فایل (مثل `accountIndex`)، مشخص می‌شود که جدول `accounting_banks` حداقل دارای ستون‌های `id`، `title_fa` و `logo` نیز می‌باشد، اما این تابع محدود به این‌ها نیست و **همه چیز** را برمی‌گرداند.

</div><div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### ساختار پاسخ‌ها (Response Structures)

ساختار خروجی **دقیقاً منطبق بر ساختار جدول دیتابیس (Schema)** است که در کدهای ارسالی موجود نیست.

#### ✅ Success (200 OK)

```
[
    {
        "status": 1,
        // سایر ستون‌های جدول accounting_banks عیناً در اینجا قرار می‌گیرند.
        // (نام و تعداد فیلدها وابسته به دیتابیس است)
    },
    ...
]
```

# POST /v2/account/bill

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/account/bill</td><td style="direction: ltr; padding: 10px;">AccountingController@accountBill</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

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

<div id="bkmrk-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-%D8%B3%D8%B7%D8%AD-%D8%AD%D8%B3%D8%A7%D8%A8-%28acco" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **تشخیص سطح حساب (Accounting Titles):**
    - با استفاده از متد `getAccountingTitles`، کد حساب ورودی (`id`) تحلیل می‌شود.
    - طول کد سطح حساب را مشخص می‌کند: 2 رقم (گروه)، 4 رقم (کل)، 6 رقم (معین) و 9 رقم (تفضیلی).
    - وابستگی‌های حساب مانند "تفضیلی‌های شناور" (Detailed First Preferences) استخراج می‌شوند تا تراکنش‌های مرتبط با آن‌ها نیز در گزارش لحاظ شوند.
- **مدیریت تاریخ و فیلترها:**
    - اگر بازه تاریخی توسط کلاینت ارسال نشود، سیستم به صورت پیش‌فرض بازه "3 ماه اخیر" را در نظر می‌گیرد (مگر برای اپراتورهای خاص با ID‌های 34 و 52 که سال 1403 را کامل برمی‌گرداند).
    - تاریخ‌ها برای کوئری زدن به دیتابیس به فرمت میلادی (`created_at`) و شمسی (برای فیلدهای رشته‌ای مثل `deadline`) تبدیل می‌شوند.
- **منابع داده‌ای و منطق واکشی (Data Sources):** سیستم بر اساس تنظیمات `bill` در حساب معین، منابع زیر را پیمایش می‌کند: 
    - **Pays (اسناد دریافت/پرداخت):** کوئری اصلی روی جدول `pays` با شروط پیچیده روی تاریخ ایجاد و سررسید (Deadline).
    - **Check Operations (عملیات چک):** بررسی وضعیت‌های خاص چک (واگذاری، نقد شدن، برگشتی) با کدهای معین خاص (مثل `111202` و `111203`). از متد `getCheckOperationInManualDocument` برای استخراج جزئیات سند استفاده می‌شود.
    - **Value Added (ارزش افزوده):** محاسبه مالیات بر ارزش افزوده بر اساس "اعلامیه‌ها" (Announcements) و نرخ‌های ذخیره شده در `ACCOUNTING_VALUE_ADDED`.
    - **Marketing:** (در کد اشاره شده اما منطق کامل در قطعه کدها نیست).
- **سوابق مالی (Financial Pasts - Opening/Closing):**
    - سیستم با استفاده از `StaticController::getFinancialPasts` اسناد افتتاحیه و اختتامیه سال‌های مالی درخواست شده را استخراج می‌کند.
    - این اسناد با استفاده از `array_unshift` به ابتدای لیست تراکنش‌ها اضافه می‌شوند تا مانده از قبل (Balance) صحیح باشد.
    - از **Redis** برای کش کردن مقادیر افتتاحیه/اختتامیه استفاده می‌شود تا بار روی دیتابیس کاهش یابد.
- **محاسبه مانده و تشخیص (Credit/Debit logic):**
    - متد `StaticController::calculatorCreditDebit` برای هر سطر تعیین می‌کند که مبلغ باید در ستون بدهکار بنشیند یا بستانکار. این تشخیص بر اساس "ماهیت حساب" (Nature) و نوع تراکنش (Payment/Receive) انجام می‌شود.
    - در نهایت جمع کل بدهکار، بستانکار و مانده نهایی محاسبه شده و "تشخیص" حساب (Debtor/Creditor/Neutral) تعیین می‌شود.
    - نتیجه نهایی مانده حساب در Redis با کلید `accounting:account:balance:{code}` ذخیره می‌شود.
- **فرمت‌دهی خروجی (Formatting):**
    - جزئیات متنی تراکنش (مثل "بابت فاکتور شماره...") توسط `convertPayDetailsDbToTable` تولید می‌شود.
    - اگر تراکنش مربوط به یک "رفرنس" (تور/پرواز) باشد، لینک HTML به آن رفرنس تولید می‌شود.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">id</td><td>Integer/String</td><td>بله</td><td>کد حساب مورد نظر (می‌تواند کد گروه، کل، معین یا تفضیلی باشد).</td></tr><tr><td style="direction: ltr;">type</td><td>String</td><td>خیر</td><td>نوع حساب (اختیاری، معمولا توسط خود کد شناسایی می‌شود).</td></tr><tr><td style="direction: ltr;">branch</td><td>Integer</td><td>بله</td><td>شناسه شعبه‌ای که صورت‌حساب آن درخواست شده است.</td></tr><tr><td style="direction: ltr;">json</td><td>JSON String</td><td>بله</td><td>آبجکت شامل تنظیمات فیلتر و صفحه‌بندی:   
`advanced.from`: تاریخ شروع (شمسی Y-m-d)   
`advanced.to`: تاریخ پایان (شمسی Y-m-d)   
`start`: آفست شروع (Pagination)   
`length`: طول صفحه</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "Bills": [
        {
            "serial_id": 15100, // سریال نمایشی سند
            "system_serial": 101, // شناسه سیستمی رکورد
            "datetime": "1403/01/01 00:00:00",
            "credit": 0, // مبلغ بستانکار
            "debit": 5000000, // مبلغ بدهکار
            "details": {
                "title": {
                    "html": "سند افتتاحیه سال 1403 ...", // توضیحات با فرمت HTML
                    "text": "سند افتتاحیه سال 1403 ..."
                },
                "type": {
                    "subject": "financial_past",
                    "title": "افتتاحیه"
                }
            },
            "communications": false
        },
        {
            "serial_id": 2050,
            "system_serial": 5002,
            "datetime": "1403/05/10 12:30:00",
            "credit": 1200000,
            "debit": 0,
            "details": {
                "title": {
                    "html": "سند دریافت وجه نقد | بابت ...",
                    "text": "سند دریافت وجه نقد | بابت ..."
                },
                "type": {
                    "subject": "pay",
                    "title": "سندمالی"
                }
            }
        }
        // ... سایر ردیف‌ها
    ],
    "Total": {
        "credit": 1200000,
        "debit": 5000000,
        "balance": 3800000,
        "diagnosis": "Debtor" // وضعیت نهایی: Debtor (بدهکار)، Creditor (بستانکار)، Neutral (تراز)
    }
}
```

# POST /v2/account/bill2

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/account/bill2</td><td style="direction: ltr; padding: 10px;">AccountingController@accountBill2</td></tr></tbody></table>

</div></div>### شرح عملکرد دقیق (Deep Logic Analysis)

این متد صورت‌حساب، برخلاف نسخه ساده قبلی، دارای منطق "تفسیر و تجمیع" است که وابستگی شدیدی به توابع استاتیک کمکی دارد:

<div id="bkmrk-%D8%A8%D8%A7%DA%AF-%D8%AD%DB%8C%D8%A7%D8%AA%DB%8C-%28bug-alert" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **<span style="color: red;">باگ حیاتی (Bug Alert):</span>** همچنان تاکید می‌کنم خط اول تابع حاوی `dd($request->all())` است که باید حذف شود.
- **1. استانداردسازی زمان (Time Normalization):**   
    ورودی‌های تاریخ توسط تابع `checkDatetime` استاندارد می‌شوند تا فرمت‌های مختلف (با جداکننده یا بدون آن) به یک فرمت واحد Y-m-d تبدیل شوند. این کار برای کوئری‌های دقیق روی Redis و دیتابیس حیاتی است.
- **2. کشینگ توضیحات (Redis Description Caching):**   
    داخل حلقه پردازش تراکنش‌ها، سیستم ابتدا Redis را چک می‌کند (`Redis::get('accountingpays' . $item->id)`).   
    اگر توضیحات سند قبلاً ساخته نشده باشد، تابع سنگین `convertPayDetailsDbToTable` صدا زده می‌شود و خروجی آن (شامل لینک‌های HTML و متن فرمت شده) در Redis ذخیره می‌شود تا در درخواست‌های بعدی سرعت بالا برود.
- **3. تجمیع هوشمند کارتخوان‌ها (POS Aggregation Logic):**   
    این مهمترین تفاوت است. اگر نوع پرداخت `pos` (کارتخوان) باشد: 
    - تراکنش‌ها به لیست اصلی `Bills` اضافه **نمی‌شوند**.
    - در عوض، در آرایه `$PosTotal` بر اساس **تاریخ روز** جمع زده می‌شوند.
    - در انتهای پردازش، یک آیتم واحد برای هر روز ساخته می‌شود که عنوان "سند مجموع کارتخوان‌های..." دارد و ریز تراکنش‌ها داخل فیلد `subset` قرار می‌گیرند (به نمونه خروجی دقت کنید).
- **4. محاسبه مانده از قبل (Financial Pasts):**   
    توسط تابع `getFinancialPasts`، مانده حساب از ابتدای تاریخ بازه انتخابی محاسبه می‌شود. این تابع هوشمندانه بررسی می‌کند که آیا "سند دستی افتتاحیه" وجود دارد یا باید تمام تراکنش‌های سال‌های قبل را جمع بزند.
- **5. کارمزدها (Wage Handling):**   
    اگر تراکنش شامل کارمزد بانکی باشد (`wage > 0`)، یک ردیف جداگانه در صورت‌حساب با عنوان "کارمزد نقل و انتقال بانکی" و تایپ `wage` درج می‌شود.

</div>### ساختار خروجی (Refined Response)

با توجه به فایل جدید، ساختار خروجی شامل فیلد `subset` برای تراکنش‌های تجمیعی است:

```
{
    "Bills": [
        {
            // نمونه تراکنش معمولی
            "serial_id": 15102,
            "system_serial": 204,
            "datetime": "1403/02/15 10:00:00",
            "credit": 0,
            "debit": 2500000,
            "details": {
                "title": {
                    "html": "سند پرداخت پایا...",
                    "text": "سند پرداخت پایا..."
                },
                "type": {
                    "subject": "pay",
                    "title": "سندمالی"
                }
            }
        },
        {
            // نمونه تراکنش تجمیعی (POS) - کشف شده در فایل جدید
            "serial_id": "1403021601", // ID ترکیبی از تاریخ
            "system_serial": "1403021601",
            "datetime": "1403/02/16 00:00:00",
            "credit": 50000000, // جمع کل کارتخوان‌های آن روز
            "debit": 0,
            "details": {
                "title": {
                    "html": "سند مجموع کارتخوان های 1403/02/16",
                    "text": "سند مجموع کارتخوان های 1403/02/16"
                },
                "type": {
                    "subject": "pos",
                    "title": "سند کارتخوان"
                }
            },
            "subset": [ // <--- بخش جدید اضافه شده
                {
                    "id": 5001,
                    "amount": 20000000,
                    "description": "تراکنش اول..."
                },
                {
                    "id": 5002,
                    "amount": 30000000,
                    "description": "تراکنش دوم..."
                }
            ]
        }
    ],
    "Total": {
        "credit": 50000000,
        "debit": 2500000,
        "balance": 47500000,
        "diagnosis": "Creditor"
    }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### وابستگی‌های سیستمی (System Dependencies)

عملکرد صحیح این روت به وجود و صحت توابع زیر در فایل‌های Helper وابسته است:

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D8%AA%D8%A7%D8%A8%D8%B9-%D9%86%D9%82%D8%B4-%DA%A9%D9%84%DB%8C%D8%AF%DB%8C-s" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><tbody><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام تابع</th><th style="padding: 8px;">نقش کلیدی</th></tr><tr><td dir="ltr">StaticController::getAccountingTitles</td><td>تشخیص سطح حساب (کل/معین/تفضیلی) برای تصمیم‌گیری در مورد نوع کوئری.</td></tr><tr><td dir="ltr">StaticController::convertPayDetailsDbToTable</td><td>تولید HTML لینک‌دار برای رفرنس‌ها و تشخیص وضعیت چک‌ها.</td></tr><tr><td dir="ltr">StaticController::int2DateTime</td><td>تبدیل اعداد دیتابیسی (مثل 14030215) به رشته نمایشی (1403/02/15).</td></tr></tbody></table>

</div>

# POST /v2/currencies/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/currencies/list</td><td style="direction: ltr; padding: 10px;">AccountingController@currencies</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه ارائه لیست ارزهای موجود در سیستم را دارد و داده‌های استاتیک دیتابیس را با داده‌های لحظه‌ای (نرخ بازار) ترکیب می‌کند.

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%DB%8C%D9%86%DA%AF-%D8%A7%D9%88%D9%84%DB%8C%D9%87-%28data" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **فیلترینگ اولیه (Database Query):**
    - تنها ارزهایی که وضعیت فعال دارند (`status = 1`) انتخاب می‌شوند.
    - اگر پارامتر `selected` ارسال شود، فقط ارزهایی که تیک "انتخاب شده" (Select) دارند برگردانده می‌شوند (مخصوص نمایش‌های خلاصه).
- **ادغام با نرخ بازار (Redis Integration):**
    - سیستم کل نرخ‌های بازار را از کلید ردیس `application:market_rate` دریافت می‌کند.
    - بر اساس فیلد `icon` (که به عنوان کد ارز مثل USD, EUR تفسیر می‌شود)، نرخ لحظه‌ای پیدا می‌شود.
    - **نکته محاسباتی:** نرخ دریافت شده از ردیس در عدد **10** ضرب شده و گرد می‌شود (احتمالاً جهت تبدیل واحد پولی یا استانداردسازی نمایش).

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">selected</td><td>Boolean / Integer</td><td>خیر</td><td>اگر ارسال شود (مثلاً 1)، فقط ارزهای منتخب (Selected) را برمی‌گرداند. در غیر این صورت همه ارزهای فعال نمایش داده می‌شوند.</td></tr></tbody></table>

</div></div>### نمونه خروجی (Response)

```
{
    "status": true,
    "time": 1715765000, // Unix Timestamp
    "data": [  // (فقط یک مثال جهت فهم بهتر میباشد)
        {
            "id": 1,
            "title": "دلار آمریکا",
            "icon": "USD",
            "symbol": "$",
            "select": 1,
            "status": 1,
            "rate": {
                "value": 580000, // (نرخ ردیس * 10)
                "datetime": "2024-05-15 10:30:00" // زمان آخرین آپدیت نرخ
            }
        },
        {
            "id": 2,
            "title": "یورو",
            "icon": "EUR",
            "symbol": "€",
            "select": 0,
            "status": 1,
            "rate": {
                "value": 620000,
                "datetime": "2024-05-15 10:30:00"
            }
        }
    ]
}

```

# POST /v2/accounts/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounts/list</td><td style="direction: ltr; padding: 10px;">AccountingController@accountsList</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد لیست حساب‌های بانکی/مالی فعال مربوط به شعبه کاربر جاری را برمی‌گرداند و اطلاعات حساب را با اطلاعات بانک عامل ادغام می‌کند.

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%DB%8C%D9%86%DA%AF-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%3A-%D9%81%D9%82" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **فیلترینگ داده‌ها:**
    - فقط حساب‌های فعال (`status = 1`).
    - فقط حساب‌های متعلق به شعبه جاری کاربر (`branch = request->branch`).
- **الحاق اطلاعات بانک (Left Join):**   
    جدول حساب‌ها (`accounting_accounts`) با جدول بانک‌ها (`accounting_banks`) الحاق می‌شود تا نام فارسی بانک و لوگوی آن دریافت شود.   
    *نکته:* فیلد `title` در خروجی، در واقع همان `accounting_banks.title_fa` است (مگر اینکه خود جدول اکانت هم فیلد تایتل داشته باشد که باعث هم‌پوشانی می‌شود).

</div>### ساختار خروجی (Response)

<span style="color: red; font-weight: bold;">توجه:</span> برخلاف سایر متدها، این متد مستقیماً یک آرایه (Array) برمی‌گرداند و فاقد کلیدهای استاندارد `status` یا `data` است.

```
[
    {
        "id": 105,
        "branch": 2,
        "bank": 12, // شناسه بانک در جدول accounting_banks
        "account_number": "123-456-789",
        "sheba": "IR000000000000000000",
        "status": 1,
        "title": "بانک ملت", // برگرفته از title_fa جدول بانک‌ها
        "logo": "mellat.png", // برگرفته از جدول بانک‌ها
        "balance": 5000000, // سایر فیلدهای جدول اکانت...
        "pos_device": 1
    },
    {
        "id": 106,
        "branch": 2,
        "bank": 4,
        "title": "بانک پاسارگاد",
        "logo": "pasargad.png",
        // ...
    }
]
```

# GET /v2/accounting/account

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account</td><td style="direction: ltr; padding: 10px;">AccountingController@accountIndex</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد جزئیات کامل یک حساب تفضیلی/بانکی خاص را برمی‌گرداند. تفاوت اصلی این متد با لیست‌گیری، در نرمال‌سازی (Normalize) داده‌ها و دسته‌بندی اطلاعات در آبجکت‌های تو در تو است.

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%28join" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **دریافت اطلاعات (Join Query):**   
    اطلاعات از جدول `accounting_accounts` دریافت شده و با جدول `accounting_banks` جوین می‌شود تا نام و لوگوی بانک رزرو شود.
- **ترجمه وضعیت‌ها (Status Mapping):**
    - **نوع حساب (type):** اگر مقدار دیتابیس `2` باشد، به عنوان `cash` (صندوق) و در غیر این صورت `bank` خروجی داده می‌شود.
    - **واحد پولی (currency):** اگر `0` باشد، `rials` و در غیر این صورت `currency` (ارزی) در نظر گرفته می‌شود.
- **ساختار درختی (Nested Objects):**   
    اطلاعات مربوط به درگاه پرداخت (`gateway`)، دستگاه پوز (`pos`)، بانک (`bank`) و شعبه (`branch`) در آبجکت‌های مجزا دسته‌بندی می‌شوند تا پارس کردن آن در فرانت‌اند ساده‌تر باشد.

</div>### پارامترهای ورودی (Input Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; border-collapse: collapse; text-align: right;"><thead><tr style="background: #f4f4f4;"><th style="padding: 8px;">نام پارامتر</th><th style="padding: 8px;">نوع</th><th style="padding: 8px;">الزامی؟</th><th style="padding: 8px;">توضیحات</th></tr></thead><tbody><tr><td style="direction: ltr;">id</td><td>Integer</td><td>بله</td><td>شناسه یکتای حساب مورد نظر در جدول accounting\_accounts.</td></tr></tbody></table>

</div></div>### نمونه خروجی موفق (Success Response)

```
{
    "status": true,
    "time": 1715768000,
    "data": {
        "id": 15,
        "bank": {
            "id": 3,
            "title": "بانک ملی ایران",
            "logo": "melli.png"
        },
        "account_number": "010555666777", // مپ شده از فیلد number
        "card": "6037991122334455",
        "sheba": "IR55017000000010555666777",
        "check": 1, // وضعیت دسته‌چک (دارد/ندارد)
        "type": "bank", // یا "cash"
        "currency": "rials", // یا "currency"
        "branch": {
            "code": "1245",
            "title": "شعبه مرکزی"
        },
        "pos": {
            "code": "998877", // سریال دستگاه پوز
            "status": true   // آیا متصل است؟
        },
        "gateway": {
            "data": "merchant_id_example", // اطلاعات اتصال درگاه
            "status": true
        },
        "status": 1,
        "created_at": "2023-01-01 12:00:00"
    }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### نمونه خروجی خطا (Error Response)

در صورت بروز هرگونه خطا (مانند پیدا نشدن رکورد یا خطای دیتابیس):

```
{
    "status": false,
    "time": 1715768005,
    "code": 0, // کد خطا
    "message": "Attempt to read property \"id\" on null", // متن خطا
    "trace": [...] // جزئیات فنی برای دیباگ
}
```

# POST /v2/accounting/account

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account</td><td style="direction: ltr; padding: 10px;">AccountingController@accountStore</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه ثبت یک حساب بانکی یا صندوق جدید در سیستم را بر عهده دارد. برخلاف متد نمایش، این متد داده‌های دریافت شده از کلاینت را به فرمت عددی و دیتابیسی تبدیل (Reverse Mapping) می‌کند.

<div id="bkmrk-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-%D8%B3%D8%B1%DB%8C%D8%A7%D9%84-%28serial-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **تولید سریال (Serial Generation):**   
    شناسه سریال حساب به صورت خودکار توسط تابع کمکی `StaticController::getSerialId` بر اساس نوع موجودیت ("account") و شعبه مربوطه تولید می‌شود.
- **تبدیل داده‌ها (Data Mapping):**
    - **نوع حساب:** ورودی `cash` به `2` و سایر مقادیر به `1` تبدیل می‌شوند.
    - **ارز:** ورودی `rials` به `0` و سایر مقادیر به `1` تبدیل می‌شوند.
    - **وضعیت‌ها:** فیلدهای بولین (مانند `check`, `pos`, `online`) در صورت وجود یا true بودن به `1` و در غیر این صورت به `null` تبدیل می‌شوند.
- **ذخیره‌سازی (Database Insert):**   
    داده‌ها در جدول `accounting_accounts` درج می‌شوند. تاریخ ایجاد و ویرایش نیز با زمان فعلی سرور تنظیم می‌شود.

</div>### پارامترهای ورودی (JSON Body)

ساختار جیسون ارسالی باید به شکل زیر باشد:

```
{
    "branch": 2,               // شناسه شعبه
    "bank": 12,                // شناسه بانک از لیست بانک‌ها
    "account_number": "123456",// شماره حساب
    "card": "6037...",         // شماره کارت (اختیاری)
    "sheba": "IR00...",        // شماره شبا (اختیاری)
    "type": "bank",            // نوع: "bank" یا "cash"
    "currency": "rials",       // ارز: "rials" یا "currency"
    "check": true,             // آیا دسته چک دارد؟
    "status": 1,               // وضعیت فعال/غیرفعال
    "branch": {                // آبجکت اطلاعات شعبه
        "code": "101",
        "title": "مرکزی"
    },
    "pos": {                   // تنظیمات پوز
        "status": true,
        "code": "990011"       // سریال پوز
    },
    "gateway": {               // تنظیمات درگاه آنلاین
        "status": false,
        "data": null
    }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خروجی (Response)

#### موفقیت (Success):

```
{
    "status": true,
    "time": 1715772000
}
```

#### خطا (Error):

در صورت بروز خطای سیستمی (مثلاً دیتابیس یا فرمت داده):

```
{
    "status": false,
    "time": 1715772005,
    "code": 0,
    "message": "SQLSTATE[23000]: Integrity constraint violation...",
    "trace": [...]
}
```

# PUT /v2/accounting/account

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">PUT</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account</td><td style="direction: ltr; padding: 10px;">AccountingController@accountUpdate</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد اطلاعات یک حساب موجود را به طور کامل بازنویسی (Full Update) می‌کند. شناسه حساب از طریق بدنه درخواست (Body) دریافت می‌شود.

<div id="bkmrk-%D9%87%D8%AF%D9%81%E2%80%8C%DA%AF%DB%8C%D8%B1%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%3A-%D8%B9%D9%85%D9%84%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **هدف‌گیری رکورد:** عملیات آپدیت بر اساس فیلد `id` ارسال شده در JSON انجام می‌شود.
- **بازنویسی کامل:** تمام فیلدهای قابل ویرایش (اطلاعات بانکی، نوع حساب، وضعیت‌ها و تنظیمات درگاه/پوز) با مقادیر جدید جایگزین می‌شوند.
- **به‌روزرسانی زمان:** فیلد `updated_at` به زمان لحظه‌ای سرور تغییر می‌کند.
- **نکته مهم:** فیلدهای سیستمی و ثابت مانند `serial` (شماره سریال سیستمی) و `created_at` و `branch` (شناسه اصلی شعبه) در این متد تغییر نمی‌کنند.

</div>### پارامترهای ورودی (JSON Body)

**هشدار:** تمام فیلدهای زیر الزامی هستند. حتی اگر تغییری نکرده‌اند باید ارسال شوند، در غیر این صورت ممکن است با خطای سیستمی مواجه شوید.

```
{
    "id": 15,                  // شناسه حساب (الزامی برای پیدا کردن رکورد)
    "branch": {                // آبجکت شعبه (الزامی)
        "code": "101",
        "title": "شعبه مرکزی"
    },
    "bank": 12,
    "account_number": "987654321",
    "card": "6037...",
    "sheba": "IR99...",
    "type": "bank",            // یا "cash"
    "currency": "rials",       // یا "currency"
    "check": true,
    "status": 1,
    "pos": {
        "status": true,
        "code": "NEW_POS_SERIAL"
    },
    "gateway": {
        "status": false,
        "data": null
    }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خروجی (Response)

#### موفقیت (Success):

```
{
    "status": true,
    "time": 1715775000
}
```

#### نکته امنیتی/منطقی:

اگر `id` ارسال شده وجود نداشته باشد، سیستم همچنان `status: true` برمی‌گرداند اما عملاً هیچ تغییری در دیتابیس رخ نداده است (Silent Failure).

#### خطا (Error):

در صورت ارسال ناقص داده‌ها (مثلاً حذف آبجکت branch):

```
{
    "status": false,
    "time": 1715775005,
    "code": 0,
    "message": "Trying to access array offset on value of type null",
    "trace": [...]
}
```

# DELETE /v2/accounting/account

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">DELETE</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account</td><td style="direction: ltr; padding: 10px;">AccountingController@accountDelete</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه حذف فیزیکی (Hard Delete) یک حساب از جدول `accounting_accounts` را دارد.

<div id="bkmrk-%D9%86%D8%AD%D9%88%D9%87-%D8%AD%D8%B0%D9%81%3A-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%85%D9%86%D8%B7%D8%A8" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **نحوه حذف:** رکورد منطبق با `id` ارسالی مستقیماً از دیتابیس پاک می‌شود.
- **هشدار مهم:** این عملیات غیرقابل بازگشت است. هیچ بررسی‌ای مبنی بر اینکه آیا این حساب دارای تراکنش مالی، چک پاس نشده یا اسناد حسابداری است، در سطح کد انجام نمی‌شود. (وابسته به تنظیمات دیتابیس).

</div>### پارامترهای ورودی (JSON Body)

با وجود اینکه متد از نوع DELETE است، پارامتر `id` در بدنه درخواست (Body) انتظار می‌رود:

```
{
    "id": 15  // شناسه حسابی که باید حذف شود
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خروجی (Response)

#### موفقیت (Success):

```
{
    "status": true,
    "time": 1715779000
}
```

#### خطا (Error):

ممکن است در صورت وجود وابستگی‌های دیتابیسی (Foreign Keys) خطای Integrity Violation رخ دهد:

```
{
    "status": false,
    "time": 1715779005,
    "code": "23000",
    "message": "SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row...",
    "trace": [...]
}
```

# GET /v2/wallet/balance

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/v2/wallet/balance</td><td style="direction: ltr; padding: 10px;">AccountingController@balanceWallet</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه محاسبه تراز کیف پول (Wallet) را بر عهده دارد. بر اساس کدهای فعلی، این متد **صرفاً موجودی کیف پول شعبه (ERP)** را محاسبه می‌کند.

<div id="bkmrk-%D9%85%D9%86%D8%B7%D9%82-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87%3A-%D8%AC%D9%85%D8%B9-%D8%B3%D8%AA%D9%88" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **منطق محاسبه:** جمع ستون‌های `credit` (بستانکار) و `debit` (بدهکار) از جدول `wallet`.
- **فیلترها:**
    - رکوردهایی که `status` آن‌ها برابر 2 (احتمالا حذف شده/لغو شده) نباشد.
    - فیلد `operator_type` برابر با **'erp'** باشد (ثابت در کد).
    - فیلد `branch` برابر با شناسه ارسالی (یا شناسه شعبه توکن جاری) باشد.
- **تشخیص ماهیت (Diagnosis):** سیستم به صورت خودکار وضعیت تراز را به یکی از سه حالت `creditor` (بستانکار/مثبت)، `debtor` (بدهکار/منفی) یا `neutral` (بی‌حساب/صفر) تعیین می‌کند.

</div>### پارامترهای ورودی (Query String)

```
?type=office       // (بلااستفاده) به دلیل باگ داخلی نادیده گرفته می‌شود
&id=101            // (اختیاری) شناسه شعبه. اگر ارسال نشود از توکن کاربر خوانده می‌شود
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خروجی (Response)

#### موفقیت (Success):

ساختار خروجی استاندارد شامل `payload` (داده اصلی) و `meta` (اطلاعات متا) است.

```
{
    "payload": {
        "credit": 15000000,       // مجموع ورودی‌ها
        "debit": 5000000,         // مجموع خروجی‌ها
        "balance": 10000000,      // مانده (credit - debit)
        "diagnosis": "creditor"   // وضعیت: creditor | debtor | neutral
    },
    "meta": {
        "timestamp": 1715780000
    }
}
```

#### خطا (Error):

```
{
    "status": false,
    "time": 1715780005,
    "message": "Error message here...",
    "trace": [...]
}
```

# POST /v2/wallet/check

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/wallet/check</td><td style="direction: ltr; padding: 10px;">AccountingController@checkWallet</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای **بررسی کفایت موجودی** (Balance Check) قبل از انجام تراکنش استفاده می‌شود. برخلاف متد `balance` که فقط گزارش می‌دهد، این متد معمولاً یک پاسخ Boolean یا آبجکت وضعیت برمی‌گرداند که آیا تراکنش با مبلغ مشخص شده مجاز است یا خیر.

<div id="bkmrk-%D9%87%D8%AF%D9%81%3A-%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D8%AB%D8%A8%D8%AA-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **هدف:** جلوگیری از ثبت سفارش/تراکنش برای حساب‌های فاقد موجودی.
- **منطق:** پارامترهای نوع حساب، شناسه و مبلغ درخواستی به متد داخلی `getCheckWallet` ارسال می‌شود.
- **نقص امنیتی:** در حال حاضر هیچ اعتبارسنجی روی مبلغ (مثلاً عدم ارسال عدد منفی) انجام نمی‌شود.

</div>### پارامترهای ورودی (JSON Body)

تمامی فیلدها الزامی هستند:

```
{
    "type": "office",      // نوع کیف پول (office, user, colleague)
    "id": 101,             // شناسه مالک کیف پول
    "amount": 5000000      // مبلغ تراکنش مورد نظر (به ریال)
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خروجی (Response)

#### سناریوی ۱: موجودی کافی است

```
{
    "status": true,
    "data": true           // یا آبجکتی که تایید می‌کند موجودی کافی است
}
```

#### سناریوی ۲: موجودی ناکافی است

نکته: در این حالت هم `status` کلی پاسخ `true` است (چون درخواست بررسی با موفقیت انجام شده)، اما `data` منفی است.

```
{
    "status": true,
    "data": false          // یا پیامی مبنی بر کمبود موجودی
}
```

#### خطا (Error):

از آنجایی که بلوک `try-catch` وجود ندارد، در صورت بروز خطا در دیتابیس یا ورودی‌ها، ساختار JSON استاندارد بازگردانده نمی‌شود و ممکن است با خطای سیستمی (Exception Trace) مواجه شوید.

# GET /v2/wallet/transactions

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/v2/wallet/transactions</td><td style="direction: ltr; padding: 10px;">AccountingController@transactionsWallet</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای دریافت **لیست تراکنش‌های کیف پول** استفاده می‌شود و داده‌ها را پس از پردازش سنگین (شامل joinهای متعدد، تبدیل داده‌ها، تعیین subject/object/operator، لینک‌دهی رزرو و فاکتور، و ساخت ساختار نهایی) برمی‌گرداند.

<div id="bkmrk-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C-%28paginatio" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- صفحه‌بندی (Pagination) به صورت دستی با پارامترهای `start` و `length` انجام می‌شود.
- اگر پارامتر `branch != 1` باشد، فقط تراکنش‌های همان شعبه برگشت داده می‌شوند.
- اگر `branch == 1` باشد، **تمام تراکنش‌های کل سیستم** برگردانده می‌شود (نقص امنیتی جدی).
- برای هر رکورد بین 5 تا 8 کوئری دیتابیس زده می‌شود (N+1 Query) که در مقیاس بزرگ بسیار کند است.

</div>### پارامترهای ورودی (Query + Body)

#### Query Params

```
?branch=101
```

<div id="bkmrk-branch%3A-%D8%A7%DA%AF%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-1-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **branch**: اگر مقدار 1 باشد → هیچ فیلتری اعمال نمی‌شود و کل داده‌ها خوانده می‌شوند.

</div>#### Body Params (paginate)

```
{
  "paginate": {
    "start": 0,
    "length": 20
  }
}
```

<div id="bkmrk-start%3A-%D8%B9%D8%AF%D8%AF-offset-le" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **start**: عدد offset
- **length**: تعداد آیتم در هر صفحه (اگر صفر باشد → خطای تقسیم بر صفر)

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "items": [
    {
      "id": 10023,
      "subject": "خرید از هاب توسط دفتر ایکس",
      "object": "دفتر ایکس (1023)",
      "credit": 500000,
      "debit": 0,
      "description": "متن توضیحات | <a href="https://docs.airplus.app/...">رفرنس ...</a>",
      "operator": {
          "id": 4,
          "text": "1234 - علی رضایی",
          "query": { ... }
      },
      "service": "دفتر سرویس",
      "sub_service": "دفتر زیرسرویس",
      "details": { ... },
      "confirm_at": "2024-06-15 12:11:05",
      "confirm_note": null,
      "confirm_by": { ... }
    }
  ],
  "meta": {
    "timestamp": 1715780023,
    "table": {
      "total": 200,
      "per_page": 20,
      "current_page": 1,
      "last_page": 10,
      "from": 1,
      "to": 20
    }
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خطا (Error)

در صورت بروز Exception (مثلاً موارد زیر):

<div id="bkmrk-%D9%86%D8%A8%D9%88%D8%AF%D9%86-%D9%81%DB%8C%D9%84%D8%AF-paginate-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- نبودن فیلد paginate
- صفر بودن length
- خراب بودن داده Redis
- وجود نداشتن رزرو/فاکتور/اپراتورهای مورد نیاز
- N+1 Query timeout

</div>```
{
  "error": {
    "code": 500,
    "message": "Exception message...",
    "trace": [ ... ]
  }
}
```

`

# POST /v2/accounting/connections/store

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/store</td><td style="direction: ltr; padding: 10px;">AccountingController@storeConnection</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد وظیفه **ایجاد یک اتصال (Connection)** و گروه‌بندی چندین رکورد مالی مختلف (مثل سندهای پرداختی، اقلام فاکتور، چک‌ها و سوابق مالی) تحت یک شناسه واحد را دارد.

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%DB%8C%DA%A9-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AC%D8%AF%DB%8C%D8%AF-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">1. ابتدا یک رکورد جدید در جدول `connections` ایجاد می‌کند. 
    - شماره سریال (`serial`) با استفاده از تابع کمکی `StaticController::getSerialId` تولید می‌شود (این تابع آخرین سریال شعبه را می‌خواند و یکی اضافه می‌کند).
2. سپس روی آرایه ورودی `items` حلقه می‌زند و بر اساس `type` هر آیتم، جدول مربوطه را آپدیت می‌کند: 
    - اگر نوع `pay`, `wage`, `manual_document` باشد → جدول **pays** آپدیت می‌شود.
    - اگر نوع `fitem` باشد → جدول **factor\_items** آپدیت می‌شود.
    - اگر نوع `financial_past` باشد → جدول **financial\_pasts** آپدیت می‌شود.
    - اگر نوع `check` باشد → جدول **check\_operations** آپدیت می‌شود.
3. در رکوردهای هدف، فیلد `relationship` برابر با ID کانکشن جدید و فیلد `updated_at` برابر با زمان حال تنظیم می‌شود.

</div>### پارامترهای ورودی (JSON Body)

```
{
  "branch": 101,
  "type": "merge_doc",    // نوع کانکشن
  "id": 500,              // شناسه آبجکت اصلی (Object ID)
  "items": [
    { "id": 101, "type": "pay" },
    { "id": 205, "type": "check" },
    { "id": 12, "type": "fitem" }
  ]
}
```

<div id="bkmrk-branch%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%B9%D8%A8%D9%87-%28" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- **branch**: شناسه شعبه (جهت تولید سریال).
- **items**: آرایه‌ای از اشیاء که هر کدام شامل `id` رکورد هدف و `type` آن هستند.

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1715780023
}
```

#### خطا (Error)

مدیریت خطای خاصی پیاده‌سازی نشده است. اگر دیتابیس خطا دهد (مثلاً ID اشتباه باشد یا اتصال قطع شود)، خطای استاندارد لاراول (500) باز میگردد.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>

# POST /v2/accounting/connections/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"><div><table border="1" style="width: 100%; margin: auto; border-collapse: collapse; text-align: center; border: 1px solid #ddd;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/update</td><td style="direction: ltr; padding: 10px;">AccountingController@updateConnection</td></tr></tbody></table>

</div></div>### شرح عملکرد (Functionality)

این متد برای **ویرایش یک Connection موجود** استفاده می‌شود. نوع عملیات وابسته به دو آرایه است:

<div id="bkmrk-items_add%3A-%D8%A2%DB%8C%D8%AA%D9%85%E2%80%8C%D9%87%D8%A7%DB%8C%DB%8C" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- `items_add`: آیتم‌هایی که باید به Connection اضافه شوند
- `items_remove`: آیتم‌هایی که باید از Connection جدا شوند

</div>برای هر آیتم، فیلد `relationship` در جدول مربوطه یا به مقدار `connection_id` آپدیت می‌شود یا **null** می‌گردد.

نوع هر آیتم، جدول مقصد را تعیین می‌کند:

<div id="bkmrk-pay%2C-wage-%E2%86%92-%D8%AC%D8%AF%D9%88%D9%84-pay" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- `pay`, `wage` → جدول `pays`
- `fitem` → جدول `factor_items`
- `financial_past` → جدول `financial_pasts`
- `check` → جدول `check_operations`

</div>### پارامترهای ورودی (JSON Body)

```
{
  "connection_id": 42,
  "items_add": [
    { "type": "pay", "id": 1001 },
    { "type": "check", "id": 55 },
    { "type": "fitem", "id": 990 }
  ],
  "items_remove": [
    { "type": "financial_past", "id": 81 },
    { "type": "pay", "id": 402 }
  ]
}

```

فیلدهای اجباری:

<div id="bkmrk-connection_id-%28int%29-" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- `connection_id` (int)
- `items_add` (آرایه آیتم‌ها)
- `items_remove` (آرایه آیتم‌ها)

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;"></div>### خطا (Error)

اینجا هیچ مدیریت خطا، اعتبارسنجی یا کنترل وجود ندارد. اگر:

<div id="bkmrk-connection_id-%D8%A7%D8%B4%D8%AA%D8%A8%D8%A7%D9%87" style="direction: rtl; font-family: Vazir,Tahoma; text-align: justify; line-height: 1.85;">- `connection_id` اشتباه باشد،
- آیتم حذف‌شونده قبلاً جدا شده باشد،
- آیتم افزوده‌شونده وجود نداشته باشد،
- نوع (`type`) اشتباه ارسال شود،

</div>باز هم متد بدون هیچ هشدار یا Exception، پاسخ موفق بازمی‌گرداند. (Silent Failure)

# POST /v2/accounting/connections/trash

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/trash</td><td style="direction: ltr; padding: 10px;">AccountingController@trashConnection</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این متد برای **حذف نرم (Soft Delete)** یک Connection استفاده می‌شود. در این عملیات:

<div id="bkmrk-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%85%D8%B1%D8%A8%D9%88%D8%B7-%D8%A8%D9%87-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- رکورد مربوط به اتصال در جدول `connections` با مقدار `status = 2` علامت‌گذاری می‌شود.
- تمام رکوردهایی که قبلاً به این Connection متصل بوده‌اند، در جداول زیر از آن جدا می‌شوند: 
    - `pays`
    - `factor_items`
    - `financial_pasts`
    - `check_operations`
- فیلد `relationship` در تمام این جداول برابر با مقدار `null` قرار می‌گیرد.

</div>این عملیات هیچ رکوردی را حذف نمی‌کند و فقط ارتباط آن‌ها با Connection را قطع می‌کند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

```
{
  "connection_id": 42
}
```

<div id="bkmrk-connection_id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- **connection\_id**: شناسه اتصال موردنظر برای حذف نرم.

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### خطاها (Errors)

برای این مسیر مدیریت خطای اختصاصی پیاده‌سازی نشده است.

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA%DB%8C-%DA%A9%D9%87-connecti" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- در صورتی که `connection_id` وجود نداشته باشد، دیتابیس هیچ ردیفی را آپدیت نمی‌کند.
- در صورت بروز خطای دیتابیس (DB Exception)، پاسخ خطای استاندارد سرور برگردانده می‌شود.

</div>

# POST /v2/accounting/connections/trash

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/trash</td><td style="direction: ltr; padding: 10px;">AccountingController@trashConnection</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این متد برای **حذف نرم (Soft Delete)** یک Connection استفاده می‌شود. در این عملیات:

<div id="bkmrk-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%85%D8%B1%D8%A8%D9%88%D8%B7-%D8%A8%D9%87-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- رکورد مربوط به اتصال در جدول `connections` با مقدار `status = 2` علامت‌گذاری می‌شود.
- تمام رکوردهایی که قبلاً به این Connection متصل بوده‌اند، در جداول زیر از آن جدا می‌شوند: 
    - `pays`
    - `factor_items`
    - `financial_pasts`
    - `check_operations`
- فیلد `relationship` در تمام این جداول برابر با مقدار `null` قرار می‌گیرد.

</div>این عملیات هیچ رکوردی را حذف نمی‌کند و فقط ارتباط آن‌ها با Connection را قطع می‌کند.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

```
{
  "connection_id": 42
}
```

<div id="bkmrk-connection_id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- **connection\_id**: شناسه اتصال موردنظر برای حذف نرم.

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### خطاها (Errors)

برای این مسیر مدیریت خطای اختصاصی پیاده‌سازی نشده است.

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA%DB%8C-%DA%A9%D9%87-connecti" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- در صورتی که `connection_id` وجود نداشته باشد، دیتابیس هیچ ردیفی را آپدیت نمی‌کند.
- در صورت بروز خطای دیتابیس (DB Exception)، پاسخ خطای استاندارد سرور برگردانده می‌شود.

</div>

# POST /v2/accounting/connections/view

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/view</td><td style="direction: ltr; padding: 10px;">AccountingController@viewConnection</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **نمایش جزئیات کامل یک Connection** استفاده می‌شود. در این متد، نوع اتصال بررسی می‌شود و در صورتی که اتصال از نوع `leger_account` باشد، عملیات زیر انجام می‌شود:

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D8%AA%D8%A7%D8%A8%D8%B9-%D8%AF%D8%A7%D8%AE%D9%84%DB%8C-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- فراخوانی تابع داخلی **ledgerAccounts()** برای استخراج تمامی آیتم‌های مرتبط با Connection.
- گردآوری تراکنش‌ها از جداول مختلف شامل: 
    - pays
    - factor\_items
    - check\_operations
    - announcements
    - financial\_pasts (افتتاحیه)
- تولید آرایه یکپارچه شامل رکوردهای مالی، چک‌ها، رفرنس‌ها، اعلان‌ها و اسناد مرتبط.
- مرتب‌سازی نهایی نتایج بر اساس تاریخ شمسی.

</div>خروجی شامل لیست نهایی اسناد (Bills) است که برای نمایش جزئیات دفاتر حسابداری استفاده می‌شود.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

```
{
  "connection_id": 42
}
```

<div id="bkmrk-connection_id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- **connection\_id**: شناسه اتصال موردنظر جهت نمایش اسناد.

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000,
  "data": [
     { ... ledger items ... }
  ]
}
```

فیلد `data` شامل آرایه‌ای از تمام آیتم‌های دفتر حساب (Ledger) است. هر آیتم شامل فیلدهای:

<div id="bkmrk-serial_id-serial-dat" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- serial\_id
- serial
- datetime
- credit
- debit
- description (html/text)
- details (documents/title/type)
- relationship
- communications

</div>### خطاها (Errors)

این مسیر مدیریت خطای اختصاصی ندارد.

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA%DB%8C-%DA%A9%D9%87-connecti" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- در صورتی که `connection_id` یافت نشود، مقدار `$connection` تهی شده و اجرای کد ممکن است خطای سیستمی ایجاد کند.
- در صورت بروز خطای پایگاه داده، خطای عمومی سرور برگردانده می‌شود.

</div>

# POST /v2/accounting/connections/merge

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/merge</td><td style="direction: ltr; padding: 10px;">AccountingController@mergeConnection</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **ادغام دو Connection** مورد استفاده قرار می‌گیرد. عملیات به این صورت انجام می‌شود:

<div id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%D9%87%D8%A7%DB%8C%DB%8C-%DA%A9%D9%87-re" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- تمام رکوردهایی که `relationship` آنها برابر `connection_last_id` است، در جداول مختلف به `connection_current_id` منتقل می‌شوند.
- این عملیات شامل چهار جدول زیر است: 
    - `pays`
    - `factor_items`
    - `financial_pasts`
    - `check_operations`
- در هر به‌روزرسانی، مقدار `updated_at` برابر با تاریخ و زمان جاری (Carbon) ثبت می‌شود.

</div>در نهایت، مسیر صرفاً تأیید انجام عملیات را بازمی‌گرداند و اطلاعات اضافی ارسال نمی‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

```
{
  "connection_last_id": 14,
  "connection_current_id": 7
}
```

<div id="bkmrk-connection_last_id%3A-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- **connection\_last\_id**: شناسه اتصال قدیمی که باید ادغام شود.
- **connection\_current\_id**: شناسه اتصال جدید که همه رکوردها به آن منتقل می‌شوند.

</div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### خطاها (Errors)

این مسیر مدیریت خطای اختصاصی ندارد.

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%86%D8%A8%D9%88%D8%AF%D9%86-%D8%B4%D9%86%D8%A7%D8%B3%D9%87%E2%80%8C" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- در صورت نبودن شناسه‌ها در دیتابیس، عملیات آپدیت بدون خطا اجرا می‌شود.
- در صورت بروز خطای دیتابیس، پاسخ خطای عمومی سرور بازگردانده خواهد شد.

</div>

# POST /v2/accounting/connections/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/connections/list</td><td style="direction: ltr; padding: 10px;">AccountingController@listConnection</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **دریافت لیست Connections فعال** استفاده می‌شود. امکان اعمال دو نوع فیلتر ورودی وجود دارد:

<div id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- فیلتر بر اساس مقدار `serial` (فیلد `search`)
- فیلتر بر اساس شناسه `object` (فیلد `id`)

</div>پس از اعمال فیلترها، فقط رکوردهایی با `status = 1` بازگردانده می‌شوند. در مرحله بعد، برای هر Connection یک ساختار خروجی شامل id، شماره سریال محاسبه‌شده و عنوان تولید می‌شود.

شماره سریال نهایی با افزودن `1000` به مقدار اصلی تولید می‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

همه پارامترها اختیاری هستند.

```
{
  "search": "123",     // اختیاری - جستجو بر اساس serial
  "id": "45"           // اختیاری - فیلتر بر اساس object id
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### خروجی (Response)

#### موفق (Success)

```
{
  "status": true,
  "time": 1718450000,
  "data": [
    {
      "id": 12,
      "serial": 1123,
      "title": "ارتباط 1123"
    },
    ...
  ]
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;"></div>### خطاها (Errors)

این مسیر مدیریت خطای اختصاصی ندارد.

<div id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%B9%D8%AF%D9%85-%D9%88%D8%AC%D9%88%D8%AF-%D9%87%DB%8C%DA%86" style="direction: rtl; font-family: Vazir, Tahoma; text-align: justify; line-height: 1.9;">- در صورت عدم وجود هیچ Connection، مقدار `data` آرایه خالی خواهد بود.
- در صورت بروز خطای دیتابیس، پاسخ خطای عمومی سرور (5xx) برگردانده می‌شود.

</div>

# POST /v2/accounting/account/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account/update</td><td style="direction: ltr; padding: 10px;">AccountingController@updateAccountInTreeView</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **ایجاد (store)** یا **ویرایش (update)** اطلاعات حساب‌های درخت حسابداری مورد استفاده قرار می‌گیرد. در این متد سه نوع ساختار حسابداری پشتیبانی می‌شود:

<div id="bkmrk-%DA%AF%D8%B1%D9%88%D9%87-%D8%AD%D8%B3%D8%A7%D8%A8%D8%AF%D8%A7%D8%B1%DB%8C-%28group" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- گروه حسابداری (Group)
- کل حسابداری (General)
- معین حسابداری (Moeen)

</div>نوع عملیات از طریق پارامتر `action` مشخص می‌شود:

<div id="bkmrk-store%3A-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AC" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- `store`: ایجاد رکورد جدید
- `update`: ویرایش رکورد موجود

</div>ورودی‌ها در کلید `data` قرار می‌گیرند و بر اساس نوع حساب (`type`) عمل درج یا ویرایش روی یکی از جداول زیر انجام می‌شود:

<div id="bkmrk-accounting_groups-ac" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- `accounting_groups`
- `accounting_generals`
- `accounting_moeens`

</div>در صورت بروز خطا، پیام و Trace کامل استثنا بازگردانده می‌شود.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;"></div>### پارامترهای ورودی (JSON Body)

#### ساختار کلی ورودی

```
{
  "action": "store | update",
  "id": 12,                   // فقط در حالت update
  "data": {
    "type": "group | general | moeen",
    "code": "112",
    "title_fa": "عنوان فارسی",
    "title_en": "English Title",
    "nature": 1,
    "status": 1,
    "...": "سایر فیلدهای ویژه نوع حساب"
  }
}
```

#### فیلدهای تکمیلی برای نوع General

<div id="bkmrk-group" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- group

</div>#### فیلدهای تکمیلی برای نوع Moeen

<div id="bkmrk-general-nature_check" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- general
- nature\_check
- currency
- aggregation
- bill (JSON)
- detailed\_first (JSON)
- detailed\_second (JSON)
- detailed\_third (JSON)
- detailed\_fourth (JSON)

</div>### خروجی موفق (Success Response)

```
{
  "status": true,
  "time": 1718450000
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت بروز خطا (مانند خطای دیتابیس)، ساختار زیر بازگردانده می‌شود:

```
{
  "status": false,
  "time": 1718450000,
  "code": 5005,
  "message": "Exception message...",
  "trace": [ ... ]
}
```

# GET /v2/accounting/account/get

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">GET</td><td style="direction: ltr; padding: 10px;">/v2/accounting/account/get</td><td style="direction: ltr; padding: 10px;">AccountingController@getAccountInTreeView</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **دریافت جزئیات یک حساب درخت حسابداری** استفاده می‌شود. با توجه به پارامتر `type` که می‌تواند یکی از سه مقدار **group**، **general** یا **moeen** باشد، اطلاعات از جداول مرتبط استخراج شده و ساختار داده متناسب بازگردانده می‌شود.

اطلاعات بازگشتی شامل:

<div id="bkmrk-%DA%A9%D8%AF-%D8%AD%D8%B3%D8%A7%D8%A8-%28code-%2B-publ" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- کد حساب (code + public\_code)
- عنوان فارسی و انگلیسی
- ماهیت (nature)
- وضعیت (status)
- برای سطوح پایین‌تر، اطلاعات والد (parent)
- برای معین (moeen): تنظیمات اضافی مانند bill، detailed\_first و ...

</div>### پارامترهای ورودی (Query Params)

```
/v2/accounting/account/get?type=TYPE&id=ID
```

<div id="bkmrk-type-%28%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C%29%3A-%DB%8C%DA%A9%DB%8C-%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **type** (الزامی): یکی از مقادیر `group`، `general` یا `moeen`
- **id** (الزامی): شناسه حساب

</div>### خروجی موفق (Success Response)

#### نمونه پاسخ برای نوع Group

```
{
  "status": true,
  "time": 1718449999,
  "data": {
    "id": 3,
    "type": "group",
    "code": "11",
    "public_code": "11",
    "title_fa": "دارایی",
    "title_en": "Assets",
    "nature": 1,
    "status": 1
  }
}
```

#### نمونه پاسخ برای نوع General

```
{
  "status": true,
  "time": 1718449999,
  "data": {
    "parent": {
      "type": "group",
      "code": "11",
      "public_code": "11",
      "id": 3,
      "title": { "fa": "دارایی", "en": "Assets" }
    },
    "id": 18,
    "type": "general",
    "code": "02",
    "public_code": "1102",
    "title_fa": "موجودی کالا",
    "title_en": "Inventory",
    "nature": 1,
    "status": 1
  }
}
```

#### نمونه پاسخ برای نوع Moeen

```
{
  "status": true,
  "time": 1718449999,
  "data": {
    "parent": {
      "type": "general",
      "code": "02",
      "public_code": "1102",
      "id": 18,
      "title": { "fa": "موجودی کالا", "en": "Inventory" }
    },
    "id": 51,
    "type": "moeen",
    "code": "005",
    "public_code": "1102005",
    "title_fa": "موجودی انبار مرکزی",
    "title_en": "Main Warehouse",
    "nature": 1,
    "nature_check": 1,
    "currency": 1,
    "aggregation": 0,
    "bill": [ ... ],
    "detailed_first": null,
    "detailed_second": null,
    "detailed_third": null,
    "detailed_fourth": null,
    "status": 1
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت بروز خطا مانند عدم یافتن رکورد یا خطای دیتابیس:

```
{
  "status": false,
  "time": 1718449999,
  "code": 5005,
  "message": "Error message...",
  "trace": [ ... ]
}
```

# POST /v2/manual-document/update

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/update</td><td style="direction: ltr; padding: 10px;">AccountingController@updateManualDocument</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **ایجاد (add)** یا **ویرایش (update)** یک سند دستی (Manual Document) استفاده می‌شود. روش کار متد بطور خلاصه:

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D8%A8%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- ابتدا تاریخ ورودی با تنظیمات «پایان دوره مالی» در `office_config` مقایسه می‌شود.
- اگر تاریخ سند در بازه بسته مالی باشد، عملیات متوقف و پیام خطا بازگردانده می‌شود.
- در حالت `action = add`: 
    - محاسبه سال مالی از تاریخ سند
    - محاسبه سریال جدید سند دستی با `StaticController::getSerialId()`
    - ثبت رکورد جدید در جدول `manual_documents`
    - ثبت رکوردهای مرتبط در جدول `pays`
- در حالت `action = update`: 
    - به‌روزرسانی رکورد سند دستی
    - به‌روزرسانی موارد موجود در جدول `pays` یا افزودن موارد جدید
    - حذف رکوردهای پرداختی که در `data_delete` آمده‌اند

</div>در هر دو حالت، ثبت لاگ در `SystemLog` انجام می‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### پارامترهای ورودی (JSON Body)

#### ساختار کلی

```
{
  "action": "add | update",
  "id": 15,                     // فقط در حالت update
  "date": "1403-05-20",
  "description": "سند دستی",
  "status": 3,
  "currency": 1,
  "currency_fee": 0,
  "sub_serial": null,
  "data": [
    {
      "preference": "colleague-42",
      "moeen": "moeen-18",
      "credit": "0",
      "debit": "250000",
      "description": "توضیحات"
    }
  ],
  "data_delete": [ ... ]         // فقط در حالت update
}
```

#### توضیحات پارامترهای کلیدی

<div id="bkmrk-action%3A-%D8%AA%D8%B9%DB%8C%DB%8C%D9%86-%D9%86%D9%88%D8%B9-%D8%B9%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **action**: تعیین نوع عملیات. مقدار `add` برای ایجاد سند جدید و `update` برای ویرایش.
- **data\[\]**: آرایه خطوط حسابداری. هر خط شامل: 
    - preference: ترکیب نوع و شناسه؛ مثال: `colleague-42`
    - moeen: کد معین (با یا بدون پیشوند)
    - credit / debit: مبالغ با کاراکترهای فارسی/انگلیسی، که به عدد انگلیسی تبدیل می‌شوند
    - description: توضیحات خط سند
- **data\_delete**: لیست id پرداخت‌هایی که باید حذف شوند.

</div>### خروجی موفق (Success Response)

#### در حالت ایجاد (add)

```
{
  "status": true,
  "time": 1718450000,
  "message": "ثبت سند با موفقیت انجام شد. شماره: [3125, 3126]"
}
```

#### در حالت ویرایش (update)

```
{
  "status": true,
  "time": 1718450000,
  "message": "ویرایش سند با موفقیت انجام شد."
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

#### خطا در بازه زمانی بسته مالی

```
{
  "status": false,
  "time": 1718450000,
  "type": "danger",
  "code": 5007,
  "message": "ثبت و ویرایش سند دستی در بازه زمانی بسته شده امکان پذیر نمی‌باشد..."
}
```

#### خطاهای دیتابیس (Insert/Update)

```
{
  "status": false,
  "time": 1718450000,
  "code": 5006,
  "message": "SQL error message...",
  "trace": [ ... ]
}
```

# DELETE /v2/manual-document

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">DELETE</td><td style="direction: ltr; padding: 10px;">/v2/manual-document</td><td style="direction: ltr; padding: 10px;">AccountingController@trashManualDocument</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر جهت **حذف نرم (Soft Delete)** یک سند دستی (Manual Document) استفاده می‌شود. فرآیند عملکرد به صورت زیر است:

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%AA%D9%86%D8%B8%DB%8C%D9%85-%C2%AB%D9%BE%D8%A7%DB%8C%D8%A7%D9%86-%D8%AF" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- ابتدا تنظیم «پایان دوره مالی بسته» از جدول `office_config` خوانده می‌شود.
- تاریخ سند موردنظر از جدول `manual_documents` گرفته شده و با تاریخ بسته مالی مقایسه می‌شود.
- اگر تاریخ سند داخل بازه بسته مالی باشد، حذف انجام نمی‌شود و پیام خطا باز می‌گردد.
- در صورت معتبر بودن تاریخ و شناسه: 
    - فیلد `status=5` روی رکورد سند و پرداخت‌های مرتبط در جدول `pays` اعمال می‌شود.
    - زمان ویرایش با `Carbon::now()` به‌روزرسانی می‌شود.
    - لاگ عملیات از طریق `SystemLog` ثبت و وارد صف `snailJob` می‌شود.
- در نهایت پاسخ HTTP با وضعیت `204 No Content` بازگردانده می‌شود.

</div>### ورودی‌ها (Request Inputs)

**Body Parameters (JSON/Form):**

```
{
  "id": 1542
}
```

<div id="bkmrk-id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B3%D9%86%D8%AF-%D8%AF%D8%B3%D8%AA%DB%8C-%DA%A9" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **id**: شناسه سند دستی که باید حذف شود (الزامی)
- **branch**: شناسه شعبه جاری (به صورت خودکار توسط سیستم/توکن ارسال می‌شود)
- **operator**: اطلاعات اپراتور لاگین شده (از JWT)

</div>### خروجی موفق (Success Response)

در صورت موفقیت عملیات، پاسخ بدون بدنه با کد 204 بازگردانده می‌شود:

```
HTTP 204 No Content
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خطاها (Error Responses)

#### ۱) سند داخل دوره مالی بسته شده باشد

```
{
  "status": false,
  "time": 1718450000,
  "type": "danger",
  "code": 5007,
  "message": "حذف سند دستی در بازه زمانی بسته شده امکان پذیر نمی‌باشد. حساب ها تا تاریخ YYYY/MM بسته شده‌ است."
}
```

#### ۲) عدم ارسال شناسه معتبر

```
{
  "error": {
    "code": 1000,
    "message": "شناسه سند ارسالی صحیح نمی باشد"
  },
  "meta": {
    "timestamp": 1718450000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### نکات داخلی (Internal Logic Notes)

<div id="bkmrk-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE%E2%80%8C%D9%87%D8%A7-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D9%85%D9%82%D8%A7%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- تاریخ‌ها قبل از مقایسه با تابع `Functions::checkDatetime` نرمال‌سازی می‌شوند.
- فیلد `status=5` برای حذف نرم اسناد و پرداخت‌ها استفاده می‌شود.
- لاگ شامل: 
    - type = TrashManualDocument
    - goal = id سند
    - by = operator.id
    - ip, agent, datetime

</div>

# POST /v2/manual-document/list

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/list</td><td style="direction: ltr; padding: 10px;">AccountingController@listManualDocument</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **لیست‌گیری، جستجو، فیلتر و صفحه‌بندی** اسناد دستی در جدول `manual_documents` استفاده می‌شود. عملکرد متد به صورت زیر است:

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-json-%D8%A7%D8%B2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- دریافت ورودی JSON از کلید `json` شامل تنظیمات صفحه‌بندی و فیلتر.
- محاسبه مقدار `start` بر اساس مقدار فعلی و طول (length).
- اجرای کوئری با شروط: 
    - فیلتر تاریخ بین `from` و `to`
    - فیلتر براساس سال
    - فیلتر شماره سند (from\_document / to\_document)
    - فیلتر بر اساس branch
- بارگذاری گزارش تراز سند از Redis: 
    - `documents:balance_report:{id}`
- در صورت نبود داده در Redis، فراخوانی `getManualDocumentDetails()`.
- ساخت خروجی استاندارد شامل: 
    - serial و system\_serial
    - title و description
    - status فارسی
    - تراز مالی (financial)

</div>### پارامترهای ورودی (JSON Body)

**Body Parameter:** کل ورودی در فیلد `json` ارسال می‌شود

```
{
  "start": 0,
  "length": 20,
  "draw": 1,
  "search": {
    "from": "1403-01-01",
    "to": "1403-01-30",
    "year": "",
    "from_document": "",
    "to_document": ""
  }
}
```

<div id="bkmrk-start%3A-%D9%86%D9%82%D8%B7%D9%87-%D8%B4%D8%B1%D9%88%D8%B9-%D8%B5%D9%81%D8%AD" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **start**: نقطه شروع صفحه‌بندی
- **length**: تعداد آیتم در هر صفحه
- **draw**: شماره درخواست (برای DataTables)
- **search.from / search.to**: بازه تاریخی
- **search.year**: فیلتر سال
- **from\_document / to\_document**: بازه شماره سند (با تبدیل داخلی -1000)

</div>### خروجی موفق (Success Response)

```
{
  "status": true,
  "time": 1718450000,
  "draw": 1,
  "recordsTotal": 152,
  "recordsFiltered": 20,
  "data": [
    {
      "serial": 3125,
      "system_serial": 125,
      "type": "manual",
      "title": "سند شماره 3125 | 1403/01/25 | دستی - توضیحات",
      "date": "14030125",
      "manual_serial": false,
      "sub_serial": false,
      "description": "توضیحات سند",
      "status": "تائید نهایی",
      "financial": {
        "debit_financial_past": 0,
        "credit_financial_past": 0,
        "debit_start_period": 0,
        "credit_start_period": 0,
        "debit_during_period": 1500000,
        "credit_during_period": 900000,
        "debit_balance": 600000,
        "credit_balance": 0
      }
    }
  ]
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت بروز خطای دیتابیس یا Exception:

```
{
  "status": false,
  "time": 1718450000,
  "code": 5005,
  "message": "SQL error message...",
  "trace": [ ... ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### نکات داخلی (Internal Logic Notes)

<div id="bkmrk-%D8%B4%D9%85%D8%A7%D8%B1%D9%87-%D8%B3%D9%86%D8%AF-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%3D-se" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- شماره سند خروجی = `serial + 1000`
- تاریخ خروجی به صورت `YYYYMMDD` در فیلد date
- عنوان سند از طریق `getManualDocumentTitle()` ساخته می‌شود
- گزارش تراز مالی سند در Redis با کلید `documents:balance_report:{id}`
- بازه صفحه = `currentPage = start / length`

</div>

# POST /v2/manual-document/view

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/view</td><td style="direction: ltr; padding: 10px;">AccountingController@viewManualDocument</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای **نمایش جزئیات کامل یک سند دستی (Manual Document)** استفاده می‌شود. پس از دریافت شناسه سند:

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D8%AA%D8%A7%D8%A8%D8%B9-accoun" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- فراخوانی تابع `AccountingController::getManualDocumentDetails(id)`
- استخراج اطلاعات سند شامل: 
    - ردیف‌های سند (Moeen، طرف حساب، شرح، بدهکار، بستانکار)
    - تراز کامل سند شامل: 
        - debit\_during\_period
        - credit\_during\_period
        - debit\_balance
        - credit\_balance
- داده‌های برگشتی شامل کلیدهای `data` و `balance` هستند.

</div>### پارامترهای ورودی (Request Inputs)

**Body (JSON یا Form-Data):**

```
{
  "id": 1524
}
```

<div id="bkmrk-id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AF%D8%B1-%D8%AC" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **id**: شناسه رکورد در جدول `manual_documents` (اجباری)

</div>### خروجی موفق (Success Response)

ساختار خروجی شامل:

<div id="bkmrk-data%3A-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%DA%A9%D8%A7%D9%85%D9%84-%D8%A2%DB%8C%D8%AA" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **data**: آرایه کامل آیتم‌های سند (بسته به نوع سند متفاوت است: daily\_sales, daily\_credit\_debit, manual, opening, closing)
- **balance**: تراز نهایی سند

</div>```
{
  "status": true,
  "time": 1718450000,
  "data": [
    {
      "moeen": {
        "id": 1102,
        "title": "بانک ملت"
      },
      "preference": {
        "id": "colleague-5421",
        "title": "علی رضایی"
      },
      "preference_2": false,
      "preference_3": false,
      "preference_4": false,
      "description": "سند دستی 3125 | پرداخت نقدی",
      "credit": 0,
      "debit": 2500000
    }
  ],
  "balance": {
    "debit_financial_past": 0,
    "credit_financial_past": 0,
    "debit_start_period": 0,
    "credit_start_period": 0,
    "debit_during_period": 2500000,
    "credit_during_period": 0,
    "debit_finish_period": 2500000,
    "credit_finish_period": 0,
    "debit_balance": 2500000,
    "credit_balance": 0
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت وقوع هرگونه Exception، ساختار خطا به شکل زیر بازگردانده می‌شود:

```
{
  "status": false,
  "time": 1718450000,
  "code": 5005,
  "message": "Error message ...",
  "trace": [ ... ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خلاصه منطق داخلی getManualDocumentDetails

بسته به نوع سند:

<div id="bkmrk-daily_sales%3A-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **daily\_sales**: محاسبه از جداول فاکتور و پرداخت‌ها
- **daily\_credit\_debit**: ترکیب اقلام پرداخت و بدهکار/بستانکار
- **manual**: لیست ردیف‌های `pays` با `group = id`
- **opening / closing**: استخراج اقلام افتتاحیه/اختتامیه

</div>در پایان:

<div id="bkmrk-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%DA%AF%D8%B2%D8%A7%D8%B1%D8%B4-%D8%AA%D8%B1%D8%A7%D8%B2-%D8%AF%D8%B1-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- ذخیره گزارش تراز در Redis با کلید:   
    `documents:balance_report:{id}`
- برگشت:   
    `{ data: [...], balance: {...} }`

</div>

# POST /v2/manual-document/get

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/get</td><td style="direction: ltr; padding: 10px;">AccountingController@getManualDocument</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر جهت **دریافت تمامی جزئیات یک سند دستی** و **تمام ردیف‌های وابسته به آن** استفاده می‌شود.

فرآیند متد:

<div id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D9%88-%D8%A8%D8%A7%D8%B2%DB%8C%D8%A7%D8%A8%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- جستجو و بازیابی رکورد موردنظر از جدول `manual_documents`.
- استخراج آیتم‌های مرتبط از جدول `pays` بر اساس `group = id`.
- محاسبه مجموع: 
    - تعداد آیتم‌ها (count)
    - جمع بدهکار (debit)
    - جمع بستانکار (credit)
- استخراج جزئیات هر pay از Redis: 
    - کلید: `accounting:pays:{pay_id}`
    - درصورت نبود داده: تبدیل داده با `convertPayDetailsDbToTable`
- تبدیل شناسه‌های معین و طرف‌حساب به ساختار استاندارد با: 
    - `Functions::getDetailsItems()`
- برگشت ساختار استاندارد «document payload» شامل: 
    - system\_serial
    - serial (با +1000)
    - type
    - date
    - title با استفاده از `getManualDocumentTitle`
    - توضیحات، زیر شماره‌ها، وضعیت و جمع کل

</div>### پارامترهای ورودی (JSON Body)

**Body:**

```
{
  "id": 1524
}
```

<div id="bkmrk-id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B3%D9%86%D8%AF-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **id**: شناسه سند در جدول `manual_documents` (اجباری)

</div>### خروجی موفق (Success Response)

خروجی شامل سه بخش اصلی است:

<div id="bkmrk-items%3A-%D9%84%DB%8C%D8%B3%D8%AA-%DA%A9%D8%A7%D9%85%D9%84-%D9%BE%D8%B1%D8%AF" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **items**: لیست کامل پرداخت‌ها/دریافت‌های سند
- **payload**: اطلاعات کامل سند
- **meta**: زمان اجرا

</div>```
{
  "items": [
    {
      "id": 980211,
      "serial": 1452,
      "moeen": {
        "id": 1102,
        "serial": 220,
        "group": 4,
        "details": { ... },
        "type": { "fa": "معین", "en": "moeen" },
        "title": {
          "fa": { "simple": "بانک ملت", "html": "بانک ملت" }
        }
      },
      "preference": {
        "id": "colleague-5421",
        "serial": 5421,
        "title": "علی رضایی"
      },
      "preference_2": {
        "id": 1452,
        "title": 1452
      },
      "preference_3": false,
      "preference_4": false,
      "description": "واریز نقدی",
      "credit": 0,
      "debit": 850000
    }
  ],
  "payload": {
    "document": {
      "serial": 3125,
      "system_serial": 1524,
      "title": "سند شماره 3125 | دستی - توضیحات"
    },
    "system_serial": 1524,
    "serial": 3125,
    "type": "manual",
    "title": "سند شماره 3125 | دستی - توضیحات",
    "date": "1403/01/25",
    "manual_serial": false,
    "sub_serial": false,
    "description": "توضیحات سند",
    "total": {
      "count": 4,
      "debit": 3200000,
      "credit": 2400000
    },
    "status": 1
  },
  "meta": {
    "timestamp": 1718450000
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت بروز Exception:

```
{
  "status": false,
  "time": 1718450000,
  "code": 5005,
  "message": "Error message...",
  "trace": [ ... ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"></div>### نکات داخلی اجرای متد

<div id="bkmrk-%DA%A9%D9%84%DB%8C%D8%AF-redis-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A2%DB%8C%D8%AA%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- کلید Redis برای آیتم‌ها:   
    `accounting:pays:{id}`
- در صورت عدم وجود داده در Redis:   
    `V2CreditDebitController::convertPayDetailsDbToTable()`
- تبدیل شناسه‌ها به عنوان و ساختار کامل:   
    `Functions::getDetailsItems(type, id, "autocomplete")`
- عنوان سند از:   
    `getManualDocumentTitle()`
- serial خروجی سند = **serial + 1000**

</div>

# POST /v2/manual-document/list/{type}

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"></div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller Method</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/list/{type}</td><td style="direction: ltr; padding: 10px;">AccountingController@listTypeManualDocument</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

</div>### شرح عملکرد (Functionality)

این مسیر برای دریافت لیست ۵۰تایی اسناد دستی، افتتاحیه یا اختتامیه استفاده می‌شود. فیلتر جستجو از طریق پارامتر `json` اعمال می‌شود و خروجی شامل سریال نمایشی (+1000)، شماره سیستمی، و عنوان نهایی سند است.

<div id="bkmrk-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D8%B1%D8%B4%D8%AA%D9%87-json-%D9%88-%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- خواندن رشته `json` و استخراج مقدار `search.value`
- اگر مقدار جستجو عدد باشد → جستجو روی `serial = value - 1000`
- اگر مقدار جستجو متن باشد → LIKE روی تاریخ و توضیحات
- اعمال فیلتر `branch`
- محدودسازی نوع به: `manual`، `opening`، `closing`
- مرتب‌سازی نزولی بر اساس id
- محدودیت خروجی: ۵۰ رکورد
- تولید عنوان نهایی سند با استفاده از:   
    `getManualDocumentTitle(type, serial+1000, description, formattedDate)`

</div>### پارامترهای مسیر (Path Parameters)

<div id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-type-s" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse;"><tbody><tr style="background: #f4f4f4; font-weight: bold; text-align: center;"><td style="padding: 10px;">نام</td><td style="padding: 10px;">نوع</td><td style="padding: 10px;">توضیح</td></tr><tr><td style="padding: 10px;">type</td><td style="padding: 10px;">string</td><td style="padding: 10px;">نوع سند (manual / opening / closing)</td></tr></tbody></table>

</div>### پارامترهای ورودی (JSON Body)

**Body:**

```
{
  "json": "{\"search\": {\"value\": \"1403/01\"}}",
  "branch": 1
}
```

<div id="bkmrk-json%3A-%D8%B1%D8%B4%D8%AA%D9%87%E2%80%8C%D8%A7%DB%8C-%DA%A9%D9%87-%D8%B4%D8%A7%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **json**: رشته‌ای که شامل: 
    - **search.value**: مقدار برای اعمال فیلتر عددی یا متنی
- **branch**: شناسه شعبه

</div>### خروجی موفق (Success Response)

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

```
{
  "status": true,
  "time": 1718451600,
  "data": [
    {
      "serial": 3125,
      "system_serial": 1524,
      "title": "سند شماره 3125 | 1403/01/21 | دستی - هزینه دفتر"
    },
    {
      "serial": 3101,
      "system_serial": 1512,
      "title": "سند شماره 3101 | افتتاحیه - افتتاح سال 1403"
    }
  ]
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"></div>### خروجی خطا (Error Response)

در صورت رخ دادن استثناء عمومی:

```
{
  "status": false,
  "time": 1718451600,
  "code": 5005,
  "message": "Exception message...",
  "trace": [ ... ]
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"></div>### الگوی ساخت عنوان سند (getManualDocumentTitle)

الگوی خروجی عنوان براساس نوع سند:

<div id="bkmrk-manual-%E2%86%92-%D8%B3%D9%86%D8%AF-%D8%B4%D9%85%D8%A7%D8%B1%D9%87-x" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **manual** → سند شماره X | تاریخ | دستی - توضیحات
- **opening** → سند شماره X | افتتاحیه - توضیحات
- **closing** → سند شماره X | اختتامیه - توضیحات
- **daily\_sales** → مکانیزه فروش‌های روزانه
- **daily\_credit\_debit** → مکانیزه دریافت/پرداخت روزانه

</div>در این Route فقط سه نوع زیر بازگردانده می‌شود:

<div id="bkmrk-manual-opening-closi" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- manual
- opening
- closing

</div>

# POST /v2/manual-document/preferences/list/details

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td style="padding: 10px;">Method</td><td style="padding: 10px;">Endpoint</td><td style="padding: 10px;">Controller</td><td style="padding: 10px;">Middleware</td></tr><tr><td style="direction: ltr; padding: 10px;">POST</td><td style="direction: ltr; padding: 10px;">/v2/manual-document/preferences/list/details</td><td style="direction: ltr; padding: 10px;">AccountingController@preferenceDetails</td><td style="direction: ltr; padding: 10px;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر برای دریافت جزئیات کامل **معین (Moeen)** و **تفصیل (Preference)** استفاده می‌شود. ورودی شامل آرایه‌ای از رکوردها است که هر کدام شامل:

<div id="bkmrk-moeen%3A-%DA%A9%D8%AF-%D9%85%D8%B9%DB%8C%D9%86-%DB%B6-%D8%B1%D9%82%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- `moeen`: کد معین ۶ رقمی
- `preference`: کد تفصیل ۹ رقمی (pref code)

</div>این متد با منطق زیر کار می‌کند:

<div id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%DA%A9%D8%AF%D9%87%D8%A7%DB%8C-group%2F" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- استخراج کدهای group/general/moeen از کد اصلی ۶ رقمی
- اعتبارسنجی وجود داشتن: 
    - accounting\_groups
    - accounting\_generals
    - accounting\_moeens
- بازگرداندن اطلاعات کامل معین شامل: 
    - id
    - serialId
    - aggregation
    - title + کد کامل
- تشخیص نوع تفصیل از روی ۲ رقم اول کد و استخراج اطلاعات آن از جداول مربوطه:   
    (accounts / offices / colleague / personnel / references / customers / titles / announcement)
- در صورت عدم یافتن هر جزء، پاسخ خطـا ۴۲۲ برگردانده می‌شود.

  </div>### پارامترهای ورودی (JSON Body)

**Body:**

```
{
  "data": [
    {
      "moeen": "110203",
      "preference": 120000145
    }
  ],
  "branch": 1
}
```

<div id="bkmrk-data%3A-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87%E2%80%8C%D8%A7%DB%8C-%D8%A7%D8%B2-%D8%A2%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **data**: آرایه‌ای از آیتم‌ها
- **moeen**: رشته/عدد با ۶ رقم (گروه + کل + معین)
- **preference**: کد کامل تفصیل
- **branch**: شناسه شعبه

  </div>### خروجی موفق (Success Response)

در صورت موفقیت، خروجی شامل آرایه‌ای از آیتم‌های پردازش شده:

```
{
  "items": [
    {
      "moeen": {
        "id": 25,
        "serialId": 25,
        "details": {
          "aggregation": 1
        },
        "code": "110203",
        "text": "110203 - فروش | فروش داخلی"
      },
      "preference": {
        "id": "title-15",
        "code": 120000145,
        "table": "title",
        "text": "هزینه تعمیرات",
        "financial": false
      }
    }
  ],
  "meta": {
    "timestamp": 1718453500
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### خطاهای اعتبارسنجی (Error Response 422)

اگر هر بخش از گروه، کل یا معین وجود نداشته باشد:

```
{
  "error": {
    "code": 1000,
    "message": "گروه حسابداری با کد 11 در معین 110203 وجود ندارد"
  },
  "meta": {
    "timestamp": 1718453500
  }
}
```

اگر تفصیل وجود نداشته باشد:

```
{
  "error": {
    "code": 1000,
    "message": "تفصیل با کد 120000999 وجود ندارد"
  },
  "meta": {
    "timestamp": 1718453500
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### ساختار استخراج کد معین (Moeen Code Structure)

برای هر کد ۶ رقمی مثلاً 110203:

<div id="bkmrk-group-%3D-11-general-%3D" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **group** = 11
- **general** = 02
- **moeen** = 03

</div>این ۳ بخش به‌ترتیب روی جداول زیر اعتبارسنجی می‌شوند:

<div id="bkmrk-accounting_groups-ac" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **accounting\_groups**
- **accounting\_generals**
- **accounting\_moeens**

  </div>### تشخیص نوع تفصیل بر اساس ۲ رقم اول کد

<div id="bkmrk-%D9%BE%DB%8C%D8%B4%D9%88%D9%86%D8%AF-%D9%86%D9%88%D8%B9-%D8%AC%D8%AF%D9%88%D9%84-%D9%85%D8%B1%D8%AA%D8%A8" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>پیشوند</td><td>نوع</td><td>جدول مرتبط</td></tr><tr><td>10</td><td>حساب بانکی</td><td>accounting\_accounts</td></tr><tr><td>12</td><td>عنوان‌ها</td><td>titles</td></tr><tr><td>13</td><td>پرسنل / اپراتور</td><td>operators</td></tr><tr><td>20</td><td>همکار</td><td>colleagues</td></tr><tr><td>21</td><td>اعلان</td><td>announcement</td></tr><tr><td>30</td><td>مرکز هزینه</td><td>offices</td></tr><tr><td>40</td><td>فاکتور (reference)</td><td>factors + customers</td></tr><tr><td>80</td><td>مشتری</td><td>customers</td></tr></tbody></table>

  </div>### نمونه خروجی تفصیل برای انواع مختلف

**1 — Bank Account (prefix 10)**

```
{
  "id": "account-12",
  "code": 100000532,
  "text": "بانک ملت شعبه مرکزی (1234567890)"
}
```

**2 — Personnel (prefix 13)**

```
{
  "id": "personnel-88",
  "code": 130001234,
  "text": "مهدی احمدی"
}
```

**3 — Office (prefix 30)**

```
{
  "id": "office-7",
  "code": 300000215,
  "text": "مرکز هزینه اداره مرکزی"
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### Meta

تمام پاسخ‌ها شامل:

<div id="bkmrk-timestamp%3A-%D8%B2%D9%85%D8%A7%D9%86-unix" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- `timestamp`: زمان Unix
- HTTP Status: 
    - 200 در صورت موفقیت
    - 422 در صورت خطا

</div>

# GET /v2/accounting/closing

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/v2/accounting/closing</td><td style="direction: ltr;">AccountingController@showClosingAccount</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر اطلاعات مربوط به **تاریخ پایان دوره مالی بسته شده** را برای دفتر (Branch/Office) فعلی برمی‌گرداند. داده‌ها از جدول `office_config` خوانده می‌شوند و مقدار کلید `END_OF_FINANCIAL_PERIOD_CLOSING_ACCOUNTS` بررسی می‌شود. در صورت وجود مقدار، تنها **سال** و **ماه** از آن جدا و بازگردانده می‌شود.

تابع کمکی `Functions::officeConfig($office, $key)` وظیفه دارد مقدار تنظیم دفتر را از جدول `office_config` بخواند. اگر چیزی یافت نشود، مقدار `false` برمی‌گردد.

این مسیر برای کنترل دوره‌های مالی در مسیرهای دیگر (مثل ثبت یا حذف سند) کاربرد دارد تا مانع عملیات بعد از تاریخ بسته شدن شود.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">  </div>### فرآیند محاسباتی (Logic Steps)

<div id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%B9%D8%A8%D9%87-%D8%A8%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">1. دریافت شناسه شعبه با کلید `branch` از Query String یا Token.
2. فراخوانی تابع `officeConfig(branch, 'END_OF_FINANCIAL_PERIOD_CLOSING_ACCOUNTS')`.
3. اگر مقدار موجود بود، رشته خروجی مثلاً `14040301` به قالب JSON تبدیل می‌شود: 
    - `year = 1404`
    - `month = 03`
4. در غیر این صورت مقدار `null` برای `payload` برگردانده می‌شود.

  </div>### پاسخ موفق (Success Response)

```
{
  "payload": {
    "year": "1404",
    "month": "03"
  },
  "meta": {
    "timestamp": 1733053000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### پاسخ در صورت نبود تنظیمات

اگر مقدار در جدول تنظیمات یافت نشود:

```
{
  "payload": null,
  "meta": {
    "timestamp": 1733053000
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### وابستگی دیتابیس (Database Dependency)

<div id="bkmrk-table-key-descriptio" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Table</td><td>Key</td><td>Description</td></tr><tr><td>office\_config</td><td>END\_OF\_FINANCIAL\_PERIOD\_CLOSING\_ACCOUNTS</td><td>تاریخ بسته شدن حساب‌های دوره مالی (فرمت: YYYYMMDD)</td></tr></tbody></table>

  </div>### توابع کمکی استفاده شده (Helper Functions)

```
static function officeConfig($office, $key)
{
    return DB::table('office_config')
             ->where('office', $office)
             ->where('key', strtoupper($key))
             ->value('value') ?: false;
}
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### اطلاعات تکمیلی (Meta)

<div id="bkmrk-http-status-code%3A-20" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- HTTP Status Code: `200` در همه حالات (حتی بدون داده)
- Authentication: لازم است (JWT Token)
- Dependency: Redis ندارد – کاملاً DB-driven

</div>

# POST /v2/accounting/closing

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/v2/accounting/closing</td><td style="direction: ltr;">AccountingController@updateClosingAccount</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر برای **بستن دوره مالی** در یک شعبه استفاده می‌شود. قبل از اعمال بستن دوره، سیستم باید اطمینان پیدا کند که در بازه انتخاب‌شده هیچ **سند قطعی‌نشده** وجود ندارد. این بررسی شامل ۳ دسته سند اصلی است:

<div id="bkmrk-%D8%A7%D8%B3%D9%86%D8%A7%D8%AF-%D9%81%D8%B1%D9%88%D8%B4-%28referenc" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- اسناد فروش (References / Factors)
- اسناد دریافت-پرداخت (Payments)
- اسناد دستی (Manual Documents)

</div>اگر هرکدام از این موارد قطعی نشده باشند، مسیر با خطای 422 و پیام مناسب بازمی‌گردد. در صورت پاک بودن همه اسناد، مقدار جدید `END_OF_FINANCIAL_PERIOD_CLOSING_ACCOUNTS` برای شعبه در جدول `office_config` ثبت یا به‌روزرسانی می‌شود.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;">  </div>### پارامترهای ورودی (JSON Body)

```
{
  "year": "1404",
  "month": "03",
  "branch": 1
}
```

<div id="bkmrk-year%3A-%D8%B3%D8%A7%D9%84-%D8%B4%D9%85%D8%B3%DB%8C-%D8%AF%D9%88%D8%B1%D9%87%E2%80%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **year**: سال شمسی دوره‌ای که باید بسته شود
- **month**: ماه شمسی دوره‌ای که باید بسته شود
- **branch**: شعبه هدف

  </div>### منطق بررسی اسناد قطعی‌نشده (checkDocumentsForClosingAccount)

این تابع ۳ نوع سند را بررسی می‌کند:

#### اسناد فروش (factors)

هر سندی که:

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D9%85%D8%A7%D9%86-%D8%B4%D8%B9%D8%A8%D9%87-%D8%A8%D8%A7%D8%B4%D8%AF-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- برای همان شعبه باشد
- `status != 3` باشد (یعنی قطعی نشده باشد)
- در سال/ماه مورد نظر ثبت شده باشد

</div>#### اسناد دریافت/پرداخت (pays)

<div class="align-right" id="bkmrk-%D9%85%D9%86%D8%B7%D9%82-%D8%AD%D8%B3%D8%A7%D8%B3-%D9%88-%D8%AF%D9%88%DA%AF%D8%A7%D9%86%D9%87%3A-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;">منطق حساس و دوگانه: - **اگر created\_at داشته باشد:** باید ≤ آخر سال میلادی متناظر باشد
- **اگر deadline (مخصوص چک) داشته باشد:** باید ≤ آخر ماه شمسی متناظر باشد و `type_pay = check`
- پرداخت‌هایی که داخل سند گروه‌بندی شده‌اند (`group IS NOT NULL`) بررسی نمی‌شوند

</div>#### اسناد دستی (manual\_documents)

<div id="bkmrk-status-%21%3D-3-%E2%86%92-%D9%82%D8%B7%D8%B9%DB%8C-%D9%86" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- status != 3 → قطعی نشده
- date ≤ YYYYMM31

</div>#### خروجی تابع:

```
{
  "references": true | false,
  "payments": true | false,
  "manual_documents": true | false,
  "salary": false,
  "treasury_bills": false
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### پاسخ خطا (422)

اگر هرگونه سند قطعی‌نشده وجود داشته باشد:

```
{
  "error": {
    "code": 1000,
    "message": "اسناد فروش روزانه | دارای سند قطعی نشده می باشد. قبل از بستن حساب ها، تمامی اسناد باید قطعی شوند."
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

\*نوع پیام بسته به اولین پرچم فعال در خروجی checkDocumentsForClosingAccount است:

<div id="bkmrk-references-%E2%86%92-%22%D8%A7%D8%B3%D9%86%D8%A7%D8%AF-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- references → "اسناد فروش روزانه"
- payments → "اسناد دریافت پرداخت"
- manual\_documents → "اسناد دستی"

  </div>### پاسخ موفق (Success Response)

اگر هیچ سندی مانع بستن دوره نباشد:

<div id="bkmrk-%D8%A7%DA%AF%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-%D9%82%D8%A8%D9%84%D8%A7%D9%8B-%D8%AF%D8%B1-o" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- اگر مقدار قبلاً در office\_config وجود داشته باشد → update
- اگر وجود نداشته باشد → insert

</div>پاسخ:

```
Status: 204 No Content
Body: ""
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-of" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>office\_config</td><td>ذخیره مقدار پایان دوره مالی (فرمت: YYYYMM)</td></tr><tr><td>factors</td><td>اسناد فروش روزانه</td></tr><tr><td>pays</td><td>اسناد دریافت و پرداخت</td></tr><tr><td>manual\_documents</td><td>سندهای دستی</td></tr></tbody></table>

  </div>### META

<div id="bkmrk-http-status-codes%3A-2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- HTTP Status Codes: 204, 422
- کاملاً منطبق با عملیات واقعی حسابداری
- هیچ داده اضافی در صورت موفقیت بازگشت داده نمی‌شود

</div>

# PUT /v2/accounting/closing/definite/{type}

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">PUT</td><td style="direction: ltr;">/v2/accounting/closing/definite/{type}</td><td style="direction: ltr;">AccountingController@definiteDocumentsInClosingAccount</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر برای **قطعی‌کردن دسته‌ای اسناد** هنگام بستن دوره مالی استفاده می‌شود. نوع سند از پارامتر `{type}` تعیین می‌شود و شامل موارد زیر است:

<div id="bkmrk-daily_sales%3A-%D8%A7%D8%B3%D9%86%D8%A7%D8%AF-%D9%81" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **daily\_sales**: اسناد فروش روزانه (جدول factors)
- **daily\_credit\_debit**: اسناد دریافت/پرداخت روزانه (جدول pays)
- **manual\_documents**: اسناد دستی (جدول manual\_documents)
- **salary**: (رزرو – بدون پیاده‌سازی)
- **treasury\_bills**: (رزرو – بدون پیاده‌سازی)

</div>هر نوع سند با شرط‌های خاص زمانی (شمسی/میلادی) و وضعیت فعلی، به وضعیت `3` (قطعی) به‌روزرسانی می‌شود. این API بخشی از پیوستگی عملیات بستن حساب است و معمولاً پس از بررسی عدم وجود سند باز انجام می‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### پارامترهای ورودی (Input)

**Path Parameter**

<div id="bkmrk-%7Btype%7D%3A-%D9%86%D9%88%D8%B9-%D8%B3%D9%86%D8%AF-%D9%87%D8%AF%D9%81-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **{type}**: نوع سند هدف (daily\_sales/daily\_credit\_debit/manual\_documents/…)

</div>**Body (JSON):**

```
{
  "year": "1404",
  "month": "03",
  "branch": 1
}
```

<div id="bkmrk-year%3A-%D8%B3%D8%A7%D9%84-%D8%B4%D9%85%D8%B3%DB%8C-%D9%85%D9%88%D8%B1%D8%AF%D9%86" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **year**: سال شمسی موردنظر
- **month**: ماه شمسی موردنظر
- **branch**: شعبه هدف

  </div>### منطق پردازش (Logic)

ابتدا سال/ماه شمسی به میلادی تبدیل می‌شوند:

<div id="bkmrk-miladi.year-%3D-%D8%B3%D8%A7%D9%84-%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **miladi.year** = سال میلادی
- **miladi.month** = ماه میلادی
- **shamsi.year** = سال شمسی
- **shamsi.month** = ماه شمسی

</div>#### قطعی‌کردن اسناد فروش روزانه (type = daily\_sales)

<div class="align-right" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%D9%87%D8%A7%3A-status-%E2%88%88-%5B1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">فیلترها: - status ∈ \[1, 4\]
- branch = {branch}
- created\_at.year = میلادی
- created\_at.month = میلادی

عملیات:</div>```
UPDATE factors SET status = 3
```

#### قطعی‌کردن دریافت/پرداخت روزانه (type = daily\_credit\_debit)

<div class="align-right" id="bkmrk-%D8%B4%D8%B1%D8%B7%E2%80%8C%D9%87%D8%A7%3A-status-%E2%88%88-%5B4%2C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">شرط‌ها: - status ∈ \[4, 6\]
- group IS NULL
- branch = {branch}

فیلتر تاریخ: - اگر deadline NULL → created\_at ≤ YYYY-12-31 میلادی
- اگر deadline NOT NULL و نوع چک باشد → deadline ≤ YYYYMM31 شمسی

عملیات:</div>```
UPDATE pays SET status = 3
```

#### قطعی‌کردن اسناد دستی (type = manual\_documents)

<div class="align-right" id="bkmrk-%D8%B4%D8%B1%D8%B7%E2%80%8C%D9%87%D8%A7%3A-status-%3D-1-b" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">شرط‌ها: - status = 1
- branch = {branch}
- date ≤ YYYYMM31 شمسی

عملیات:</div>```
UPDATE manual_documents SET status = 3
```

#### موارد رزرو (به‌صورت NO-OP)

<div class="align-right" id="bkmrk-salary-%E2%86%92-%D8%A8%D8%AF%D9%88%D9%86-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">- salary → بدون تغییر
- treasury\_bills → بدون تغییر

اگر **type نامعتبر** باشد:</div>```
return false
```

<div class="align-right" id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95;">  </div>### پاسخ موفق (Success Response)

اگر عملیات با موفقیت انجام شود (true):

```
Status: 204 No Content
Body: ""
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### پاسخ خطا (Invalid Type)

اگر نوع سند شناسایی نشود (false):

```
Status: 422 Unprocessable Entity
Body: ""
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-fa" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>factors</td><td>اسناد فروش روزانه</td></tr><tr><td>pays</td><td>اسناد دریافت/پرداخت (چک + نقد)</td></tr><tr><td>manual\_documents</td><td>سندهای دستی</td></tr></tbody></table>

  </div>### Meta

<div id="bkmrk-http-status-codes%3A-2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- HTTP Status Codes: 204, 422
- نقش کلیدی در فرآیند قطعی‌سازی قبل از بستن مالی
- همواره نیازمند year + month + branch

</div>

# GET /v2/accounting/preference

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/v2/accounting/preference</td><td style="direction: ltr;">AccountingController@indexMappingPreferences</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر برای **دریافت لیست نگاشت حسابداری** استفاده می‌شود. اطلاعات جدول `mapping_accounting_preferences` را بر اساس شعبه، نوع شیء (`object_type`) و شناسه شیء (`object`) به‌صورت **صفحه‌بندی شده (Paginated)** برمی‌گرداند.

در صورت نبود پارامتر `paginate`، سیستم به‌صورت خودکار مقدار پیش‌فرض زیر را اعمال می‌کند:

<div id="bkmrk-length-%3D-30-start-%3D-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **length = 30**
- **start = 0**

</div>محاسبه شماره صفحه بر اساس فرمول زیر انجام می‌شود:

```
page = (start == 0 ? length : start + length) / length
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### پارامترهای ورودی (Query Parameters)

<div id="bkmrk-branch-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%B9%D8%A8%D9%87-%28%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- **branch** شناسه شعبه (اجباری)
- **object\_type** نوع شیء هدف (اختیاری) مثال: customer, colleague, office
- **object** شناسه شیء هدف (اختیاری)
- **paginate\[length\]** تعداد رکورد در هر صفحه (پیش‌فرض 30)
- **paginate\[start\]** شماره شروع (Offset) — پیش‌فرض 0

  </div>### پاسخ موفق (Success Response)

پاسخ شامل داده‌های صفحه‌بندی‌شده لاراول است (object کامل pagination):

```
{
  "payload": {
    "current_page": 1,
    "data": [
      {
        "id": 12,
        "branch": 1,
        "object_type": "customer",
        "object": 24,
        "moeen": 14,
        "created_at": "2024-07-21T10:00:00.000000Z",
        "updated_at": "2024-07-21T10:00:00.000000Z"
      }
    ],
    "first_page_url": "...",
    "from": 1,
    "last_page": 4,
    "last_page_url": "...",
    "next_page_url": "...",
    "path": "...",
    "per_page": 30,
    "prev_page_url": null,
    "to": 30,
    "total": 110
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">  </div>### خطاها (Errors)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D8%AE%D8%B7%D8%A7%DB%8C-%D8%A7%D8%AE%D8%AA%D8%B5%D8%A7%D8%B5" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- این مسیر خطای اختصاصی ندارد.
- در صورت نبود داده، مقدار `payload` برابر **false** خواهد بود.

  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-ma" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f4f4f4; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>mapping\_accounting\_preferences</td><td>جدول نگاشت حسابداری برای انواع موجودیت‌ها</td></tr></tbody></table>

  </div>### Meta

<div id="bkmrk-http-status%3A-200-pag" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.95; text-align: justify;">- HTTP Status: 200
- Pagination: فعال
- فیلتر پویا بر اساس object\_type و object

</div>

# POST /v2/accounting/preference

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">POST</td><td style="direction: ltr;">/v2/accounting/preference</td><td style="direction: ltr;">AccountingController@storeMappingPreferences</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای **ثبت نگاشت‌های حسابداری** در جدول `mapping_accounting_preferences` استفاده می‌شود. ورودی به‌صورت مجموعه‌ای از آیتم‌ها در فیلد `items` ارسال می‌شود. برای هر آیتم:

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%DB%8C%E2%80%8C%D8%B4%D9%88%D8%AF-%D8%A2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- ابتدا بررسی می‌شود آیا کد (`code`) قبلاً در همین شعبه ثبت شده یا خیر.
- اگر تکراری باشد → به آرایه `error` افزوده می‌شود.
- اگر جدید باشد → اطلاعات ساختاردهی شده و آماده Insert می‌شود.

</div>در انتها، اگر هیچ خطایی وجود نداشته باشد، همه رکوردهای جدید به صورت Batch درج می‌شوند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### ورودی مورد نیاز (JSON Body)

ساختار کلی:

```
{
  "branch": 1,
  "items": [
    {
      "id": "customer-24",
      "text": "24 - مشتری عمده",
      "code": "110201"
    },
    {
      "id": "office-3",
      "text": "301 - دفتر مرکزی",
      "code": "880030"
    }
  ]
}
```

#### جزئیات فیلدها:

<div id="bkmrk-branch-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%B9%D8%A8%D9%87-%28%D8%B6" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **branch** شناسه شعبه (ضروری)
- **items**: آرایه‌ای از نگاشت‌ها هر آیتم شامل: 
    - **id**: ترکیب نوع شیء و شناسه (مثال: `customer-24`)
    - **text**: رشته شامل کد سیستمی و نام خوانا (مثال: `"24 - مشتری"`)
    - **code**: کد نهایی نگاشت موردنظر (۶ رقمی یا بیشتر)

  </div>### ساختاردهی رکورد درج‌شونده

برای هر آیتم غیرتکراری:

<div id="bkmrk-object_type-%D8%A7%D8%B2-%D8%A8%D8%AE%D8%B4-%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- `object_type` از بخش اول `id` (مثال: customer)
- `object` از بخش دوم `id` (مثال: 24)
- `system_code` اولین بخش `text` قبل از خط تیره
- `title` بخش آخر `text` بعد از آخرین خط تیره
- `code` همان کد نهایی ارسال‌شده توسط کاربر
- `branch` از ورودی
- `updated_at` = زمان فعلی

  </div>### پاسخ موفق (Success Response)

در صورت عدم وجود خطا و انجام موفق درج:

```
{
  "payload": true,
  "error": false,
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### پاسخ در صورت وجود خطا برای برخی آیتم‌ها

اگر بخشی از آیتم‌ها Duplicate باشند:

```
{
  "payload": true,
  "error": [
    {
      "message": "این کد قبلا ثبت شده است.",
      "code": "110201"
    }
  ],
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### خطای عدم ارسال فیلد items

```
{
  "error": {
    "message": "The items field is required."
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-ma" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>mapping\_accounting\_preferences</td><td>نگاشت حسابداری برای انواع موجودیت‌ها</td></tr></tbody></table>

  </div>### Meta

<div id="bkmrk-http-status-code%3A-20" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- HTTP Status Code: 200
- Batch Insert: فعال
- Duplicate Check: مبتنی بر code + branch

</div>

# GET /v2/accounting/preference/{id}

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">GET</td><td style="direction: ltr;">/v2/accounting/preference/{id}</td><td style="direction: ltr;">AccountingController@showMappingPreferences</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این مسیر برای **دریافت یک رکورد واحد** از جدول `mapping_accounting_preferences` بر اساس شناسه ارسال شده در `{id}` استفاده می‌شود.

هیچ پردازش اضافی، تغییر ساختار یا اعتبارسنجی ویژه انجام نمی‌شود؛ فقط رکورد دیتابیس خوانده شده و به‌صورت مستقیم بازگردانده می‌شود.

<div class="align-right" id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9;">  </div>### پارامتر مسیر (Path Parameter)

<div id="bkmrk-%7Bid%7D-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B9%D8%AF%D8%AF%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **{id}** شناسه عددی رکورد موردنظر در جدول نگاشت‌های حسابداری

  </div>### پاسخ موفق (Success Response)

اگر رکورد موجود باشد:

```
{
  "payload": {
    "id": 12,
    "branch": 1,
    "object_type": "customer",
    "object": 24,
    "title": "مشتری عمده",
    "system_code": "24",
    "code": "110201",
    "updated_at": "2024-07-21 10:00:00"
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

اگر رکورد موجود نباشد، مقدار `payload` برابر **false** خواهد بود.

```
{
  "payload": false,
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### خطاها (Errors)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%B3%DB%8C%D8%B1-%D9%87%DB%8C%DA%86-%D8%AE%D8%B7%D8%A7%DB%8C-%D8%A7%D8%AE" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- این مسیر هیچ خطای اختصاصی ندارد.
- در صورت عدم یافتن رکورد، `payload = false` بازگردانده می‌شود.

  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-ma" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>mapping\_accounting\_preferences</td><td>ذخیره‌ساز نگاشت‌های حسابداری</td></tr></tbody></table>

  </div>### Meta

<div id="bkmrk-http-status-code%3A-20" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- HTTP Status Code: 200
- Database Method: `find(id)`
- No pagination

</div>

# PUT /v2/accounting/preference/{id}

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td style="direction: ltr;">PUT</td><td style="direction: ltr;">/v2/accounting/preference/{id}</td><td style="direction: ltr;">AccountingController@updateMappingPreferences</td><td style="direction: ltr;">authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای **ویرایش یک نگاشت حسابداری** در جدول `mapping_accounting_preferences` استفاده می‌شود.

منطق مسیر شامل مراحل زیر است:

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%DB%8C%DA%A9%D8%AA%D8%A7-%D8%A8%D9%88%D8%AF%D9%86-code" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- بررسی یکتا بودن `code` در همان شعبه (به‌جز رکورد جاری).
- تجزیه `id` به `object_type` و `object`.
- تجزیه `text` برای استخراج: 
    - **system\_code** → بخش اول متن
    - **title** → آخرین بخش متن
- به‌روزرسانی رکورد در دیتابیس.
- بازگردانی رکورد ویرایش‌شده.

  </div>### ساختار ورودی (JSON Body)

```
{
  "branch": 1,
  "id": "customer-24",
  "text": "24 - مشتری عمده",
  "code": "110201"
}
```

#### توضیح فیلدها

<div id="bkmrk-branch-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%B4%D8%B9%D8%A8%D9%87-id" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- **branch** شناسه شعبه
- **id** ساختار `{object_type}-{object}`، مثال: 
    - customer-24
    - office-3
- **text** رشته‌ای شامل کد سیستمی و عنوان قابل نمایش مثال: `"24 - مشتری عمده"`
- **code** کد نهایی نگاشت (۶ رقمی یا بیشتر)

  </div>### اعتبارسنجی

قبل از ذخیره، بررسی می‌شود که آیا کد جدید در همان شعبه قبلاً استفاده شده است یا خیر:

```
exists where:
  branch = request.branch
  code = request.code
  id != {id}
```

اگر رکوردی با همین کد وجود داشته باشد، به‌جای به‌روزرسانی، خطا بازگردانده می‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### پاسخ موفق (Success Response)

در صورت به‌روزرسانی موفق، رکورد کامل جدید بازگردانده می‌شود:

```
{
  "payload": {
    "id": 12,
    "branch": 1,
    "object_type": "customer",
    "object": 24,
    "title": "مشتری عمده",
    "system_code": "24",
    "code": "110201",
    "updated_at": "2024-07-21T10:00:00.000000Z"
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### پاسخ خطا (Error Response)

اگر کد تکراری باشد:

```
{
  "error": {
    "message": "این کد قبلا ثبت شده است."
  },
  "meta": {
    "timestamp": 1733056000
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">  </div>### وابستگی دیتابیس

<div id="bkmrk-table-description-ma" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Table</td><td>Description</td></tr><tr><td>mapping\_accounting\_preferences</td><td>نگاشت حسابداری برای انواع موجودیت‌ها</td></tr></tbody></table>

  </div>### Meta

<div id="bkmrk-http-status-code%3A-20" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.9; text-align: justify;">- HTTP Status Code: 200
- Operation: Update
- Validation: Unique per branch (except current record)

</div>

# POST /v2/account-history/calculate-daily

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>POST</td><td>/v2/account-history/calculate-daily</td><td>AccountHistoryController@calculateDailyBalanceForYear</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای **محاسبه و ذخیره مانده‌های روزانه** یک همکار (Colleague) استفاده می‌شود. فرآیند محاسبه می‌تواند:

<div id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D9%84-%DB%8C%DA%A9-%D8%B3%D8%A7%D9%84-%D9%85%D8%B4%D8%AE%D8%B5-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- برای کل یک سال مشخص (جلالی)
- یا برای یک بازه تاریخ خاص

</div>محاسبات امکان اجرا به صورت:

<div id="bkmrk-%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86-%28sync%29-%D8%A8%D8%A7-%D8%A8%D8%A7%D8%B2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **همزمان (Sync)** با بازگردانی نتیجه
- **غیرهمزمان (Async)** با استفاده از Job در Queue

</div>نتیجه‌های محاسبه شده در Redis با TTL سی روزه ذخیره می‌شوند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### ورودی (Request Body)

```
{
  "colleague_id": 24,
  "year": 1403,          // optional
  "from": "1403-01-01",  // optional
  "to": "1403-12-29",    // optional
  "async": true          // optional, default = false
}
```

#### قوانین اعتبارسنجی

<div id="bkmrk-colleague_id-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id** الزامی و باید در جدول colleagues موجود باشد.
- **year** اختیاری، عدد بین 1300 تا 1500.
- **from/to** تاریخ شمسی در فرمت `Y-m-d`.
- **async** مقدار boolean.
- ارسال تاریخ‌های آینده ممنوع است.

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D9%88%D9%84-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-larav" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- اول اعتبارسنجی Laravel انجام می‌شود.
- اگر تاریخ آینده ارسال شود → خطا.
- اگر async=true → Job با پارامترهای: 
    - colleague\_id
    - year
    - type=daily
    
    Dispatch می‌شود.
- اگر async=false → `calculateAndStoreDailyBalance()` مستقیماً فراخوانی می‌شود.

</div>#### مشخصات Job

<div id="bkmrk-timeout%3A-3600-%D8%AB%D8%A7%D9%86%DB%8C%D9%87-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **timeout:** 3600 ثانیه
- **tries:** 3
- در صورت خطا → fail شدن job
- لاگ کامل برای شروع، پایان و خطا

  </div>### پاسخ موفق (Async Mode)

```
{
  "payload": null,
  "meta": {
    "colleague_id": 24,
    "year": 1403,
    "from": "1403-01-01",
    "to": "1403-12-29",
    "type": "daily",
    "status": "queued",
    "timestamp": "2025-12-01T10:15:00+03:30"
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ موفق (Sync Mode)

```
{
  "payload": {
    "1403-01-01": {
      "credit": 0,
      "debit": 1200000,
      "balance": -1200000
    },
    "1403-01-02": {
      "credit": 500000,
      "debit": 0,
      "balance": 500000
    }
  },
  "meta": {
    "colleague_id": 24,
    "from": "1403-01-01",
    "to": "1403-12-29",
    "total_days": 365,
    "timestamp": "2025-12-01T10:15:00+03:30"
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "colleague_id is required"
  },
  "meta": {
    "timestamp": "2025-12-01T10:18:00+03:30"
  }
}
```

#### تاریخ آینده مجاز نیست (400)

```
{
  "error": {
    "code": "FUTURE_DATE_NOT_ALLOWED",
    "message": "Cannot calculate for future dates."
  },
  "meta": {
    "colleague_id": 24,
    "to": "1404-01-01",
    "timestamp": "2025-12-01T10:18:00+03:30"
  }
}
```

#### خطای داخلی (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Unexpected error..."
  },
  "meta": {
    "colleague_id": 24,
    "timestamp": "2025-12-01T10:20:00+03:30"
  }
}
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Meta

<div id="bkmrk-redis-key-format%3A-ac" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- Redis Key Format: `account_history:daily:{colleagueId}:{year}:{date}`
- TTL: 30 روز
- Job Class: `CalculateAccountHistoryJob`
- Service Method: `calculateAndStoreDailyBalance()`

</div>

# POST /v2/account-history/calculate-monthly

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>POST</td><td>/v2/account-history/calculate-monthly</td><td>AccountHistoryController@calculateMonthlyBalanceForYear</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API وظیفه **محاسبه و ذخیره مانده‌های ماهانه** برای یک همکار (Colleague) را دارد. محاسبه می‌تواند بر اساس:

<div id="bkmrk-%DB%8C%DA%A9-%D8%B3%D8%A7%D9%84-%D9%85%D8%B4%D8%AE%D8%B5-%28%D9%85%D8%AB%D9%84%D8%A7%D9%8B-1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- یک سال مشخص (مثلاً 1402)
- یا یک بازه تاریخ دلخواه (from / to)

</div>محاسبات می‌توانند به صورت:

<div id="bkmrk-%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86-%28sync%29%3A-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **همزمان (Sync)**: خروجی محاسبه همان لحظه بازگردانده می‌شود.
- **غیرهمزمان (Async)**: پردازش در صف اجرا شده و پاسخ سریع برمی‌گردد.

</div>در حالت async، عملیات توسط `CalculateAccountHistoryJob` با type = "monthly" انجام می‌شود.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### ورودی (Request Body)

```
{
  "colleague_id": 108,
  "year": 1403,           // optional
  "from": "1403-01-01",   // optional
  "to": "1403-12-29",     // optional
  "async": false          // optional (default=false)
}
```

#### قوانین اعتبارسنجی

<div id="bkmrk-colleague_id-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id** الزامی و باید در جدول colleagues وجود داشته باشد.
- **year** اختیاری و باید بین 1300 تا 1500 باشد.
- **from/to** تاریخ شمسی معتبر در فرمت `Y-m-d`.
- **async** مقدار boolean.
- ارسال تاریخ آینده ممنوع است → خطای **FUTURE\_DATE\_NOT\_ALLOWED**.

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-laravel-%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- اعتبارسنجی Laravel اجرا می‌شود.
- اگر from یا to بزرگ‌تر از تاریخ امروز باشد → خطا.
- اگر async=true: 
    - Job با مقادیر: 
        - colleague\_id
        - year
        - type = monthly
        
        Dispatch می‌شود.
    - پاسخ سریع با وضعیت **queued** برمی‌گردد.
- اگر async=false:   
    محاسبه مستقیم با: `calculateAndStoreMonthlyBalance()` انجام می‌شود.

</div>#### مشخصات Job

<div id="bkmrk-timeout%3A-3600-%D8%AB%D8%A7%D9%86%DB%8C%D9%87-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **timeout:** 3600 ثانیه
- **tries:** 3
- در صورت خطا → fail شدن job
- ثبت کامل لاگ از شروع، پایان یا خطا

  </div>### پاسخ موفق (Async Mode)

```
{
  "payload": null,
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "from": "1403-01-01",
    "to": "1403-12-29",
    "type": "monthly",
    "status": "queued",
    "timestamp": "2025-12-01T12:35:00+03:30"
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ موفق (Sync Mode)

```
{
  "payload": {
    "1403-01": { "credit": 0, "debit": 900000, "balance": -900000 },
    "1403-02": { "credit": 500000, "debit": 0, "balance": 500000 }
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "total_months": 12,
    "timestamp": "2025-12-01T12:35:00+03:30"
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "colleague_id field is required"
  },
  "meta": {
    "timestamp": "2025-12-01T12:40:00+03:30"
  }
}
```

#### تاریخ آینده مجاز نیست (400)

```
{
  "error": {
    "code": "FUTURE_DATE_NOT_ALLOWED",
    "message": "Cannot calculate for future dates."
  },
  "meta": {
    "colleague_id": 108,
    "to": "1405-01-01",
    "timestamp": "2025-12-01T12:40:00+03:30"
  }
}
```

#### خطای داخلی (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Unexpected error..."
  },
  "meta": {
    "colleague_id": 108,
    "timestamp": "2025-12-01T12:41:00+03:30"
  }
}
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Meta

<div id="bkmrk-job-class%3A-calculate" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- Job Class: `CalculateAccountHistoryJob`
- Service Method: `calculateAndStoreMonthlyBalance`
- Job Type: `monthly`

</div>

# POST /v2/account-history/calculate-complete

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>POST</td><td>/v2/account-history/calculate-complete</td><td>AccountHistoryController@calculateCompleteHistory</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای **محاسبه کامل مانده‌ها** استفاده می‌شود؛ یعنی:

<div id="bkmrk-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87-%D9%85%D8%A7%D9%86%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%DB%8C-%D8%B1%D9%88%D8%B2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- محاسبه مانده‌های **روزانه**
- محاسبه مانده‌های **ماهانه**

</div>محاسبه‌ها می‌توانند بر اساس:

<div id="bkmrk-%DB%8C%DA%A9-%D8%B3%D8%A7%D9%84-%D9%85%D8%B4%D8%AE%D8%B5-%28year%29-%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- یک سال مشخص (year)
- یا بازه‌ای میان `from / to`

</div>روش اجرا:

<div id="bkmrk-%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86-%28sync%29%3A-%D9%87%D8%B1-%D8%AF%D9%88" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **همزمان (Sync)**: هر دو محاسبه انجام شده و پاسخ کامل داده می‌شود.
- **غیرهمزمان (Async)**: دو Job جداگانه (daily و monthly) در صف Dispatch می‌شوند.

</div>هر دو محاسبه از طریق سرویس `AccountHistoryService` انجام می‌شوند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### ورودی (Request Body)

```
{
  "colleague_id": 108,
  "year": 1403,           // optional
  "from": "1403-01-01",   // optional
  "to": "1403-12-29",     // optional
  "async": true           // optional (default = true)
}
```

#### قوانین اعتبارسنجی

<div id="bkmrk-colleague_id-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C%D8%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id** الزامی، integer، موجود در colleagues.
- **year** اختیاری، عدد 1300 تا 1500.
- **from / to** تاریخ شمسی معتبر `Y-m-d`.
- **async** مقدار boolean (پیش‌فرض true).
- تاریخ آینده ممنوع → خطای **FUTURE\_DATE\_NOT\_ALLOWED**.
- در صورت عدم ارسال year و عدم ارسال from/to → خطای INVALID\_DATE\_RANGE (در سرویس).

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%A7%D9%86%D8%AC" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- ابتدا اعتبارسنجی انجام می‌شود.
- اگر from یا to &gt; امروز باشد → خطا 400.
- اگر async=true: 
    - دو Job جدا dispatch می‌شوند: 
        - type = daily
        - type = monthly
    - پاسخ سریع با وضعیت queued برمی‌گردد.
- اگر async=false: 
    - `calculateAndStoreDailyBalance()`
    - `calculateAndStoreMonthlyBalance()`
    - خطای هرکدام → CALCULATION\_ERROR

</div>#### مشخصات Job

<div id="bkmrk-timeout%3A-3600-%D8%AB%D8%A7%D9%86%DB%8C%D9%87-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **timeout:** 3600 ثانیه
- **tries:** 3
- job جداگانه برای daily و monthly
- در صورت خطا → fail شدن job
- لاگ کامل شروع، پایان، و خطا

  </div>### پاسخ موفق (Async Mode)

```
{
  "payload": null,
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "from": "1403-01-01",
    "to": "1403-12-29",
    "jobs": ["daily", "monthly"],
    "status": "queued",
    "timestamp": "2025-12-01T14:20:00+03:30"
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ موفق (Sync Mode)

```
{
  "payload": {
    "daily": {
      "1403-01-01": { "credit": 0, "debit": 1200000, "balance": -1200000 },
      "1403-01-02": { "credit": 500000, "debit": 0, "balance": 500000 }
    },
    "monthly": {
      "1403-01": { "credit": 0, "debit": 900000, "balance": -900000 },
      "1403-02": { "credit": 500000, "debit": 0, "balance": 500000 }
    }
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "from": "1403-01-01",
    "to": "1403-12-29",
    "daily_meta": { "...": "..." },
    "monthly_meta": { "...": "..." },
    "timestamp": "2025-12-01T14:20:00+03:30"
  }
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "colleague_id is required"
  },
  "meta": {
    "timestamp": "2025-12-01T14:25:00+03:30"
  }
}
```

#### تاریخ آینده مجاز نیست (400)

```
{
  "error": {
    "code": "FUTURE_DATE_NOT_ALLOWED",
    "message": "Cannot calculate for future dates."
  },
  "meta": {
    "colleague_id": 108,
    "to": "1404-01-01",
    "timestamp": "2025-12-01T14:25:00+03:30"
  }
}
```

#### خطای محاسباتی یکی از دو عملیات (400)

```
{
  "error": {
    "code": "CALCULATION_ERROR",
    "message": "Error in one or both calculations",
    "details": {
      "daily": null,
      "monthly": {
        "code": "INTERNAL_ERROR",
        "message": "..."
      }
    }
  },
  "meta": {
    "colleague_id": 108,
    "timestamp": "2025-12-01T14:26:00+03:30"
  }
}
```

#### خطای داخلی (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Unexpected exception..."
  },
  "meta": {
    "colleague_id": 108,
    "timestamp": "2025-12-01T14:26:00+03:30"
  }
}
```

<div id="bkmrk--4" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Meta

<div id="bkmrk-jobs%3A-calculateaccou" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- Jobs: `CalculateAccountHistoryJob`
- Job Types: `daily`, `monthly`
- Service Methods: 
    - `calculateAndStoreDailyBalance()`
    - `calculateAndStoreMonthlyBalance()`
- همه عملیات‌ها برای همکار `ID = 108` نمونه‌سازی شده‌اند.

</div>

# GET /v2/account-history/daily

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>GET</td><td>/v2/account-history/daily</td><td>AccountHistoryController@getDailyBalance</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API صرفاً برای **خواندن سریع** اطلاعات محاسبه‌شده از قبل استفاده می‌شود.

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D9%85%D8%AA%D8%AF-%D9%87%DB%8C%DA%86-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87%E2%80%8C%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- این متد هیچ محاسبه‌ای انجام نمی‌دهد (Calculation Trigger نیست).
- داده‌ها را مستقیماً از **Cache (Redis)** بازیابی می‌کند.
- اگر داده‌ای برای تاریخ مورد نظر محاسبه نشده باشد (در کش نباشد)، خطای 404 برمی‌گرداند.

  </div>### ورودی (Query Parameters)

پارامترها به صورت Query String ارسال می‌شوند:

```
?colleague_id=108&year=1403&date=1403-01-05
```

#### قوانین اعتبارسنجی (Validation Rules)

<div id="bkmrk-colleague_id%3A-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id:** الزامی | integer | باید در جدول `colleagues` موجود باشد.
- **year:** الزامی | integer | بازه 1300 تا 1500.
- **date:** الزامی | فرمت دقیق `Y-m-d` (تاریخ شمسی).

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7-%D8%AA%D9%88%D8%B3%D8%B7-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- ابتدا ورودی‌ها توسط `Validator` بررسی می‌شوند. 
    - در صورت شکست: بازگشت خطای **400** با کد `VALIDATION_ERROR`.
- متد سرویس `getDailyBalanceFromCache($colleagueId, $year, $date)` فراخوانی می‌شود.
- سرویس چک می‌کند آیا کلید مربوطه در Redis وجود دارد یا خیر.
- **بررسی نتیجه سرویس:**
    - اگر خروجی شامل کلید `error` باشد → بازگشت خطای **404** (Not Found).
    - در غیر این صورت → بازگشت داده‌ها با وضعیت **200**.
- مدیریت خطا (Exception Handling): هرگونه خطا در بلاک `try-catch` منجر به خطای **500** می‌شود.

  </div>### پاسخ موفق (200 OK)

```
{
  "payload": {
    "credit": 0,
    "debit": 1500000,
    "balance": -1500000
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "date": "1403-01-05",
    "source": "cache",
    "timestamp": "2025-12-01T15:30:00+03:30"
  }
}
```

<div class="align-right" id="bkmrk-%2A-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-payload-%D8%A8%D8%B3%D8%AA" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85;"><small>\* ساختار payload بسته به خروجی سرویس ممکن است دقیقاً به این شکل باشد.</small>   </div>### پاسخ‌های خطا (Error Responses)

#### خطای اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The date does not match the format Y-m-d."
  },
  "meta": {
    "timestamp": "2025-12-01T15:30:00+03:30"
  }
}
```

#### داده یافت نشد / محاسبه نشده (404)

```
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No data found for this date. Please calculate first."
  },
  "meta": {
    "timestamp": "2025-12-01T15:31:00+03:30"
  }
}
```

#### خطای سرور (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Redis connection failed..."
  },
  "meta": {
    "colleague_id": 108,
    "timestamp": "2025-12-01T15:32:00+03:30"
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### توضیحات فنی (Meta)

<div id="bkmrk-service-method%3A-acco" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **Service Method:** `AccountHistoryService::getDailyBalanceFromCache`
- **Redis Key Pattern:** معمولاً به فرمت `account_history:daily:{colleagueId}:{year}:{date}` می‌باشد.
- این متد Stateless است و فقط خواندن انجام می‌دهد.

</div>

# GET /v2/account-history/monthly

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>GET</td><td>/v2/account-history/monthly</td><td>AccountHistoryController@getMonthlyBalance</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای دریافت مانده حساب **ماهانه** طراحی شده است و دارای مکانیزم هوشمند (Read-Through Cache) است:

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%DA%A9%D8%B4%3A-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B3%D8%B9%DB%8C-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">1. **بررسی کش:** ابتدا سعی می‌کند داده را از Redis بخواند.
2. **محاسبه خودکار (Auto-Calculation):** اگر داده در کش نباشد، سیستم به طور خودکار متد `calculateAndStoreMonthlyBalance` را اجرا می‌کند تا داده‌های آن سال تولید و ذخیره شوند.
3. **بازخوانی:** پس از محاسبه، مجدداً داده را از کش می‌خواند و برمی‌گرداند.

</div>توجه: اگر کش خالی باشد، پاسخ این سرویس ممکن است کمی طول بکشد (به اندازه زمان محاسبه مانده سالانه)

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### ورودی (Query Parameters)

```
?colleague_id=108&year=1403&month=2
```

#### قوانین اعتبارسنجی (Validation Rules)

<div id="bkmrk-colleague_id%3A-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id:** الزامی | integer | موجود در جدول `colleagues`.
- **year:** الزامی | integer | بازه 1300 تا 1500.
- **month:** الزامی | integer | بازه 1 تا 12 (نیاز به ارسال صفر قبل عدد نیست، سیستم خودکار Pad می‌کند).

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7." style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- اعتبارسنجی ورودی‌ها. 
    - خطا → 400 (VALIDATION\_ERROR).
- تبدیل ماه ورودی به فرمت دورقمی (مثلاً `2` به `02`).
- فراخوانی سرویس `getMonthlyBalanceFromCache`.
- **Logic داخل سرویس:**
    1. ساخت کلید Redis.
    2. چک کردن وجود کلید. اگر بود → بازگشت (Meta: `cached: true`).
    3. اگر نبود → اجرای متد محاسبه (Calculation).
    4. چک کردن مجدد Redis. اگر بود → بازگشت (Meta: `cached: false, calculated: true`).
    5. اگر باز هم نبود → بازگشت آرایه خطا (CACHE\_MISS).
- اگر خروجی سرویس حاوی `error` باشد → پاسخ **404**.
- در غیر این صورت → پاسخ **200**.

  </div>### پاسخ موفق (200 OK)

#### حالت ۱: داده در کش موجود بود (Cache Hit)

```
{
  "payload": {
    "credit": 500000,
    "debit": 0,
    "balance": 500000
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "month": "02",
    "cached": true,
    "timestamp": "2025-12-01T16:00:00+03:30"
  }
}
```

#### حالت ۲: داده محاسبه شد (Cache Miss -&gt; Calculated)

```
{
  "payload": {
    "credit": 500000,
    "debit": 0,
    "balance": 500000
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "month": "02",
    "cached": false,
    "calculated": true,
    "timestamp": "2025-12-01T16:00:05+03:30"
  }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### خطای اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The month must be at least 1."
  },
  "meta": { ... }
}
```

#### خطای عدم یافتن/محاسبه (404)

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

```
{
  "error": {
    "code": "CACHE_MISS",
    "message": "Balance not found in cache and calculation failed"
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "month": "02",
    "cached": false,
    "timestamp": "..."
  }
}
```

#### خطای سرور (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
```

# GET /v2/account-history/all-daily

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>GET</td><td>/v2/account-history/all-daily</td><td>AccountHistoryController@getAllDailyBalances</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای دریافت **یکجای تمام سوابق روزانه** یک سال خاص استفاده می‌شود (مثلاً برای رسم نمودار تغییرات موجودی در طول سال).

<div id="bkmrk-%D8%B9%D9%85%D9%84%DA%A9%D8%B1%D8%AF-%D8%AF%D8%B3%D8%AA%D9%87%E2%80%8C%D8%A7%DB%8C-%28batc" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **عملکرد دسته‌ای (Batch):** به جای درخواست‌های متعدد، تمام کلیدهای کش مربوط به یک سال را اسکن و بازیابی می‌کند.
- **مرتب‌سازی:** داده‌ها به صورت خودکار بر اساس تاریخ (Date) مرتب می‌شوند (Sort Ascending).
- **بدون محاسبه:** این متد فقط خواندنی است و محاسبه‌ای انجام نمی‌دهد. فقط داده‌هایی که قبلاً محاسبه و کش شده‌اند را برمی‌گرداند.

  </div>### ورودی (Query Parameters)

```
?colleague_id=108&year=1403
```

#### قوانین اعتبارسنجی (Validation Rules)

<div id="bkmrk-colleague_id%3A-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id:** الزامی | integer | موجود در جدول `colleagues`.
- **year:** الزامی | integer | بازه 1300 تا 1500.

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- اعتبارسنجی ورودی‌ها (بازگشت 400 در صورت خطا).
- ساخت الگوی جستجو در Redis: `DAILY_BALANCE_KEY{colleagueId}:{year}:*`.
- استفاده از دستور `Redis::keys` برای یافتن تمام کلیدهای منطبق.
- حلقه روی کلیدها: 
    - دریافت مقدار (`Redis::get`) و دیکود کردن JSON.
- ذخیره نتایج در آرایه با **کلید تاریخ** (برای دسترسی سریع‌تر در فرانت‌اند).
- مرتب‌سازی آرایه بر اساس کلید (تاریخ) با تابع `ksort`.
- بازگشت داده‌ها (حتی اگر لیست خالی باشد، آرایه خالی برمی‌گردد و خطای 404 نداریم).

  </div>### پاسخ موفق (200 OK)

توجه کنید که کلیدهای آبجکت `payload` همان تاریخ‌ها هستند.

```
{
  "payload": {
    "1403-01-01": {
      "credit": 0,
      "debit": 100000,
      "balance": -100000,
      "date": "1403-01-01"
    },
    "1403-01-02": {
      "credit": 50000,
      "debit": 0,
      "balance": -50000,
      "date": "1403-01-02"
    },
    "1403-05-20": {
      "credit": 200000,
      "debit": 0,
      "balance": 150000,
      "date": "1403-05-20"
    }
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "total_days": 3,
    "cached": true,
    "timestamp": "2025-12-01T16:15:00+03:30"
  }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### خطای اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The year must be between 1300 and 1500."
  },
  "meta": { ... }
}
```

#### خطای سرور (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Redis connection failed"
  },
  "meta": { ... }
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### توضیحات فنی (Meta)

<div id="bkmrk-redis-pattern%3A-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **Redis Pattern:** استفاده از Wildcard (`*`) در انتهای کلید.
- **Performance:** این کوئری ممکن است روی دیتابیس‌های ردیس بسیار حجیم (میلیون‌ها کلید) کمی کند باشد، اما در اسکیل فعلی قابل قبول است.
- خروجی به صورت `Dictionary` (Map) است، نه آرایه ساده؛ تا دسترسی به یک تاریخ خاص در کلاینت (Client-Side) راحت‌تر باشد (O(1)).

</div>

# GET /v2/account-history/all-monthly

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>GET</td><td>/v2/account-history/all-monthly</td><td>AccountHistoryController@getAllMonthlyBalances</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای دریافت **یکجای تمام مانده‌های ماهانه** یک سال مشخص استفاده می‌شود.

<div id="bkmrk-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%3A-%D9%85%D9%86%D8%A7%D8%B3%D8%A8-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%86" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **کاربرد:** مناسب برای نمایش جداول خلاصه وضعیت سالانه یا نمودارهای میله‌ای ماهانه.
- **نحوه خواندن:** داده‌ها را به صورت دسته‌ای (Bulk) با استفاده از الگوی Wildcard از Redis استخراج می‌کند.
- **بدون محاسبه (No Calculation):** این متد صرفاً داده‌های موجود در کش را می‌خواند. اگر برای ماهی داده‌ای محاسبه نشده باشد، در خروجی ظاهر نخواهد شد (Trigger محاسبه ندارد).

  </div>### ورودی (Query Parameters)

```
?colleague_id=108&year=1403
```

#### قوانین اعتبارسنجی (Validation Rules)

<div id="bkmrk-colleague_id%3A-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id:** الزامی | integer | موجود در جدول `colleagues`.
- **year:** الزامی | integer | بازه 1300 تا 1500.

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7-%D8%AA%D9%88%D8%B3%D8%B7-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- بررسی ورودی‌ها توسط Validator.
- جستجو در Redis با الگوی: `MONTHLY_BALANCE_KEY{colleagueId}:{year}:*`.
- استخراج کلیدها و سپس دریافت مقادیر (Values).
- **استخراج شماره ماه:** سیستم ماه را از انتهای کلید Redis جدا می‌کند (مثلاً از `...:1403:05` مقدار `05` برداشته می‌شود).
- ذخیره در آرایه خروجی به صورتی که **کلید آرایه، شماره ماه باشد**.
- مرتب‌سازی بر اساس ماه (`ksort`) تا داده‌ها به ترتیب زمانی (فروردین تا اسفند) باشند.
- بازگشت پاسخ نهایی.

  </div>### پاسخ موفق (200 OK)

خروجی به صورت Map (Dictionary) است که کلیدهای آن شماره ماه (معمولاً دو رقمی مثل "01") هستند.

```
{
  "payload": {
    "01": {
      "credit": 1000000,
      "debit": 500000,
      "balance": 500000
    },
    "02": {
      "credit": 200000,
      "debit": 0,
      "balance": 700000
    },
    "12": {
      "credit": 0,
      "debit": 100000,
      "balance": 600000
    }
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "total_months": 3,
    "cached": true,
    "timestamp": "2025-12-01T16:30:00+03:30"
  }
}
```

<div id="bkmrk-%2A-%D8%AF%D8%B1-%D9%85%D8%AB%D8%A7%D9%84-%D8%A8%D8%A7%D9%84%D8%A7-%D9%81%D9%82%D8%B7-%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><small>\* در مثال بالا فقط ماه‌های 1، 2 و 12 محاسبه شده و در کش بوده‌اند.</small>   </div>### پاسخ‌های خطا (Error Responses)

#### خطای اعتبارسنجی (400)

```
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The selected colleague id is invalid."
  },
  "meta": { ... }
}
```

#### خطای سرور (500)

```
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Redis connection failed"
  },
  "meta": { ... }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### توضیحات فنی (Meta)

<div id="bkmrk-redis-key-parse%3A-%D9%85%D9%86%D8%B7" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **Redis Key Parse:** منطق استخراج ماه به صورت `substr($key, strrpos($key, ':') + 1)` است. این یعنی ساختار کلید باید دقیقاً با `:` جدا شده باشد.
- **Missing Data:** اگر ماه خاصی (مثلاً تیرماه) در خروجی نیست، به این معنی است که کاربر هنوز درخواست محاسبه برای آن سال/ماه را نداده است. فرانت‌اند باید این را مدیریت کند (مثلاً نمایش مقدار 0 یا دکمه "محاسبه").

</div>

# DELETE /v2/account-history/cache

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>DELETE</td><td>/v2/account-history/cache</td><td>AccountHistoryController@clearCache</td><td>authWithJwt</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API برای **پاک‌سازی دستی کش** (Invalidation) استفاده می‌شود. زمانی که داده‌های زیرساختی تغییر کرده‌اند و نیاز است محاسبات مجدداً از صفر انجام شوند، این متد صدا زده می‌شود.

<div id="bkmrk-%D8%AF%D8%A7%D9%85%D9%86%D9%87-%D9%BE%D8%A7%DA%A9%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C%3A-%D9%87%D9%85-%DA%A9" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **دامنه پاک‌سازی:** هم کش **روزانه** و هم کش **ماهانه** مربوط به همکار و سال ورودی را حذف می‌کند.
- **نتیجه:** پس از اجرای این متد، اولین درخواست بعدی به روت‌های دریافت اطلاعات (مثل `getMonthlyBalance`)، باعث محاسبه مجدد (Recalculation) خواهد شد.

  </div>### ورودی‌ها (Input Parameters)

پارامترها می‌توانند در **Query String** یا **Body (JSON)** ارسال شوند.

```
{
  "colleague_id": 108,
  "year": 1403
}
```

#### قوانین اعتبارسنجی (Validation Rules)

<div id="bkmrk-colleague_id%3A-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **colleague\_id:** الزامی | integer | موجود در جدول `colleagues`.
- **year:** الزامی | integer | بازه 1300 تا 1500.

  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7." style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- اعتبارسنجی ورودی‌ها.
- **گام اول (روزانه):** جستجوی تمام کلیدهای روزانه با الگوی `DAILY_BALANCE_KEY{colleagueId}:{year}:*` و حذف آن‌ها.
- **گام دوم (ماهانه):** جستجوی تمام کلیدهای ماهانه با الگوی `MONTHLY_BALANCE_KEY{colleagueId}:{year}:*` و حذف آن‌ها.
- شمارش تعداد کلیدهای حذف شده.
- ثبت عملیات در Log سیستم (`Log::info`).
- بازگشت تعداد کلیدهای حذف شده به کلاینت.

  </div>### پاسخ موفق (200 OK)

```
{
  "payload": {
    "cleared_keys": 25
  },
  "meta": {
    "colleague_id": 108,
    "year": 1403,
    "timestamp": "2025-12-01T17:05:00+03:30"
  }
}
```

<div id="bkmrk-%2A-%D8%B9%D8%AF%D8%AF-cleared_keys-%D9%85" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><small>\* عدد `cleared_keys` مجموع تعداد روزها و ماه‌های حذف شده است.</small>   </div>### پاسخ‌های خطا (Error Responses)

#### خطای سمت سرویس (500)

اگر اتصال به Redis در حین حذف قطع شود:

```
{
  "error": {
    "code": "CACHE_CLEAR_ERROR",
    "message": "Redis connection failed..."
  },
  "meta": { ... }
}
```

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### نکات فنی (Technical Notes)

<div id="bkmrk-non-atomic-operation" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- **Non-Atomic Operation:** عملیات حذف در دو مرحله (ابتدا روزانه، سپس ماهانه) انجام می‌شود. اگرچه احتمال آن کم است، اما در صورت بروز خطا در وسط کار، ممکن است نیمی از کش پاک شود و نیمی باقی بماند.
- **Race Condition:** بین زمانی که دستور `Redis::keys` کلیدها را پیدا می‌کند و زمانی که `Redis::del` آن‌ها را پاک می‌کند، اگر کلید جدیدی (در کسری از ثانیه) اضافه شود، آن کلید جدید پاک نخواهد شد. این موضوع در سیستم‌های با نرخ نوشتن بسیار بالا اهمیت دارد اما در اینجا قابل چشم‌پوشی است.

</div>

# POST /v2/redis-accounting/create-missing-documents

<div id="bkmrk-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### Route Info

<div id="bkmrk-method-endpoint-cont" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;"><table border="1" style="width: 100%; border-collapse: collapse; text-align: center;"><tbody><tr style="background: #f5f5f5; font-weight: bold;"><td>Method</td><td>Endpoint</td><td>Controller</td><td>Middleware</td></tr><tr><td>POST</td><td>/v2/redis-accounting/create-missing-documents</td><td>RedisAccountingController@createMissingDocuments</td><td>authWithJwt, shamsiDate</td></tr></tbody></table>

  </div>### شرح عملکرد (Functionality)

این API مسئول **بررسی وجود اسناد مفقود** و سپس **ایجاد خودکار آن‌ها** در سیستم است. منطق ساخت اسناد به‌طور کامل در سرویس `redisAccountingService` اجرا می‌شود و این روت فقط فراخوانی و مدیریت پاسخ را انجام می‌دهد.

<div id="bkmrk-%D9%87%DB%8C%DA%86-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D8%A7%DB%8C-%D9%86%DB%8C%D8%A7%D8%B2-%D9%86%D8%AF" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- هیچ ورودی‌ای نیاز ندارد.
- عملیات به‌صورت Batch اجرا می‌شود.
- خروجی شامل تعداد کل اسناد پردازش‌شده و تعداد اسناد ایجادشده است.

  </div>### ورودی‌ها (Input Parameters)

این روت **هیچ پارامتر ورودی‌ای** در Query یا Body دریافت نمی‌کند.

<div id="bkmrk--1" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### منطق اجرا (Execution Logic)

<div id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-%D9%85%D8%AA%D8%AF-" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- فراخوانی مستقیم متد `createMissingDocuments()` از سرویس.
- سرویس عملیات زیر را انجام می‌دهد: 
    - شناسایی اسناد مفقود
    - ایجاد اسناد جدید
    - محاسبه تعداد اسناد پردازش‌شده
- Controller نتیجه سرویس را در ساختار JSON استاندارد برمی‌گرداند.
- در صورت بروز هرگونه Exception، پاسخ خطای 500 بازگردانده می‌شود.

  </div>### پاسخ موفق (200 OK)

```
{
  "success": true,
  "data": {
    "total_processed": 42,
    "created": 42,
    "existing": 0
  },
  "message": "پردازش کامل شد. 42 سند ایجاد شد."
}
```

<div id="bkmrk--2" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### پاسخ‌های خطا (Error Responses)

#### خطای سمت سرویس (500)

در صورت بروز Exception در سرویس:

```
{
  "success": false,
  "message": "خطا در ایجاد اسناد: Redis connection failed"
}
```

<div id="bkmrk--3" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">  </div>### نکات فنی (Technical Notes)

<div id="bkmrk-%D8%A7%DB%8C%D9%86-%D8%B1%D9%88%D8%AA-%D9%87%DB%8C%DA%86-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-r" style="direction: rtl; font-family: Vazir, Tahoma; line-height: 1.85; text-align: justify;">- این روت هیچ عملیات Read ندارد؛ صرفاً اجرای یک Job سنگین است.
- اجرا می‌تواند بسته به حجم داده چند ثانیه طول بکشد.
- توصیه می‌شود فقط توسط کاربران سطح بالا (Admins) فراخوانی شود.

</div>

# POST /v2/redis-accounting/create-missing-documents

# Redis Accounting Sync

مستندات فنی سرویس حسابداری - نسخه 2.0

<div id="bkmrk-post-%2Fv2%2Fredis-accou" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div class="align-center" style="text-align: center; margin-bottom: 40px;">  
</div><div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 0; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 40px;"><div style="background: #f1f5f9; padding: 15px 20px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; direction: ltr;"><span style="background: #4f46e5; color: #fff; padding: 6px 12px; border-radius: 6px; font-weight: bold; font-size: 13px; margin-right: 15px; box-shadow: 0 2px 4px rgba(79, 70, 229, 0.3);">POST</span> `/v2/redis-accounting/create-missing-documents`</div><div style="padding: 20px;">این متد برای **بازیافت اطلاعات (Data Recovery)** استفاده می‌شود. سیستم تمامی کلیدهای موجود در Redis را پیمایش کرده و با دیتابیس SQL مقایسه می‌کند. اگر سندی در Redis باشد اما در دیتابیس اصلی نباشد، آن را ایجاد می‌کند.</div></div></div>### مسیر پردازش داده (Data Flow)

<div id="bkmrk-client-request-middl" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div style="margin-bottom: 50px; text-align: center;"><div style="display: flex; flex-direction: column; align-items: center; justify-content: center;"><div dir="ltr" style="background: #fff; border: 2px solid #cbd5e1; padding: 12px 25px; border-radius: 50px; font-weight: bold; color: #475569; min-width: 200px;">Client Request</div><div dir="ltr" style="height: 20px; border-left: 2px dashed #94a3b8;">  
</div><div class="align-center" dir="ltr" style="background: #fff7ed; border: 2px solid #f97316; color: #c2410c; padding: 10px 25px; border-radius: 8px; font-weight: bold; min-width: 200px; box-shadow: 0 2px 4px rgba(249, 115, 22, 0.1);">Middleware (Auth &amp; Date)</div><div dir="ltr" style="height: 20px; border-left: 2px dashed #94a3b8;">  
</div><div dir="ltr" style="background: #4f46e5; color: #fff; padding: 15px 30px; border-radius: 8px; font-weight: bold; min-width: 220px; box-shadow: 0 4px 10px rgba(79, 70, 229, 0.3);">Service: Scan Redis Keys</div><div dir="ltr" style="height: 20px; border-left: 2px dashed #94a3b8;">  
</div><div dir="ltr" style="display: flex; gap: 20px; justify-content: center;"><div style="background: #f1f5f9; padding: 15px; border-radius: 8px; width: 140px; text-align: center; border: 1px solid #cbd5e1;"><span style="display: block; font-size: 12px; color: #64748b; margin-bottom: 5px;">Exists in DB?</span> **Skip**</div><div style="background: #dcfce7; padding: 15px; border-radius: 8px; width: 140px; text-align: center; border: 2px solid #22c55e;"><span style="display: block; font-size: 12px; color: #15803d; margin-bottom: 5px;">Missing in DB?</span> **Create Doc**</div></div><div dir="ltr" style="height: 20px; border-left: 2px dashed #94a3b8;">  
</div><div dir="ltr" style="background: #1e293b; color: #f8fafc; padding: 10px 25px; border-radius: 50px; font-weight: bold; min-width: 200px;">Return Response JSON</div></div></div><div style="display: flex; gap: 30px; flex-wrap: wrap; justify-content: center; margin-bottom: 40px;"><div style="flex: 1; min-width: 300px; max-width: 500px;">  
</div></div></div>#### Header Requirements

<div id="bkmrk-key-value-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-auth" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div style="display: flex; gap: 30px; flex-wrap: wrap; justify-content: center; margin-bottom: 40px;"><div style="flex: 1; min-width: 300px; max-width: 500px;"><table border="1" class="align-center" style="width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05);"><thead style="background: #f1f5f9;"><tr><th style="padding: 12px; text-align: left; color: #475569; border-bottom: 2px solid #e2e8f0; direction: ltr;">Key</th><th style="padding: 12px; text-align: left; color: #475569; border-bottom: 2px solid #e2e8f0; direction: ltr;">Value</th><th style="padding: 12px; text-align: center; color: #475569; border-bottom: 2px solid #e2e8f0;">وضعیت</th></tr></thead><tbody><tr style="border-bottom: 1px solid #f1f5f9;"><td style="padding: 12px; font-family: monospace; color: #ec4899; direction: ltr; text-align: left;">Authorization</td><td style="padding: 12px; font-family: monospace; color: #334155; direction: ltr; text-align: left;">Bearer {Token}</td><td style="padding: 12px; text-align: center; color: #ef4444; font-weight: bold;">الزامی</td></tr><tr><td style="padding: 12px; font-family: monospace; color: #ec4899; direction: ltr; text-align: left;">Accept</td><td style="padding: 12px; font-family: monospace; color: #334155; direction: ltr; text-align: left;">application/json</td><td style="padding: 12px; text-align: center; color: #ef4444; font-weight: bold;">الزامی</td></tr></tbody></table>

</div></div></div>### نمونه پاسخ‌ها (Responses)

<div id="bkmrk-json-response-200-ok" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div style="margin-bottom: 30px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); border-radius: 8px; overflow: hidden;"><div style="background: #0f172a; color: #94a3b8; padding: 8px 15px; font-size: 12px; border-bottom: 1px solid #1e293b; display: flex; justify-content: space-between; direction: ltr;">JSON Response <span style="color: #22c55e; font-weight: bold;">200 OK</span></div></div></div>```
{
    "success": true,
    "data": {
        "total_scanned": 1540,
        "total_processed": 12,
        "details": [
            "Document #8891 synced from Redis."
        ]
    },
    "message": "پردازش کامل شد. 12 سند ایجاد شد."
}
```

<div id="bkmrk-json-response-500-se" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div style="margin-bottom: 30px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); border-radius: 8px; overflow: hidden;">  
</div><div style="margin-bottom: 30px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); border-radius: 8px; overflow: hidden;"><div style="background: #0f172a; color: #94a3b8; padding: 8px 15px; font-size: 12px; border-bottom: 1px solid #1e293b; display: flex; justify-content: space-between; direction: ltr;">JSON Response <span style="color: #ef4444; font-weight: bold;">500 Server Error</span></div></div></div>```
{
    "success": false,
    "message": "خطا در ایجاد اسناد: Redis connection timed out."
}
```

<div id="bkmrk-%E2%9A%A0%EF%B8%8F-%D9%87%D8%B4%D8%AF%D8%A7%D8%B1-%D9%BE%D8%B1%D9%81%D9%88%D8%B1%D9%85%D9%86%D8%B3-%28p" style="direction: rtl; font-family: 'Vazir', 'Tahoma', sans-serif; line-height: 1.8; background: #f8fafc; padding: 40px; border-radius: 16px; color: #334155;"><div style="margin-bottom: 30px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); border-radius: 8px; overflow: hidden;">  
</div><div style="background: #fff1f2; border: 1px solid #fda4af; border-radius: 8px; padding: 20px; display: flex; align-items: flex-start; gap: 15px;"><div style="font-size: 24px;">⚠️</div><div>**هشدار پرفورمنس (Performance):**</div><div>این عملیات **Heavy I/O** است. در صورتی که تعداد کلیدهای ردیس بسیار زیاد باشد (مثلاً بیش از ۱۰,۰۰۰ رکورد)، ممکن است اجرای این درخواست باعث کندی سیستم یا خطای Timeout شود. پیشنهاد می‌شود فقط در زمان‌های کم ترافیک اجرا شود.</div></div></div>####   

# POST /v2/redis-accounting/create-missing-documents

# Create Missing Documents

این اندپوینت در سرویس Redis Accounting وظیفه دارد همه مدارکی که باید وجود داشته باشند اما در Redis ثبت نشده‌اند را شناسایی و ایجاد کند. این عملیات معمولاً در موارد زیر استفاده می‌شود:

<div class="api-docs" id="bkmrk-%D8%A8%D8%A7%D8%B2%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%DB%8C-%D8%A7%D8%B2-">- بازسازی داده‌های از دست رفته
- یکپارچه‌سازی وضعیت حساب‌ها
- رفع inconsistency بین DB و Redis

</div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/create-missing-documents`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`organization_id`</td><td>integer</td><td>yes</td><td>شناسه سازمانی که باید مدارک آن بررسی و تکمیل شود</td></tr><tr><td>`sync_mode`</td><td>string</td><td>no</td><td>نوع همگام‌سازی. مقادیر مجاز: `full`, `delta`</td></tr><tr><td>`dry_run`</td><td>boolean</td><td>no</td><td>اگر true باشد، فقط موارد گم‌شده شناسایی می‌شود ولی ایجاد نمی‌شود</td></tr></tbody></table>

</div>## Request Example

```
{
  "organization_id": 1204,
  "sync_mode": "delta",
  "dry_run": false
}
  
```

## Response Schema

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`status`</td><td>string</td><td>وضعیت اجرای عملیات (success یا failed)</td></tr><tr><td>`created_count`</td><td>integer</td><td>تعداد مدارک جدیدی که ایجاد شده‌اند</td></tr><tr><td>`missing_ids`</td><td>array</td><td>لیست شناسه‌هایی که گم شده بودند (در حالت dry\_run همیشه پر است)</td></tr><tr><td>`execution_time_ms`</td><td>integer</td><td>مدت زمان اجرای فرآیند بر حسب میلی‌ثانیه</td></tr></tbody></table>

</div>## Response Example

```
{
  "status": "success",
  "created_count": 32,
  "missing_ids": [
    "redis:doc:93422",
    "redis:doc:93424",
    "redis:doc:93455"
  ],
  "execution_time_ms": 128
}
  
```

## Process Flow

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-load"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item">Load organization_id</div><div class="flow-arrow">↓</div><div class="flow-item">Compare Database vs Redis</div><div class="flow-arrow">↓</div><div class="flow-item">Identify Missing Documents</div><div class="flow-arrow">↓</div><div class="flow-item">dry_run ? return diff : create documents</div><div class="flow-arrow">↓</div><div class="flow-item">Return Response</div></div></div>

# POST /v2/redis-accounting/save-manual-document

# Save Manual Document

این اندپوینت یک **سند دستی (Manual Document)** را از دیتابیس بارگذاری کرده و ردیف‌های آن را در Redis ذخیره می‌کند. این عملیات زمانی استفاده می‌شود که بخواهیم وضعیت یک سند دستی در Redis بازسازی یا هماهنگ‌سازی شود.

## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/save-manual-document`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`id`</td><td>integer</td><td>yes</td><td>شناسه سند دستی **(manual\_documents.id)**</td></tr></tbody></table>

</div>## Request Example

```
{
  "id": 9234
}
  
```

## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت انجام عملیات</td></tr><tr><td>`message`</td><td>string</td><td>پیام وضعیت ذخیره</td></tr><tr><td>`data.manual_document_id`</td><td>integer</td><td>شناسه سندی که پردازش شد</td></tr><tr><td>`data.success`</td><td>boolean</td><td>نتیجه واقعی ذخیره‌سازی از سرویس</td></tr><tr><td>`data.message`</td><td>string</td><td>پیام بازگشتی از سرویس ذخیره‌سازی</td></tr></tbody></table>

</div>```
{
  "success": true,
  "message": "Saved 3 documents to Redis",
  "data": {
    "success": true,
    "message": "Saved 3 documents to Redis",
    "manual_document_id": 9234
  }
}
  
```

## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "id": [
      "The id field is required"
    ]
  }
}
  
```

## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در ذخیره سند: <error details>"
}
  
```

## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%28id%29"><div class="flowchart"><div class="flow-item">Receive Request (id)</div><div class="flow-arrow">↓</div><div class="flow-item">Validate id</div><div class="flow-arrow">↓</div><div class="flow-item">Load Manual Document Details</div><div class="flow-arrow">↓</div><div class="flow-item">Loop over balance rows</div><div class="flow-arrow">↓</div><div class="flow-item">saveDocument(...) for each row</div><div class="flow-arrow">↓</div><div class="flow-item">Return result with saved count</div></div></div>

# POST /v2/redis-accounting/save-document

# Save Document

این اندپوینت یک **سند حسابداری** را در هسته حسابداری Redis ذخیره می‌کند. عملیات شامل ایجاد Hash سند، ساخت ایندکس‌های زمانی و حسابداری، و به‌روزرسانی مانده‌ها در سه سطح گروه/کل/حساب است. این متد ستون فقرات سیستم حسابداری Redis محسوب می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/save-document`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`serial`</td><td>integer</td><td>optional</td><td>شماره سریال سند. اگر ارسال نشود، سیستم خودش تولید می‌کند.</td></tr><tr><td>`description`</td><td>string</td><td>yes</td><td>شرح سند</td></tr><tr><td>`jalaliDate`</td><td>string</td><td>yes</td><td>تاریخ شمسی با فرمت **YYYY-MM-DD** (الزامی)</td></tr><tr><td>`debtor`</td><td>number</td><td>optional</td><td>مبلغ بدهکار (پیش‌فرض: 0)</td></tr><tr><td>`creditor`</td><td>number</td><td>optional</td><td>مبلغ بستانکار (پیش‌فرض: 0)</td></tr><tr><td>`group`</td><td>integer</td><td>yes</td><td>شناسه گروه حسابداری</td></tr><tr><td>`general`</td><td>integer</td><td>yes</td><td>شناسه کل</td></tr><tr><td>`account`</td><td>integer</td><td>yes</td><td>شناسه معین</td></tr><tr><td>`subsidiary`</td><td>integer</td><td>yes</td><td>شناسه تفضیلی</td></tr><tr><td>`datetime`</td><td>string</td><td>optional</td><td>تاریخ‌زمان میلادی (اگر ارسال نشود، now() استفاده می‌شود)</td></tr></tbody></table>

</div>## Request Example

```
{
  "serial": 120,
  "description": "خرید قطعات",
  "jalaliDate": "1403-09-01",
  "debtor": 250000,
  "creditor": 0,
  "group": 10,
  "general": 101,
  "account": 1011,
  "subsidiary": 1011003
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`status`</td><td>boolean</td><td>وضعیت نهایی ذخیره سند</td></tr><tr><td>`id`</td><td>integer</td><td>شناسه سند ایجادشده در Redis</td></tr><tr><td>`serial`</td><td>integer</td><td>سریال اختصاص داده‌شده</td></tr><tr><td>`time`</td><td>float</td><td>مدت زمان اجرای عملیات (ms)</td></tr></tbody></table>

</div>```
{
  "status": true,
  "serial": 120,
  "id": 934221,
  "time": 1.78
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "status": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "jalaliDate": [
      "The jalaliDate must match YYYY-MM-DD"
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "status": false,
  "message": "خطا در ذخیره سند",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل سند ذخیره‌شده</td></tr><tr><td>`accounting:docs:by_datetime`</td><td>SortedSet</td><td>مرتب‌سازی تمام اسناد بر اساس timestamp</td></tr><tr><td>`accounting:docs:group:{groupId}`</td><td>Set</td><td>ایندکس اسناد گروه</td></tr><tr><td>`accounting:docs:general:{generalId}`</td><td>Set</td><td>ایندکس سطح کل</td></tr><tr><td>`accounting:docs:account:{accountId}`</td><td>Set</td><td>ایندکس سطح معین</td></tr><tr><td>`accounting:docs:subsidiary:{subsidiaryId}`</td><td>Set</td><td>ایندکس سطح تفضیلی</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%E2%86%93-va"><div class="flowchart"><div class="flow-item">Receive Request</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Input</div><div class="flow-arrow">↓</div><div class="flow-item">Convert Jalali → Gregorian</div><div class="flow-arrow">↓</div><div class="flow-item">Normalize Debtor/Creditor</div><div class="flow-arrow">↓</div><div class="flow-item">Generate Serial (if needed)</div><div class="flow-arrow">↓</div><div class="flow-item">Create Hash &amp; Indexes in Redis</div><div class="flow-arrow">↓</div><div class="flow-item">Update Balances</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final Response</div></div></div>

# DELETE /v2/redis-accounting/delete-document

# Delete Document

این اندپوینت یک **سند حسابداری** را از هسته Redis حذف می‌کند. عملیات شامل حذف Hash سند، حذف شناسه از تمامی ایندکس‌ها، و پاک‌سازی کامل کلیدهای بالانس مرتبط با گروه/کل/معین/تفضیلی است. توجه: در منطق فعلی سیستم، بالانس‌ها **recalculate نمی‌شوند** و به‌جای آن حذف کامل می‌گردند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/delete-document`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`id`</td><td>integer</td><td>yes</td><td>شناسه سندی که باید حذف شود</td></tr></tbody></table>

</div>## Request Example

```
{
  "id": 934221
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی حذف سند</td></tr><tr><td>`message`</td><td>string</td><td>پیام موفقیت‌آمیز</td></tr></tbody></table>

</div>```
{
  "success": true,
  "message": "سند با موفقیت حذف شد"
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "id": [
      "The id field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در حذف سند: Internal server error message..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>سند ذخیره‌شده که باید حذف شود</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>حذف شناسه از ایندکس تاریخ میلادی</td></tr><tr><td>`accounting:docs:by_jalali_date`</td><td>SortedSet</td><td>حذف شناسه از ایندکس تاریخ شمسی</td></tr><tr><td>`accounting:docs:group:{groupId}`</td><td>Set</td><td>ایندکس سطح گروه — حذف شناسه</td></tr><tr><td>`accounting:docs:general:{generalId}`</td><td>Set</td><td>ایندکس سطح کل — حذف شناسه</td></tr><tr><td>`accounting:docs:account:{accountId}`</td><td>Set</td><td>ایندکس سطح معین — حذف شناسه</td></tr><tr><td>`accounting:docs:subsidiary:{subsidiaryId}`</td><td>Set</td><td>ایندکس سطح تفضیلی — حذف شناسه</td></tr><tr><td>`balance:group:{groupId}`</td><td>Hash</td><td>حذف کامل مانده سطح گروه</td></tr><tr><td>`balance:general:{generalId}`</td><td>Hash</td><td>حذف کامل مانده سطح کل</td></tr><tr><td>`balance:account:{accountId}`</td><td>Hash</td><td>حذف کامل مانده سطح معین</td></tr><tr><td>`balance:subsidiary:{subsidiaryId}`</td><td>Hash</td><td>حذف کامل مانده سطح تفضیلی</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%E2%86%93-va"><div class="flowchart"><div class="flow-item">Receive Request</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Input (id Required)</div></div></div>

# DELETE /v2/redis-accounting/clear-all

# Clear All Documents

این اندپوینت **تمام اسناد حسابداری** ذخیره‌شده در Redis را حذف می‌کند. عملیات شامل حذف تمامی Hashها، پاک‌سازی کامل تمام ایندکس‌ها (Set / Sorted Set)، و حذف همه کلیدهای بالانس در سطوح گروه، کل، معین و تفضیلی است. این عملیات یک **عملیات مخرب و غیرقابل بازگشت** محسوب می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/clear-all`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Request Body Schema

این اندپوینت هیچ ورودی‌ای نمی‌پذیرد.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>—</td><td>—</td><td>—</td><td>بدون ورودی</td></tr></tbody></table>

</div>## Request Example

```
DELETE /v2/redis-accounting/clear-all
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت عملیات</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی</td></tr></tbody></table>

</div>```
{
  "success": true,
  "message": "تمام اسناد از Redis حذف شدند"
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در حذف اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:doc:*`</td><td>Hash</td><td>تمام اسناد ذخیره‌شده — همگی حذف می‌شوند</td></tr><tr><td>`accounting:docs:* `</td><td>Set / SortedSet</td><td>تمام ایندکس‌ها شامل تاریخ، گروه، کل، معین، تفضیلی — حذف کامل</td></tr><tr><td>`balance:group:*`</td><td>Hash</td><td>تمام مانده‌های سطح گروه — حذف کامل</td></tr><tr><td>`balance:general:*`</td><td>Hash</td><td>تمام مانده‌های سطح کل — حذف کامل</td></tr><tr><td>`balance:account:*`</td><td>Hash</td><td>تمام مانده‌های سطح معین — حذف کامل</td></tr><tr><td>`balance:subsidiary:*`</td><td>Hash</td><td>تمام مانده‌های سطح تفضیلی — حذف کامل</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%E2%86%93-ca"><div class="flowchart"><div class="flow-item">Receive Request</div><div class="flow-arrow">↓</div><div class="flow-item">Call redisAccountingService.clearAllDocuments()</div><div class="flow-arrow">↓</div><div class="flow-item">Delete All Document Hashes</div><div class="flow-arrow">↓</div><div class="flow-item">Delete All Indexes (Sets &amp; ZSets)</div><div class="flow-arrow">↓</div><div class="flow-item">Delete All Balance Keys</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final Response</div></div></div>

# GET /v2/redis-accounting/documents/subsidiary

# Get Documents by Subsidiary

این اندپوینت لیست **اسناد حسابداری مربوط به یک کد تفضیلی** را از Redis برمی‌گرداند. دیتای خروجی شامل اسناد، مانده فعلی، تعداد کل اسناد، و در صورت درخواست، مانده تجمیعی از ابتدای سال تا تاریخ مشخص است. عملیات بر اساس ایندکس Redis `accounting:docs:subsidiary:{code}` انجام می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/subsidiary`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Query Parameters Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد تفضیلی که اسناد باید براساس آن فیلتر شوند</td></tr><tr><td>`from_year_start`</td><td>boolean</td><td>optional</td><td>اگر true باشد، مانده از ابتدای سال شمسی تا امروز یا تا تاریخ مشخص محاسبه می‌شود</td></tr><tr><td>`to_date`</td><td>string (YYYY-MM-DD)</td><td>optional</td><td>تاریخ پایانی برای محاسبه year\_start\_balance (پیش‌فرض: تاریخ امروز)</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/subsidiary?code=1011003&from_year_start=true&to_date=1403-09-15
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

ساختار خروجی:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی درخواست</td></tr><tr><td>`data.documents`</td><td>array</td><td>لیست اسناد؛ توجه: مقدار برگشتی شامل **\[diffProcess, docs\]** است</td></tr><tr><td>`data.balance`</td><td>object</td><td>مانده فعلی Redis برای تفضیلی</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>تعداد کل اسناد برگردانده‌شده</td></tr><tr><td>`data.year_start_balance`</td><td>object</td><td>فقط اگر from\_year\_start=true باشد</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      [
        [1733301120.123, 1733301120.125, ...],
        [
          {
            "id": "934221",
            "date": "1403-09-01",
            "debit": "250000",
            "credit": "0",
            "group": "10",
            "general": "101",
            "account": "1011",
            "subsidiary": "1011003"
          }
        ]
      ],
      "balance": {
        "debit": 250000,
        "credit": 0,
        "remaining": 250000
      },
      "total_documents": 1,
      "year_start_balance": {
        "debit": 250000,
        "credit": 0,
        "remaining": 250000,
        "year_start": "1403-01-01",
        "to_date": "1403-09-15"
      }
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "code": [
      "The code field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:subsidiary:{code}`</td><td>Set</td><td>لیست شناسه اسناد مرتبط با این تفضیلی</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل سند</td></tr><tr><td>`balance:subsidiary:{code}`</td><td>Hash</td><td>مانده فعلی سندهای مربوط به این تفضیلی</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>ایندکس تمام اسناد بر اساس تاریخ</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%2B-va"><div class="flowchart"><div class="flow-item">Receive Request + Validate Inputs</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch IDs from Redis Set (accounting:docs:subsidiary:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">Load Each Document Hash (accounting:doc:{id})</div><div class="flow-arrow">↓</div><div class="flow-item">Sort Documents by Date</div><div class="flow-arrow">↓</div><div class="flow-item">Load Current Balance (balance:subsidiary:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">If from_year_start = true → Calculate Year-Start Balance</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Response</div></div></div>

# GET /v2/redis-accounting/documents/account

# Get Documents by Account

این اندپوینت لیست **اسناد حسابداری مربوط به یک کد معین** را از Redis بازیابی می‌کند. خروجی شامل اسناد، مانده فعلی، تعداد اسناد، و در صورت فعال بودن، ماندهٔ تجمیعی از ابتدای سال است. عملیات بر اساس ایندکس Redis `accounting:docs:account:{code}` انجام می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/account`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Content-Type:** application/json</div></div></div>## Query Parameters Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد معین که اسناد براساس آن فیلتر می‌شوند</td></tr><tr><td>`from_year_start`</td><td>boolean</td><td>optional</td><td>اگر true باشد، مانده تجمیعی از ابتدای سال شمسی محاسبه می‌شود</td></tr><tr><td>`to_date`</td><td>string (YYYY-MM-DD)</td><td>optional</td><td>تاریخ پایانی برای محاسبه year\_start\_balance (پیش‌فرض: تاریخ امروز)</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/account?code=1011&from_year_start=true
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

ساختار خروجی:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی</td></tr><tr><td>`data.documents`</td><td>array</td><td>خروجی **دقیقاً شامل \[diffProcess, docs\]** است diffProcess آرایه‌ای از timestamps docs آرایه سندها</td></tr><tr><td>`data.balance`</td><td>object</td><td>مانده فعلی Redis برای حساب معین</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>توجه: **در سیستم فعلی مقدار آن ۲ است** چون count روی آرایه دوبخشی \[diffProcess, docs\] اعمال شده (این یک نقص منطقی در پیاده‌سازی است)</td></tr><tr><td>`data.year_start_balance`</td><td>object</td><td>فقط اگر from\_year\_start=true ارسال می‌شود</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      [
        [1733301120.101, 1733301120.103, ...],
        [
          {
            "id": "2210",
            "date": "1403-05-01",
            "debit": "500000",
            "credit": "0",
            "group": "10",
            "general": "101",
            "account": "1011",
            "subsidiary": "1011001"
          }
        ]
      ],
      "balance": {
        "debit": 500000,
        "credit": 0,
        "remaining": 500000
      },
      "total_documents": 2,
      "year_start_balance": {
        "debit": 500000,
        "credit": 0,
        "remaining": 500000,
        "year_start": "1403-01-01",
        "to_date": "1403-09-15"
      }
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "code": [
      "The code field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:account:{code}`</td><td>Set</td><td>لیست شناسه اسناد مربوط به این معین</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل سند</td></tr><tr><td>`balance:account:{code}`</td><td>Hash</td><td>مانده حساب در Redis</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>ایندکس سراسری تمام اسناد بر اساس تاریخ برای year\_start\_balance</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-receive-request-%2B-va"><div class="flowchart"><div class="flow-item">Receive Request + Validate Inputs</div><div class="flow-arrow">↓</div><div class="flow-item">Load Document IDs (accounting:docs:account:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Every Document Hash (accounting:doc:{id})</div><div class="flow-arrow">↓</div><div class="flow-item">Sort Documents by Date</div><div class="flow-arrow">↓</div><div class="flow-item">Read Current Balance (balance:account:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">If from_year_start=true → compute getBalanceFromYearStart()</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Response</div></div></div>

# GET /v2/redis-accounting/documents/general

# Get Documents by General

این اندپوینت اسناد حسابداری مرتبط با یک **کد کل (General Code)** را از Redis بازیابی می‌کند. خروجی شامل: لیست اسناد، مانده فعلی، تعداد اسناد، و اگر درخواست شده باشد، مانده تجمیعی از ابتدای سال است. داده‌ها از ایندکس Redis با الگو `accounting:docs:general:{code}` استخراج می‌شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/general`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Auth:** Bearer Token (Required)</div><div>**Middleware:** shamsiDate</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد کل هدف</td></tr><tr><td>`from_year_start`</td><td>boolean</td><td>optional</td><td>در صورت true بودن، مانده از ابتدای سال شمسی تا تاریخ مشخص محاسبه می‌شود</td></tr><tr><td>`to_date`</td><td>string (YYYY-MM-DD)</td><td>optional</td><td>تاریخ پایانی جهت محاسبه year\_start\_balance (پیش‌فرض: امروز)</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/general?code=101&from_year_start=true
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

ساختار خروجی:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی</td></tr><tr><td>`data.documents`</td><td>array</td><td>مقدار برگشتی **دقیقاً** به‌صورت زیر است:  
**\[ diffProcess\[\], documents\[\] \]**  
diffProcess شامل timestamps مراحل عملکرد Redis است.  
documents آرایه کامل اسناد می‌باشد.</td></tr><tr><td>`data.balance`</td><td>object</td><td>مانده فعلی کل در Redis</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>توجه: مقدار فعلی **همیشه ۲** است چون count روی آرایه دوبخشی اعمال می‌شود (این یک ایراد منطقی در پیاده‌سازی فعلی API است)</td></tr><tr><td>`data.year_start_balance`</td><td>object</td><td>در صورت درخواست (from\_year\_start = true)</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      [
        [1733301120.201, 1733301120.203, ...],
        [
          {
            "id": "88401",
            "date": "1403-03-15",
            "debit": "150000",
            "credit": "0",
            "group": "10",
            "general": "101",
            "account": "1011",
            "subsidiary": "1011001"
          }
        ]
      ],
      "balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000
      },
      "total_documents": 2,
      "year_start_balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000,
        "year_start": "1403-01-01",
        "to_date": "1403-09-15"
      }
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "code": [
      "The code field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:general:{code}`</td><td>Set</td><td>لیست ID اسناد مرتبط با کد کل</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل سند</td></tr><tr><td>`balance:general:{code}`</td><td>Hash</td><td>مانده فعلی Redis برای کل</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>ایندکس سراسری برای year\_start\_balance</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-inputs-%E2%86%93-lo"><div class="flowchart"><div class="flow-item">Validate Inputs</div><div class="flow-arrow">↓</div><div class="flow-item">Load IDs (accounting:docs:general:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Documents (accounting:doc:{id})</div><div class="flow-arrow">↓</div><div class="flow-item">Sort by Document Date</div><div class="flow-arrow">↓</div><div class="flow-item">Get Balance (balance:general:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">If from_year_start=true → Compute Year-Start Balance</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Response</div></div></div>

# GET /v2/redis-accounting/documents/group

# Get Documents by Group

این اندپوینت وظیفه دارد تمام اسناد حسابداری مرتبط با یک **کد گروه (Group Code)** را از Redis بازیابی کند. داده‌ها شامل اسناد، مانده فعلی، تعداد اسناد، و در صورت درخواست، مانده تجمیعی از ابتدای سال شمسی است. استخراج داده از Redis بر اساس الگوی زیر انجام می‌شود: `accounting:docs:group:{code}`.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/group`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد گروه موردنظر</td></tr><tr><td>`from_year_start`</td><td>boolean</td><td>optional</td><td>اگر true باشد، ماندهٔ تجمیعی از ابتدای سال شمسی محاسبه می‌شود</td></tr><tr><td>`to_date`</td><td>string (YYYY-MM-DD)</td><td>optional</td><td>تاریخ نهایی جهت محاسبه year\_start\_balance (پیش‌فرض: امروز)</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/group?code=10&from_year_start=true
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

ساختار خروجی دقیقاً مطابق پیاده‌سازی فعلی RedisAccountingService است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت عملیات</td></tr><tr><td>`data.documents`</td><td>array</td><td>آرایه دوبخشی: **\[ diffProcess\[\], docs\[\] \]**  
diffProcess شامل timestamps اجرای مراحل Redis است.  
docs شامل اطلاعات کامل هر سند است.</td></tr><tr><td>`data.balance`</td><td>object</td><td>ماندهٔ فعلی گروه در Redis</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>مقدار همیشه **۲** است چون count روی آرایه دوبخشی اعمال شده (نقص منطقی در سیستم فعلی)</td></tr><tr><td>`data.year_start_balance`</td><td>object</td><td>فقط اگر from\_year\_start=true ارسال می‌شود</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      [
        [1733301001.102, 1733301001.105, ...],
        [
          {
            "id": "88401",
            "date": "1403-03-15",
            "debit": "150000",
            "credit": "0",
            "group": "10",
            "general": "101",
            "account": "1011",
            "subsidiary": "1011001"
          }
        ]
      ],
      "balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000
      },
      "total_documents": 2,
      "year_start_balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000,
        "year_start": "1403-01-01",
        "to_date": "1403-09-15"
      }
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "code": [
      "The code field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:group:{code}`</td><td>Set</td><td>شناسه اسناد مرتبط با این گروه</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل هر سند</td></tr><tr><td>`balance:group:{code}`</td><td>Hash</td><td>مانده فعلی گروه در Redis</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>برای year\_start\_balance استفاده می‌شود</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-inputs-%E2%86%93-fe"><div class="flowchart"><div class="flow-item">Validate Inputs</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch IDs (accounting:docs:group:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">Load Each Document (accounting:doc:{id})</div><div class="flow-arrow">↓</div><div class="flow-item">Sort by Date Field</div><div class="flow-arrow">↓</div><div class="flow-item">Get Redis Balance (balance:group:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">If requested → Calculate Year-Start Balance</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Payload</div></div></div>

# GET /v2/redis-accounting/documents/group

# Get Documents by Group

این اندپوینت وظیفه دارد تمام اسناد حسابداری مرتبط با یک **کد گروه (Group Code)** را از Redis بازیابی کند. داده‌ها شامل اسناد، مانده فعلی، تعداد اسناد، و در صورت درخواست، مانده تجمیعی از ابتدای سال شمسی است. استخراج داده از Redis بر اساس الگوی زیر انجام می‌شود: `accounting:docs:group:{code}`.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/group`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد گروه موردنظر</td></tr><tr><td>`from_year_start`</td><td>boolean</td><td>optional</td><td>اگر true باشد، ماندهٔ تجمیعی از ابتدای سال شمسی محاسبه می‌شود</td></tr><tr><td>`to_date`</td><td>string (YYYY-MM-DD)</td><td>optional</td><td>تاریخ نهایی جهت محاسبه year\_start\_balance (پیش‌فرض: امروز)</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/group?code=10&from_year_start=true
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

ساختار خروجی دقیقاً مطابق پیاده‌سازی فعلی RedisAccountingService است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت عملیات</td></tr><tr><td>`data.documents`</td><td>array</td><td>آرایه دوبخشی: **\[ diffProcess\[\], docs\[\] \]**  
diffProcess شامل timestamps اجرای مراحل Redis است.  
docs شامل اطلاعات کامل هر سند است.</td></tr><tr><td>`data.balance`</td><td>object</td><td>ماندهٔ فعلی گروه در Redis</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>مقدار همیشه **۲** است چون count روی آرایه دوبخشی اعمال شده (نقص منطقی در سیستم فعلی)</td></tr><tr><td>`data.year_start_balance`</td><td>object</td><td>فقط اگر from\_year\_start=true ارسال می‌شود</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      [
        [1733301001.102, 1733301001.105, ...],
        [
          {
            "id": "88401",
            "date": "1403-03-15",
            "debit": "150000",
            "credit": "0",
            "group": "10",
            "general": "101",
            "account": "1011",
            "subsidiary": "1011001"
          }
        ]
      ],
      "balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000
      },
      "total_documents": 2,
      "year_start_balance": {
        "debit": 150000,
        "credit": 0,
        "remaining": 150000,
        "year_start": "1403-01-01",
        "to_date": "1403-09-15"
      }
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "code": [
      "The code field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:group:{code}`</td><td>Set</td><td>شناسه اسناد مرتبط با این گروه</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل هر سند</td></tr><tr><td>`balance:group:{code}`</td><td>Hash</td><td>مانده فعلی گروه در Redis</td></tr><tr><td>`accounting:docs:by_date`</td><td>SortedSet</td><td>برای year\_start\_balance استفاده می‌شود</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-inputs-%E2%86%93-fe"><div class="flowchart"><div class="flow-item">Validate Inputs</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch IDs (accounting:docs:group:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">Load Each Document (accounting:doc:{id})</div><div class="flow-arrow">↓</div><div class="flow-item">Sort by Date Field</div><div class="flow-arrow">↓</div><div class="flow-item">Get Redis Balance (balance:group:{code})</div><div class="flow-arrow">↓</div><div class="flow-item">If requested → Calculate Year-Start Balance</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Payload</div></div></div>

# GET /v2/redis-accounting/documents/date-range

# Get Documents By Date Range

این اندپوینت اسناد حسابداری را بر اساس یک بازهٔ تاریخ شمسی (Jalali) از Redis استخراج می‌کند. استخراج اسناد بر اساس ایندکس تاریخ شمسی `accounting:docs:by_jalali_date` انجام شده و خروجی شامل لیست اسناد، تعداد کل اسناد و اطلاعات معتبر از بازهٔ ارسال‌شده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/documents/date-range`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`start_date`</td><td>string (YYYY-MM-DD)</td><td>yes</td><td>تاریخ شمسی شروع بازه</td></tr><tr><td>`end_date`</td><td>string (YYYY-MM-DD)</td><td>yes</td><td>تاریخ شمسی پایان بازه</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/documents/date-range?start_date=1403-01-01&end_date=1403-03-31
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-start_date%3A-required">- start\_date: required + regex YYYY-MM-DD
- end\_date: required + regex YYYY-MM-DD
- start\_date &lt;= end\_date
- هر دو تاریخ باید با Jalalian قابل تبدیل باشند

</div>## Response (Success)

ساختار خروجی بر اساس منطق RedisAccountingService است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت عملیات</td></tr><tr><td>`data.documents`</td><td>array</td><td>لیست کامل اسناد فلت (بدون diffProcess)</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>تعداد دقیق اسناد بازگردانده‌شده</td></tr><tr><td>`data.date_range`</td><td>object</td><td>جزئیات بازه شمسی و میلادی مبنا</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "documents": [
      {
        "id": "88401",
        "date": "1403-02-15",
        "debit": "450000",
        "credit": "0",
        "group": "10",
        "general": "101",
        "account": "1011",
        "subsidiary": "1011001"
      }
    ],
    "total_documents": 1,
    "date_range": {
      "start": "1403-01-01",
      "end": "1403-03-31",
      "start_shamsi": "1403/01/01",
      "end_shamsi": "1403/03/31"
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی - تاریخ باید به صورت شمسی (YYYY-MM-DD) باشد",
  "errors": {
    "start_date": [
      "The start_date format is invalid."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Start &gt; End)

```
{
  "success": false,
  "message": "تاریخ شروع باید کوچکتر یا مساوی تاریخ پایان باشد"
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت اسناد: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--5"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`accounting:docs:by_jalali_date`</td><td>Sorted Set</td><td>ایندکس اصلی بازه تاریخ؛ score = YYYYMMDD شمسی</td></tr><tr><td>`accounting:doc:{id}`</td><td>Hash</td><td>اطلاعات کامل سند</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-input-forma"><div class="flowchart"><div class="flow-item">Validate Input Format (Regex)</div><div class="flow-arrow">↓</div><div class="flow-item">Parse Jalali Dates (Jalalian)</div><div class="flow-arrow">↓</div><div class="flow-item">Validate start_date &lt;= end_date</div><div class="flow-arrow">↓</div><div class="flow-item">Convert YYYY-MM-DD → YYYYMMDD scores</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch IDs from ZSET by Score Range</div><div class="flow-arrow">↓</div><div class="flow-item">Load Each Document via HGETALL</div><div class="flow-arrow">↓</div><div class="flow-item">Sort Docs by Jalali Date</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Payload</div></div></div>

# GET /v2/redis-accounting/balance

# Get Redis Balance

این اندپوینت مانده حساب را بر اساس نوع و کد حساب از Redis بازیابی می‌کند. مانده‌ها در کلیدهای `balance:{type}:{code}` ذخیره می‌شوند و شامل سه مقدار **debit**، **credit** و **remaining** هستند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/balance`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`type`</td><td>string</td><td>yes</td><td>یکی از مقادیر: `group`، `general`، `account`، `subsidiary`</td></tr><tr><td>`code`</td><td>integer</td><td>yes</td><td>کد حساب موردنظر</td></tr></tbody></table>

</div>## Request Example

```
GET /v2/redis-accounting/balance?type=group&code=10
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت</td></tr><tr><td>`data.debit`</td><td>float</td><td>مقدار بدهکار</td></tr><tr><td>`data.credit`</td><td>float</td><td>مقدار بستانکار</td></tr><tr><td>`data.remaining`</td><td>float</td><td>debit - credit</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "debit": 450000,
    "credit": 150000,
    "remaining": 300000
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## No Balance Found

در صورتی که کلید `balance:{type}:{code}` وجود نداشته باشد، خروجی بدون ارور و به شکل زیر است:

```
{
  "success": true,
  "data": {
    "debit": 0,
    "credit": 0,
    "remaining": 0
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "type": [
      "The type field is required."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت مانده: Redis connection error..."
}
  
```

<div class="api-docs" id="bkmrk--5"></div>## Internal Redis Architecture

<div class="api-docs" id="bkmrk-key-pattern-type-des"><table class="schema-table"><thead><tr><th>Key Pattern</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`balance:{type}:{code}`</td><td>Hash</td><td>ساختار مانده حساب - debit
- credit
- remaining = debit - credit

</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-inputs-%28typ"><div class="flowchart"><div class="flow-item">Validate Inputs (type, code)</div><div class="flow-arrow">↓</div><div class="flow-item">Read from Redis (HGETALL)</div><div class="flow-arrow">↓</div><div class="flow-item">Convert Fields to float</div><div class="flow-arrow">↓</div><div class="flow-item">Compute remaining = debit - credit</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Payload</div></div></div>

# GET /v2/redis-accounting/stats

# Get Redis Statistics

این اندپوینت آمار کلی Redis را در حوزه سیستم حسابداری استخراج می‌کند. شامل تعداد اسناد، تعداد ایندکس‌ها، تعداد مانده‌ها و میزان حافظه مصرف شده Redis. این روت برای استفاده داخلی (Ops, Engineering, Monitoring) طراحی شده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fredis-accou"><div class="endpoint-info"><div>**URL:** `/v2/redis-accounting/stats`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div><div>**Controller:** RedisAccountingController@getRedisStats</div></div></div>## Authentication

این اندپوینت کاملاً محافظت‌شده است. فقط کاربران احراز هویت شده و دارای JWT معتبر دسترسی دارند.

<div class="api-docs" id="bkmrk--1"></div>## Request Parameters

بدون ورودی. این روت هیچ query یا body ندارد.

<div class="api-docs" id="bkmrk--2"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>تعداد کل اسناد ذخیره‌شده در Redis (کلیدهای accounting:doc:\*)</td></tr><tr><td>`data.total_indexes`</td><td>integer</td><td>تعداد کل ایندکس‌های Redis (کلیدهای accounting:docs:\*)</td></tr><tr><td>`data.total_balances`</td><td>integer</td><td>تعداد کل کلیدهای مانده (balance:\*)</td></tr><tr><td>`data.memory_usage`</td><td>string</td><td>حجم حافظه مصرف‌شده Redis به صورت human-readable (مثلاً 5.21M)</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "total_documents": 3412,
    "total_indexes": 128,
    "total_balances": 904,
    "memory_usage": "6.12M"
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در دریافت آمار: Redis connection lost"
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Architecture Notes

برای استخراج آمار، سرویس داخلی `redisAccountingService` از دستور `Redis::keys` استفاده می‌کند. این عملیات نمایی و غیرمقیاس‌پذیر است و فقط برای محیط‌های داخلی مناسب است (Production-safe only when dataset is controlled).

<div class="api-docs" id="bkmrk-category-key-pattern"><table class="schema-table"><thead><tr><th>Category</th><th>Key Pattern</th><th>Description</th></tr></thead><tbody><tr><td>Documents</td><td>`accounting:doc:*`</td><td>سندهای ذخیره‌شده</td></tr><tr><td>Indexes</td><td>`accounting:docs:*`</td><td>تمام ZSetها و مجموعه‌های ایندکسی</td></tr><tr><td>Balances</td><td>`balance:*`</td><td>مانده‌های سطوح مختلف</td></tr></tbody></table>

</div>

# POST /v2/batch-accounting/process/date-range

# Batch Process Documents by Date Range

این اندپوینت پردازش دسته‌ای اسناد حسابداری را در یک بازه تاریخی شمسی انجام می‌دهد. تمام اسنادی که در تاریخ مشخص‌شده قرار دارند از دیتابیس فراخوانی شده، در چانک‌های کنترل‌شده پردازش می‌شوند و داده‌های Redis Accounting شامل اسناد و مانده‌ها آپدیت می‌گردند. این عملیات برای عملیات‌های Rebuild، Sync و Mass‑Recalculate استفاده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/process/date-range`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div><div>**Controller:** BatchAccountingController@processByDateRange</div></div></div>## Authentication

این اندپوینت کاملاً محافظت‌شده بوده و فقط کاربران دارای JWT معتبر می‌توانند آن را اجرا کنند. در صورت نامعتبر بودن Token، خطاهای استاندارد میدلور AuthWithJWT بازگردانده می‌شود.

<div class="api-docs" id="bkmrk--1"></div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`start_date`</td><td>string (shamsi)</td><td>yes</td><td>تاریخ شروع بازه (فرمت شمسی YYYY-MM-DD) — توسط middleware اعتبارسنجی می‌شود</td></tr><tr><td>`end_date`</td><td>string (shamsi)</td><td>yes</td><td>تاریخ پایان بازه (فرمت شمسی YYYY-MM-DD)</td></tr><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>تعداد پردازش در هر چانک. حداقل 10، حداکثر 1000. پیش‌فرض: 100</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/process/date-range
Content-Type: application/json

{
  "start_date": "1403-01-01",
  "end_date": "1403-01-31",
  "batch_size": 200
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>True در صورت موفقیت</td></tr><tr><td>`data.total_processed`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده در همه چانک‌ها</td></tr><tr><td>`data.total_errors`</td><td>integer</td><td>تعداد خطاهای رخ‌داده در پردازش Batchها</td></tr><tr><td>`data.batch_results`</td><td>array</td><td>خروجی کامل تمامی Batchها</td></tr><tr><td>`data.date_range`</td><td>object</td><td>محدوده تاریخی پردازش‌شده</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 283,
    "total_errors": 2,
    "batch_results": [
      {
        "processed": 100,
        "errors": 0
      },
      {
        "processed": 98,
        "errors": 1
      },
      {
        "processed": 85,
        "errors": 1
      }
    ],
    "date_range": {
      "start": "1403-01-01",
      "end": "1403-01-31"
    }
  },
  "message": "پردازش کامل شد. 283 سند پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "start_date": [
      "The start_date field format is invalid."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Response (Shamsi Date Middleware Error)

```
{
  "success": false,
  "message": "فیلد start_date باید تاریخ شمسی معتبر به فرمت YYYY-MM-DD باشد",
  "field": "start_date",
  "received_value": "2024-01-01",
  "example": "1404-05-27"
}
  
```

<div class="api-docs" id="bkmrk--5"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش: Redis connection timeout"
}
  
```

<div class="api-docs" id="bkmrk--6"></div>## Internal Processing Architecture

این عملیات تمام اسناد جدول `manual_documents` را در بازه تاریخی مشخص دریافت می‌کند، سپس آنها را در چانک‌های کنترل‌شده پردازش می‌کند. بین هر Batch یک توقف 0.1 ثانیه‌ای برای جلوگیری از فشار Redis انجام می‌شود.

<div class="api-docs" id="bkmrk-module-description-m"><table class="schema-table"><thead><tr><th>Module</th><th>Description</th></tr></thead><tbody><tr><td>`manual_documents`</td><td>فیلتر بر اساس تاریخ و وضعیت (status != 5)</td></tr><tr><td>`processBatch()`</td><td>پردازش هر چانک (ذخیره سند، بروزرسانی مانده‌ها، آپدیت ایندکس‌ها)</td></tr><tr><td>`usleep(100000)`</td><td>توقف 0.1s بین Batchها برای جلوگیری از فشار Redis</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-authent"><div class="flowchart"><div class="flow-item">Validate JWT Authentication</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Shamsi Dates (middleware)</div><div class="flow-arrow">↓</div><div class="flow-item">Request Validation (start_date, end_date, batch_size)</div><div class="flow-arrow">↓</div><div class="flow-item">Query DB: manual_documents (date range, status != 5)</div><div class="flow-arrow">↓</div><div class="flow-item">Chunk Documents (batch_size)</div><div class="flow-arrow">↓</div><div class="flow-item">Loop Through Chunks</div><div class="flow-arrow">↓</div><div class="flow-item">Process Each Batch (processBatch)</div><div class="flow-arrow">↓</div><div class="flow-item">Update Redis: documents + indexes + balances</div><div class="flow-arrow">↓</div><div class="flow-item">usleep(100ms) Throttle</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Results (processed/errors)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Response</div></div></div>

# POST /v2/batch-accounting/process/month

# Process Batch Documents By Month

این اندپوینت اسناد حسابداری را بر اساس یک ماه شمسی مشخص دریافت کرده و آن‌ها را به صورت دسته‌ای (Batch Processing) پردازش می‌کند. ابتدا از تاریخ ورودی برای تعیین محدودهٔ کامل ماه استفاده می‌شود سپس عملیات پردازش دسته‌ای بر اساس منطق `processBatchByDateRange` اجرا می‌گردد. خروجی شامل تعداد کل اسناد پردازش‌شده، اطلاعات هر Batch و پیام نهایی مربوط به نام فارسی ماه است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/process/month`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`month_date`</td><td>string (YYYY-MM-DD)</td><td>yes</td><td>تاریخ شمسی داخل ماه مورد نظر. فقط برای تشخیص شماره ماه استفاده می‌شود.</td></tr><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>سایز هر Batch. حداقل 10، حداکثر 1000. مقدار پیش‌فرض: 100.</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/process/month

{
  "month_date": "1403-05-01",
  "batch_size": 200
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-month_date%3A-required">- month\_date: required + regex YYYY-MM-DD
- batch\_size: integer + min=10 + max=1000
- month\_date باید با shamsiDate middleware معتبر تشخیص داده شود

</div>## Response (Success)

ساختار خروجی بر اساس `BatchAccountingService::processBatchByDateRange` است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>نتیجه نهایی پردازش</td></tr><tr><td>`data.total_processed`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده</td></tr><tr><td>`data.total_errors`</td><td>integer</td><td>تعداد خطاهای رخ‌داده در Batchها</td></tr><tr><td>`data.batches_count`</td><td>integer</td><td>تعداد کل Batchهای اجراشده</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی شامل نام ماه فارسی استخراج‌شده با `getMonthName`</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 450,
    "total_errors": 0,
    "batches_count": 3
  },
  "message": "پردازش ماه مرداد کامل شد. 450 سند پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "month_date": [
      "The month_date format is invalid."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش ماه: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`ShamsiDateHelper::startOfMonth`</td><td>استخراج اولین روز ماه شمسی به فرمت YYYY-MM-DD</td></tr><tr><td>`ShamsiDateHelper::endOfMonth`</td><td>استخراج آخرین روز ماه شمسی</td></tr><tr><td>`processBatchByDateRange()`</td><td>منطق اصلی Batch Processing روی بازهٔ تاریخ</td></tr><tr><td>`getMonthName()`</td><td>تبدیل شماره ماه به نام فارسی (فروردین تا اسفند)</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Shamsi Date (middleware)</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Request Body (month_date + batch_size)</div><div class="flow-arrow">↓</div><div class="flow-item">Extract startOfMonth &amp; endOfMonth (ShamsiDateHelper)</div><div class="flow-arrow">↓</div><div class="flow-item">Call processBatchByDateRange(start, end, batchSize)</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Batch Processing (chunking + processing + throttling)</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Results (processed + errors)</div><div class="flow-arrow">↓</div><div class="flow-item">Get Month Name (getMonthName)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final Response</div></div></div>

# POST /v2/batch-accounting/process/month

<div id="bkmrk-" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div># Process Batch Documents By Month

<div id="bkmrk--1" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>این اندپوینت اسناد حسابداری را بر اساس یک ماه شمسی مشخص دریافت کرده و آن‌ها را به صورت دسته‌ای (Batch Processing) پردازش می‌کند. ابتدا از تاریخ ورودی برای تعیین محدودهٔ کامل ماه استفاده می‌شود سپس عملیات پردازش دسته‌ای بر اساس منطق `processBatchByDateRange` اجرا می‌گردد. خروجی شامل تعداد کل اسناد پردازش‌شده، اطلاعات هر Batch و پیام نهایی مربوط به نام فارسی ماه است.

<div id="bkmrk--2" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>## Request Overview

<div id="bkmrk-url%3A-%2Fv2%2Fbatch-accou" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"><div style="background: #ffffff; border: 1px solid #e7ecf5; border-radius: 12px; padding: 18px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06);"><div class="align-left" dir="ltr" style="margin-bottom: 6px;">**URL:** `/v2/batch-accounting/process/month`</div><div class="align-left" dir="ltr" style="margin-bottom: 6px;">**Method:** <span style="padding: 4px 10px; border-radius: 6px; font-weight: 600; font-size: 13px; display: inline-block; direction: ltr; margin-right: 5px; color: #fff; background: #1c7cf3;">POST</span></div><div class="align-left" dir="ltr" style="margin-bottom: 6px;">**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div id="bkmrk-field-type-required-" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"><div style="overflow-x: auto; margin-top: 16px; border-radius: 12px; border: 1px solid #eceef2;"><table style="width: 100%; border-collapse: collapse; text-align: center; background: #ffffff; overflow: hidden; font-size: 15px;"><thead><tr style="background: #f3f7ff; font-weight: 600; color: #143b7e;"><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Field</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Type</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Required</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Description</th></tr></thead><tbody><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`month_date`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">string (YYYY-MM-DD)</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">yes</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">تاریخ شمسی داخل ماه مورد نظر. فقط برای تشخیص شماره ماه استفاده می‌شود.</td></tr><tr><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">`batch_size`</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">integer</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">no</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">سایز هر Batch. حداقل 10، حداکثر 1000. مقدار پیش‌فرض: 100.</td></tr></tbody></table>

</div></div>## Request Example

```
POST /v2/batch-accounting/process/month

{
  "month_date": "1403-05-01",
  "batch_size": 200
}
```

<div id="bkmrk--3" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>## Validation Rules

<div id="bkmrk-month_date%3A-required" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;">- month\_date: required + regex YYYY-MM-DD
- batch\_size: integer + min=10 + max=1000
- month\_date باید با shamsiDate middleware معتبر تشخیص داده شود

</div>## Response (Success)

ساختار خروجی بر اساس `BatchAccountingService::processBatchByDateRange` است.

<div id="bkmrk-field-type-descripti" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"><div style="overflow-x: auto; margin-top: 16px; border-radius: 12px; border: 1px solid #eceef2;"><table style="width: 100%; border-collapse: collapse; text-align: center; background: #ffffff; overflow: hidden; font-size: 15px;"><thead><tr style="background: #f3f7ff; font-weight: 600; color: #143b7e;"><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Field</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Type</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Description</th></tr></thead><tbody><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`success`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">boolean</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">نتیجه نهایی پردازش</td></tr><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`data.total_processed`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">integer</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">تعداد کل اسناد پردازش‌شده</td></tr><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`data.total_errors`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">integer</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">تعداد خطاهای رخ‌داده در Batchها</td></tr><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`data.batches_count`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">integer</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">تعداد کل Batchهای اجراشده</td></tr><tr><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">`message`</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">string</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">پیام نهایی شامل نام ماه فارسی استخراج‌شده با `getMonthName`</td></tr></tbody></table>

</div></div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 450,
    "total_errors": 0,
    "batches_count": 3
  },
  "message": "پردازش ماه مرداد کامل شد. 450 سند پردازش شد."
}
```

<div id="bkmrk--4" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "month_date": [
      "The month_date format is invalid."
    ]
  }
}
```

<div id="bkmrk--5" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش ماه: Internal server error..."
}
```

<div id="bkmrk--6" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"></div>## Internal Architecture

<div id="bkmrk-component-descriptio" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"><div style="overflow-x: auto; margin-top: 16px; border-radius: 12px; border: 1px solid #eceef2;"><table style="width: 100%; border-collapse: collapse; text-align: center; background: #ffffff; overflow: hidden; font-size: 15px;"><thead><tr style="background: #f3f7ff; font-weight: 600; color: #143b7e;"><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Component</th><th style="padding: 12px 16px; border-bottom: 1px solid #e1e6ef;">Description</th></tr></thead><tbody><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`ShamsiDateHelper::startOfMonth`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">استخراج اولین روز ماه شمسی به فرمت YYYY-MM-DD</td></tr><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`ShamsiDateHelper::endOfMonth`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">استخراج آخرین روز ماه شمسی</td></tr><tr><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">`processBatchByDateRange()`</td><td style="padding: 12px 16px; border-bottom: 1px solid #f0f2f7; background-color: #fff;">منطق اصلی Batch Processing روی بازهٔ تاریخ</td></tr><tr><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">`getMonthName()`</td><td style="padding: 12px 16px; border-bottom: none; background-color: #fff;">تبدیل شماره ماه به نام فارسی (فروردین تا اسفند)</td></tr></tbody></table>

</div></div>## Process Flow

<div id="bkmrk-validate-jwt-token-%E2%86%93" style="font-family: 'Inter', system-ui, sans-serif; line-height: 1.7; color: #1a1a1a; direction: rtl; text-align: right; max-width: 880px; margin: 0 auto; padding: 20px; background-color: #ffffff;"><div style="margin: 28px 0; text-align: center;"><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Validate JWT Token</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Validate Shamsi Date (middleware)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Validate Request Body (month_date + batch_size)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Extract startOfMonth &amp; endOfMonth (ShamsiDateHelper)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Call processBatchByDateRange(start, end, batchSize)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Execute Batch Processing (chunking + processing + throttling)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Aggregate Results (processed + errors)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Get Month Name (getMonthName)</div><div style="font-size: 22px; margin: 10px 0; color: #143b7e;">↓</div><div style="display: inline-block; padding: 12px 18px; border: 1px solid #e7ecf5; border-radius: 10px; margin: 10px auto; background: #ffffff; box-shadow: 0 2px 6px rgba(20, 50, 95, 0.06); font-weight: 600; color: #1a1a1a;">Return Final Response</div></div></div>

# POST /v2/batch-accounting/process/year

# Process Batch Documents By Year

این اندپوینت اسناد حسابداری را بر اساس یک سال شمسی مشخص پردازش می‌کند. ابتدا تاریخ شروع سال و پایان سال شمسی محاسبه می‌شود، سپس عملیات دسته‌ای (Batch Processing) توسط متد `processBatchByDateRange` اجرا می‌گردد. نتیجه نهایی شامل تعداد اسناد پردازش‌شده، تعداد خطاها و مجموع Batchهای اجرا شده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/process/year`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`year`</td><td>integer</td><td>yes</td><td>سال شمسی بین 1300 تا 1500 برای پردازش اسناد</td></tr><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>سایز هر Batch. حداقل 50 و حداکثر 2000. مقدار پیش‌فرض: 500.</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/process/year

{
  "year": 1402,
  "batch_size": 800
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-year%3A-required-%2B-int">- year: required + integer + min=1300 + max=1500
- batch\_size: integer + min=50 + max=2000
- اعتبارسنجی سال و ورودی‌ها قبل از پردازش انجام می‌شود

</div>## Response (Success)

ساختار خروجی بر اساس `BatchAccountingService::processBatchByYear` است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>نتیجه کلی پردازش</td></tr><tr><td>`data.total_processed`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده در سال</td></tr><tr><td>`data.total_errors`</td><td>integer</td><td>تعداد خطاهای رخ‌داده</td></tr><tr><td>`data.batches_count`</td><td>integer</td><td>تعداد Batchهای اجراشده</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی پردازش سال</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 7800,
    "total_errors": 12,
    "batches_count": 16
  },
  "message": "پردازش سال 1402 کامل شد. 7800 سند پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "year": [
      "The year must be between 1300 and 1500."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش سال: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`processBatchByYear()`</td><td>محاسبه تاریخ آغاز سال و انتهای سال شمسی سپس فراخوانی `processBatchByDateRange`</td></tr><tr><td>`ShamsiDateHelper::endOfYear`</td><td>استخراج آخرین روز سال شمسی بر اساس تاریخ شروع</td></tr><tr><td>`processBatchByDateRange()`</td><td>پردازش دسته‌ای شامل chunking، sleep بین batchها و محاسبه خروجی</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Request Body (year + batch_size)</div><div class="flow-arrow">↓</div><div class="flow-item">Compute startOfYear = {year}-01-01</div><div class="flow-arrow">↓</div><div class="flow-item">Compute endOfYear via ShamsiDateHelper::endOfYear</div><div class="flow-arrow">↓</div><div class="flow-item">Call processBatchByDateRange(startOfYear, endOfYear, batchSize)</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Batch Processing (chunking + throttling + batch execution)</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Results (processed + errors + batch_count)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Response</div></div></div>

# POST /v2/batch-accounting/process/current-month

# Process Batch Documents of Current Month

این اندپوینت ماه جاری شمسی را به صورت خودکار تشخیص داده و اسناد آن را با استفاده از پردازش دسته‌ای (Batch Processing) پردازش می‌کند. تاریخ روز جاری از طریق `ShamsiDateHelper::today()` استخراج می‌شود و سپس عملیات ماه جاری مانند متد `processBatchByMonth` انجام می‌گیرد. پیام نهایی شامل نام فارسی ماه جاری است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/process/current-month`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>سایز Batchها. حداقل مقدار در متدهای سال و ماه متفاوت است اما اینجا مقدار پیش‌فرض 100 است.</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/process/current-month

{
  "batch_size": 150
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-batch_size%3A-integer-">- batch\_size: integer (در صورت ارسال)
- year/month/day استخراج‌شده از today() توسط middleware shamsiDate معتبر در نظر گرفته می‌شود

</div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>نتیجه پردازش دسته‌ای</td></tr><tr><td>`data.total_processed`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده در ماه جاری</td></tr><tr><td>`data.total_errors`</td><td>integer</td><td>تعداد خطاهای رخ‌داده در Batchها</td></tr><tr><td>`data.batches_count`</td><td>integer</td><td>تعداد Batchهای اجراشده</td></tr><tr><td>`message`</td><td>string</td><td>پیام شامل نام فارسی ماه جاری (getMonthName)</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 1120,
    "total_errors": 0,
    "batches_count": 8
  },
  "message": "پردازش آذر جاری کامل شد. 1120 سند پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش ماه جاری: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`ShamsiDateHelper::today()`</td><td>بازگرداندن تاریخ کامل امروز شمسی به صورت YYYY-MM-DD</td></tr><tr><td>`processBatchByMonth()`</td><td>محاسبه start/end ماه و ارسال به processBatchByDateRange</td></tr><tr><td>`getMonthName()`</td><td>استخراج نام فارسی ماه از روی شماره ماه استخراج‌شده از today()</td></tr><tr><td>`processBatchByDateRange()`</td><td>منطق اصلی Batch Processing شامل chunking، sleep بین Batchها و محاسبه خروجی</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Extract Current Date (ShamsiDateHelper::today)</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Start/End of Month via startOfMonth &amp; endOfMonth</div><div class="flow-arrow">↓</div><div class="flow-item">Call processBatchByMonth(currentDate, batchSize)</div><div class="flow-arrow">↓</div><div class="flow-item">processBatchByDateRange(start, end, batchSize)</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Batch Processing (chunking + throttling)</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Results (processed + errors + batch_count)</div><div class="flow-arrow">↓</div><div class="flow-item">Get Current Month Name (getMonthName)</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Response</div></div></div>

# POST /v2/batch-accounting/process/current-year

# Process Batch Documents of Current Year

این اندپوینت سال جاری شمسی را به صورت خودکار تشخیص داده و اسناد آن را از طریق پردازش دسته‌ای (Batch Processing) پردازش می‌کند. سال جاری از تاریخ کامل امروز، که توسط `ShamsiDateHelper::today()` تولید می‌شود، استخراج شده و سپس متد `processBatchByYear` فراخوانی می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/process/current-year`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>سایز Batchها. مقدار پیش‌فرض 500.</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/process/current-year

{
  "batch_size": 700
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-batch_size%3A-integer-">- batch\_size: integer (در صورت ارسال)
- تاریخ today() توسط middleware shamsiDate معتبر تشخیص داده می‌شود

</div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>نتیجه عملیات پردازش</td></tr><tr><td>`data.total_processed`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده</td></tr><tr><td>`data.total_errors`</td><td>integer</td><td>خطاهای رخ‌داده در Batchها</td></tr><tr><td>`data.batches_count`</td><td>integer</td><td>مجموع Batchهای اجراشده</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی شامل سال استخراج‌شده از تاریخ امروز</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "success": true,
    "total_processed": 9100,
    "total_errors": 3,
    "batches_count": 20
  },
  "message": "پردازش سال 1403 کامل شد. 9100 سند پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پردازش سال جاری: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`ShamsiDateHelper::today()`</td><td>بازگرداندن تاریخ امروز به فرمت YYYY-MM-DD</td></tr><tr><td>`processBatchByYear()`</td><td>محاسبه start/end سال و ارسال آن‌ها به processBatchByDateRange</td></tr><tr><td>`ShamsiDateHelper::endOfYear`</td><td>تشخیص آخرین روز سال شمسی</td></tr><tr><td>`processBatchByDateRange()`</td><td>منطق کامل پردازش دسته‌ای شامل chunking، sleep و aggregate خروجی</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Get Today (ShamsiDateHelper::today)</div><div class="flow-arrow">↓</div><div class="flow-item">Extract Current Year (YYYY)</div><div class="flow-arrow">↓</div><div class="flow-item">Call processBatchByYear(currentYear, batchSize)</div><div class="flow-arrow">↓</div><div class="flow-item">Compute startOfYear / endOfYear</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Batch Processing (chunking + throttling)</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Results (processed + errors + batch_count)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Response</div></div></div>

# POST /v2/batch-accounting/rebuild-indexes

# Rebuild All Redis Indexes

این اندپوینت یک عملیات بسیار سنگین و مخرب (destructive) برای بازسازی کامل تمامی ایندکس‌های Redis اجرا می‌کند. قبل از شروع، تمام داده‌های موجود در Redis شامل اسناد، ایندکس‌ها و مانده‌ها حذف می‌شوند و سپس بر اساس رکوردهای فعال جدول `manual_documents`، ایندکس‌ها از صفر ساخته می‌شوند. برای جلوگیری از اجرای اشتباه، فیلد `confirm` اجباری است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/rebuild-indexes`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>حداکثر اندازه هر Batch. باید بین 100 و 1000 باشد. مقدار پیش‌فرض: 200.</td></tr><tr><td>`confirm`</td><td>boolean</td><td>yes</td><td>باید مقدار true/1 باشد (Rule: accepted). جهت جلوگیری از اجرای تصادفی.</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/rebuild-indexes

{
  "batch_size": 300,
  "confirm": true
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Validation Rules

<div class="api-docs" id="bkmrk-batch_size%3A-nullable">- batch\_size: nullable, integer, min:100, max:1000
- confirm: required, boolean, accepted

</div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی بازسازی</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>تعداد کل اسنادی که از دیتابیس خوانده شدند (status != 5)</td></tr><tr><td>`data.processed_count`</td><td>integer</td><td>تعداد کل اسناد پردازش‌شده</td></tr><tr><td>`data.error_count`</td><td>integer</td><td>تعداد اسناد خطادار</td></tr><tr><td>`data.duration_seconds`</td><td>float</td><td>مدت زمان کامل عملیات بازسازی</td></tr><tr><td>`data.performance.documents_per_second`</td><td>float</td><td>میانگین سرعت پردازش</td></tr><tr><td>`data.performance.batch_size`</td><td>integer</td><td>مقدار نهایی batch\_size مورد استفاده</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی شامل تعداد اسناد پردازش‌شده و مدت زمان</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "total_documents": 12850,
    "processed_count": 12850,
    "error_count": 0,
    "duration_seconds": 14.27,
    "performance": {
      "documents_per_second": 900.32,
      "batch_size": 300
    }
  },
  "message": "بازسازی کامل انجام شد. 12850 سند در 14.27 ثانیه پردازش شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Validation Error: 422)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "confirm": [
      "فیلد confirm الزامی است."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در بازسازی: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`redisAccountingService->clearAllDocuments()`</td><td>حذف کامل تمام اسناد و ایندکس‌های Redis قبل از بازسازی</td></tr><tr><td>`manual_documents` table</td><td>منبع اصلی داده‌ها برای بازسازی — فقط رکوردهای status != 5 استخراج می‌شوند</td></tr><tr><td>`chunk($batchSize)`</td><td>پردازش اسناد دیتابیس به صورت Batch جهت کنترل مصرف حافظه</td></tr><tr><td>`processBatch()`</td><td>منطق داخلی ساخت مجدد اسناد، ایندکس‌ها و آپدیت مانده‌ها</td></tr><tr><td>`getTotalDocumentCount()`</td><td>برای محاسبه درصد پیشرفت (progress) در هر Batch</td></tr><tr><td>`Log::info()`</td><td>ثبت کامل پیشرفت و زمان اجرای rebuild</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Input (batch_size, confirm)</div><div class="flow-arrow">↓</div><div class="flow-item">Clear All Redis Documents (clearAllDocuments)</div><div class="flow-arrow">↓</div><div class="flow-item">Count Active DB Documents (status != 5)</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Documents in Batches (chunk)</div><div class="flow-arrow">↓</div><div class="flow-item">Process Each Batch (processBatch)</div><div class="flow-arrow">↓</div><div class="flow-item">Update Progress Log</div><div class="flow-arrow">↓</div><div class="flow-item">Aggregate Final Stats (processed, errors)</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Response</div></div></div>

# POST /v2/batch-accounting/optimize-memory

# Optimize Redis Memory

این اندپوینت عملیات بهینه‌سازی حافظه Redis را به صورت کامل و ساختاریافته انجام می‌دهد. هدف عملیات شامل: - اندازه‌گیری حافظه فعلی، - حذف کلیدهای غیرمعتبر، - اجرای فشرده‌سازی AOF از طریق `bgrewriteaof`, - و ارائه گزارش میزان حافظه آزادشده است. از آنجا که این عملیات می‌تواند بر عملکرد موقتی Redis اثر بگذارد، معمولاً در محیط‌های Production با احتیاط فراخوانی می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/optimize-memory`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

این اندپوینت هیچ ورودی ندارد.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td colspan="4" style="text-align: center; color: #888;">No parameters</td></tr></tbody></table>

</div>## Request Example

```
POST /v2/batch-accounting/optimize-memory
{}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت کلی عملیات</td></tr><tr><td>`data.initial_memory`</td><td>integer</td><td>حجم حافظه Redis پیش از عملیات (بر حسب بایت)</td></tr><tr><td>`data.final_memory`</td><td>integer</td><td>حجم حافظه Redis پس از عملیات</td></tr><tr><td>`data.saved_memory`</td><td>integer</td><td>میزان حافظه آزادشده</td></tr><tr><td>`data.optimization_percentage`</td><td>float</td><td>درصد بهبود حافظه (saved\_memory / initial\_memory × 100)</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی شامل درصد بهینه‌سازی</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "initial_memory": 183783424,
    "final_memory": 119013376,
    "saved_memory": 64770048,
    "optimization_percentage": 35.25
  },
  "message": "بهینه‌سازی کامل شد. 35.25% حافظه آزاد شد."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در بهینه‌سازی: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`getRedisMemoryUsage()`</td><td>استخراج مقدار مصرف حافظه Redis پیش از عملیات و پس از عملیات</td></tr><tr><td>`Redis::eval()`</td><td>اجرای اسکریپت Lua برای شمارش و حذف کلیدهای غیرمعتبر یا کلیدهایی که باید TTL داشته باشند. (در نسخه فعلی فقط بخش اسکلت حذف نوشته شده و قابل گسترش است.)</td></tr><tr><td>`Redis::bgrewriteaof()`</td><td>عملیات فشرده‌سازی AOF برای کاهش مصرف فضای دیسک و حافظه</td></tr><tr><td>`saved_memory`</td><td>اختلاف میان initial\_memory و final\_memory</td></tr><tr><td>`optimization_percentage`</td><td>محاسبه درصد بهینه‌سازی با فرمول دقیق round((saved / initial) × 100, 2)</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Get Initial Memory Usage</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Lua Script for Orphan/Invalid Keys</div><div class="flow-arrow">↓</div><div class="flow-item">Run Redis BGREWRITEAOF</div><div class="flow-arrow">↓</div><div class="flow-item">Measure Final Memory Usage</div><div class="flow-arrow">↓</div><div class="flow-item">Calculate Saved Memory &amp; Percentage</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Report</div></div></div>

# GET /v2/batch-accounting/report/comprehensive

# Comprehensive Redis Accounting Report

این اندپوینت یک گزارش جامع (Comprehensive Report) از وضعیت کامل سیستم Redis Accounting و Batch Accounting تولید می‌کند. این گزارش شامل آمار عمومی Redis، تحلیل اسناد ماهانه، بخش‌های پرمصرف (Top Subsidiaries)، جمع‌بندی بالانس‌ها، گزارش حافظه، و متریک‌های عملکردی است. این گزارش معمولاً برای تیم‌های DevOps، Backend Engineering و تیم‌های Performance Monitoring استفاده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/report/comprehensive`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Query Parameters

این اندپوینت هیچ پارامتر ورودی ندارد.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td colspan="4" style="text-align: center; color: #888;">No parameters</td></tr></tbody></table>

</div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت نهایی عملیات</td></tr><tr><td>`data.general_stats`</td><td>object</td><td>خروجی کامل getRedisStats شامل تعداد اسناد، ایندکس‌ها، بالانس‌ها و حافظه Redis</td></tr><tr><td>`data.documents_by_month`</td><td>array</td><td>آمار تجمیع‌شده اسناد بر اساس ماه (مثلاً {1403-01: 1280})</td></tr><tr><td>`data.top_subsidiaries`</td><td>array</td><td>بیشترین سطح فعالیت (Subsidiary Codes با بیشترین سند/بیشترین تراکنش)</td></tr><tr><td>`data.balance_summary`</td><td>object</td><td>خلاصه مانده‌ها در تمام لایه‌ها (account, general, group, subsidiary)</td></tr><tr><td>`data.memory_info`</td><td>object</td><td>گزارش جزئی مصرف حافظه Redis (مقایسه internal metrics)</td></tr><tr><td>`data.performance_metrics`</td><td>object</td><td>متریک‌های وظرفیت و throughput پردازش Batch</td></tr><tr><td>`message`</td><td>string</td><td>پیام خروجی</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "general_stats": {
      "total_documents": 12850,
      "total_indexes": 5120,
      "total_balances": 740,
      "memory_usage": "148 MB"
    },
    "documents_by_month": {
      "1403-01": 1320,
      "1403-02": 1408,
      "1403-03": 960
    },
    "top_subsidiaries": [
      { "code": 21012, "documents": 482 },
      { "code": 21007, "documents": 441 }
    ],
    "balance_summary": {
      "total_debit": 483900000,
      "total_credit": 480200000,
      "difference": 3700000
    },
    "memory_info": {
      "used_memory": 183783424,
      "peak_memory": 221184000,
      "fragmentation_ratio": 1.23
    },
    "performance_metrics": {
      "avg_documents_per_batch": 280,
      "avg_processing_time_ms": 14.2,
      "throughput_docs_per_second": 905.4
    }
  },
  "message": "گزارش کامل تهیه شد"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در تهیه گزارش: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`redisAccountingService->getRedisStats()`</td><td>آمار کلیدی Redis شامل کلیدها، مانده‌ها و حافظه</td></tr><tr><td>`getDocumentsByMonth()`</td><td>خروجی تحلیلی تعداد اسناد بر اساس سال/ماه شمسی</td></tr><tr><td>`getTopSubsidiaries()`</td><td>تشخیص پُرتراکنش‌ترین کدهای معین</td></tr><tr><td>`getBalanceSummary()`</td><td>تجمیع مانده‌های کل سیستم</td></tr><tr><td>`getDetailedMemoryInfo()`</td><td>گزارش چرایی مصرف حافظه: fragmentation, allocator, peaks</td></tr><tr><td>`getPerformanceMetrics()`</td><td>متریک‌های سرعت، حجم Batch، زمان‌بندی‌ها و throughput</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Redis General Stats</div><div class="flow-arrow">↓</div><div class="flow-item">Analyze Documents by Month</div><div class="flow-arrow">↓</div><div class="flow-item">Calculate Top Subsidiaries</div><div class="flow-arrow">↓</div><div class="flow-item">Summarize Balances</div><div class="flow-arrow">↓</div><div class="flow-item">Read Memory Detailed Info</div><div class="flow-arrow">↓</div><div class="flow-item">Collect Performance Metrics</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final Comprehensive Report</div></div></div>

# POST /v2/batch-accounting/preview

# Preview Batch Processing (Dry‑Run)

این اندپوینت یک **پیش‌نمایش کامل** از عملیات پردازش Batch را ارائه می‌دهد، بدون اینکه هیچ پردازش واقعی روی Redis یا دیتابیس انجام شود. این پیش‌نمایش برای تصمیم‌گیری مهندسی قبل از اجرای پردازش‌های سنگین استفاده می‌شود و شامل: - تعداد اسناد قابل پردازش، - تعداد Batch مورد نیاز، - زمان تخمینی، - تعداد روزهای شمسی در بازه، - و اطلاعات کامل بازه تاریخ می‌باشد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbatch-accou"><div class="endpoint-info"><div>**URL:** `/v2/batch-accounting/preview`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Middleware:** authWithJwt, shamsiDate</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`start_date`</td><td>string</td><td>yes</td><td>تاریخ شمسی شروع بازه به فرمت دقیق `YYYY-MM-DD`. (اعتبارسنجی regex و همچنین اعتبارسنجی Jalali توسط Middleware)</td></tr><tr><td>`end_date`</td><td>string</td><td>yes</td><td>تاریخ پایان بازه، فرمت مشابه start\_date</td></tr><tr><td>`batch_size`</td><td>integer</td><td>no</td><td>اندازه Batch بین **10 تا 1000**. مقدار پیش‌فرض **100**.</td></tr></tbody></table>

</div>## Validation Rules

<div class="api-docs" id="bkmrk-start_date%3A-required">- start\_date: required, string, regex:/^\\\\d{4}-\\\\d{2}-\\\\d{2}$/
- end\_date: required, string, regex:/^\\\\d{4}-\\\\d{2}-\\\\d{2}$/
- batch\_size: nullable, integer, min:10, max:1000

</div>## Response (Success)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`success`</td><td>boolean</td><td>وضعیت کلی پیش‌نمایش</td></tr><tr><td>`data.total_documents`</td><td>integer</td><td>تعداد کل اسناد در دیتابیس بین start و end با status != 5</td></tr><tr><td>`data.batch_size`</td><td>integer</td><td>مقدار نهایی batch\_size (ورودی یا پیش‌فرض)</td></tr><tr><td>`data.estimated_batches`</td><td>integer</td><td>تعداد Batch موردنیاز (ceil(total / batch\_size))</td></tr><tr><td>`data.estimated_time_seconds`</td><td>float</td><td>تخمین زمان کل (0.5 ثانیه برای هر batch)</td></tr><tr><td>`data.estimated_time_minutes`</td><td>float</td><td>زمان تخمینی بر حسب دقیقه</td></tr><tr><td>`data.working_days_count`</td><td>integer</td><td>تعداد روزهای شمسی شامل start→end</td></tr><tr><td>`data.date_range`</td><td>object</td><td>شامل start، end، نسخه‌های فرمت‌شده (formatFull)، و اطلاعات بازه</td></tr><tr><td>`message`</td><td>string</td><td>پیام نهایی پیش‌نمایش: «پیش‌نمایش: X سند در Y batch پردازش خواهد شد»</td></tr></tbody></table>

</div>```
{
  "success": true,
  "data": {
    "total_documents": 1480,
    "batch_size": 100,
    "estimated_batches": 15,
    "estimated_time_seconds": 7.5,
    "estimated_time_minutes": 0.13,
    "working_days_count": 31,
    "date_range": {
      "start": "1403-01-01",
      "end": "1403-01-31",
      "start_formatted": "۱ فروردین ۱۴۰۳",
      "end_formatted": "۳۱ فروردین ۱۴۰۳"
    }
  },
  "message": "پیش‌نمایش: 1480 سند در 15 batch پردازش خواهد شد"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Validation Error: 422)

```
{
  "success": false,
  "message": "خطا در اعتبارسنجی",
  "errors": {
    "start_date": [
      "فرمت تاریخ نامعتبر است."
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Server Error)

```
{
  "success": false,
  "message": "خطا در پیش‌نمایش: Internal server error..."
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Internal Architecture

<div class="api-docs" id="bkmrk-component-descriptio"><table class="schema-table"><thead><tr><th>Component</th><th>Description</th></tr></thead><tbody><tr><td>`manual_documents` table</td><td>فیلتر اسناد بر اساس بازه تاریخ و حذف status=5</td></tr><tr><td>`ShamsiDateHelper::generateDateRange()`</td><td>تولید تمام روزهای شمسی بین start و end</td></tr><tr><td>`ShamsiDateHelper::formatFull()`</td><td>فرمت تبدیل YYYY-MM-DD به متن کامل مانند «۱ فروردین ۱۴۰۳»</td></tr><tr><td>`estimatedBatches`</td><td>محاسبه تعداد Batch از طریق ceil(total/batch\_size)</td></tr><tr><td>`estimatedTime`</td><td>زمان تخمینی با مدل 0.5 ثانیه برای هر Batch</td></tr></tbody></table>

</div>## Process Flow

<div class="api-docs" id="bkmrk-validate-jwt-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Validate JWT Token</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Input (start_date, end_date, batch_size)</div><div class="flow-arrow">↓</div><div class="flow-item">Count DB Documents (manual_documents)</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Estimated Batches</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Estimated Time</div><div class="flow-arrow">↓</div><div class="flow-item">Generate Shamsi Date Range</div><div class="flow-arrow">↓</div><div class="flow-item">Return Preview JSON</div></div></div>

# POST /v2/core/offices/list

# Core Offices List

این اندپوینت لیست کامل دفاتر (Offices) سیستم را از جدول `offices` بازیابی کرده و برای استفاده در پنل‌های مدیریتی Core Center برگشت می‌دهد. دسترسی به این اندپوینت کاملاً ویژه است و فقط کاربرانی که Branch آن‌ها دقیقاً مقدار **"\[0\]"** باشد می‌توانند وارد شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Foffice"><div class="endpoint-info"><div>**URL:** `/v2/core/offices/list`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@officesList</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Authentication

لازم است هدر زیر حتماً ارسال شود:

```
Authorization: Bearer <JWT_TOKEN>
```

میدلور **AuthWithJWT** موارد زیر را احراز می‌کند:

<div class="api-docs" id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-%D9%88%D8%AC%D9%88%D8%AF-%D8%AF%D8%A7%D8%B4%D8%AA%D9%87-%D8%A8%D8%A7%D8%B4%D8%AF">- توکن وجود داشته باشد
- توکن با HS256 و کلید `env('JWT_SECRET_KEY')` معتبر باشد
- UUID در توکن وجود داشته باشد
- کاربر در جدول مربوط به typ (operators / customers / colleague\_auth) یافت شود
- وضعیت کاربر **status = 1** باشد

</div>سپس میدلور group, level, operator را به درخواست اضافه می‌کند.

<div class="api-docs" id="bkmrk--1"></div>## Authorization (Core Access)

میدلور **CoreAccess** سطح دسترسی را کنترل می‌کند.

<div class="api-docs" id="bkmrk-condition-allow%3F-%24re"><table class="schema-table"><thead><tr><th>Condition</th><th>Allow?</th></tr></thead><tbody><tr><td>`$request->get('operator')->branch == "[0]"`</td><td style="color: green;">✔ Access Granted</td></tr><tr><td>Anything Else</td><td style="color: red;">✘ Access Denied</td></tr></tbody></table>

</div>در صورت عدم دسترسی خروجی زیر برگردانده می‌شود:

```
{
  "status": false,
  "time": 1710000000,
  "error": {
    "code": 5005,
    "message": "Access to the [core center] requires special access."
  },
  "support": {
    "phone": "021-91016838 in 121",
    "email": "ict@airplus.app",
    "panel": "helpdesk.airplus.app"
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Body Parameters

این اندپوینت هیچ پارامتر ورودی ندارد.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td colspan="4" style="text-align: center; color: #777;">No body parameters</td></tr></tbody></table>

</div>## Response (Success)

خروجی شامل تمام رکوردهای جدول `offices` است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`status`</td><td>boolean</td><td>همیشه true در حالت موفق</td></tr><tr><td>`time`</td><td>integer</td><td>UNIX timestamp</td></tr><tr><td>`data`</td><td>array\[object\]</td><td>نتیجه خام DB::table('offices')-&gt;get()</td></tr></tbody></table>

</div>```
{
  "status": true,
  "time": 1710000000,
  "data": [
    {
      "id": 1,
      "title_fa": "دفتر مرکزی",
      "brand_fa": "ایرپلاس",
      "city": "تهران",
      "status": 1,
      ...
    },
    ...
  ]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (JWT Error)

نمونه خروجی اگر توکن ارسال نشود:

```
{
  "status": false,
  "time": 1710000000,
  "error": {
    "code": 1005,
    "message": "Token not provided!"
  },
  "support": { ... }
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Process Flow

<div class="api-docs" id="bkmrk-check-bearer-token-%E2%86%93"><div class="flowchart"><div class="flow-item">Check Bearer Token</div><div class="flow-arrow">↓</div><div class="flow-item">Decode JWT (HS256)</div><div class="flow-arrow">↓</div><div class="flow-item">Load User From Table (operators/customers/colleague)</div><div class="flow-arrow">↓</div><div class="flow-item">Check user.status == 1</div><div class="flow-arrow">↓</div><div class="flow-item">CoreAccess → Check operator.branch == "[0]"</div><div class="flow-arrow">↓</div><div class="flow-item">Query DB: offices table</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Response</div></div></div>

# POST /v2/core/accommodations/list

# Core Accommodations List

این اندپوینت لیست اقامتگاه‌ها (هتل‌ها) را با پشتیبانی از فیلترهای پیشرفته، صفحه‌بندی پویا، بارگذاری داده‌های تکمیلی از Redis، اطلاعات جغرافیایی از دیتابیس، و استخراج Supplier Mapping برگشت می‌دهد. این سرویس صرفاً در اختیار کاربرانی قرار می‌گیرد که دسترسی Core داشته باشند (**operator.branch == "\[0\]"**).

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodations/list`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@accommodationsList</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Authentication &amp; Access Control

مانند سایر سرویس‌های هسته Core:

<div class="api-docs" id="bkmrk-authwithjwt%3A-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-j">- **authWithJwt**: بررسی JWT، بازیابی اپراتور از DB، بررسی status=1
- **core**: اجازه فقط اگر `$request->operator->branch == "[0]"`

</div>در صورت عدم دسترسی:

```
{
  "status": false,
  "time": 1710000000,
  "error": {
    "code": 5005,
    "message": "Access to the [core center] requires special access."
  },
  "support": { ... }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Request Body

ورودی با کلید `json` در Body ارسال می‌شود و توسط کد `json_decode` تبدیل می‌شود.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>`draw`</td><td>integer</td><td>yes</td><td>شماره سریال درخواست (برای DataTables)</td></tr><tr><td>`start`</td><td>integer</td><td>yes</td><td>نقطه شروع صفحه‌بندی. اگر 0 باشد: مقدار برابر length قرار داده می‌شود اگر &gt;0 باشد: مقدار += length</td></tr><tr><td>`length`</td><td>integer</td><td>yes</td><td>تعداد رکورد در هر صفحه</td></tr><tr><td>`advanced.country`</td><td>string</td><td>no</td><td>فیلتر کشور</td></tr><tr><td>`advanced.state`</td><td>string</td><td>no</td><td>فیلتر استان</td></tr><tr><td>`advanced.city`</td><td>string</td><td>no</td><td>فیلتر شهر</td></tr><tr><td>`advanced.rate`</td><td>string</td><td>no</td><td>فیلتر تعداد ستاره</td></tr><tr><td>`advanced.r`</td><td>string</td><td>no</td><td>جستجوی آزاد روی id، عنوان فارسی، عنوان انگلیسی، آدرس</td></tr></tbody></table>

</div>## Database Query Logic

کوئری اصلی روی جدول `hotels` اعمال می‌شود:

<div class="api-docs" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%DA%A9%D8%B4%D9%88%D8%B1%D8%8C-%D8%A7%D8%B3%D8%AA%D8%A7%D9%86%D8%8C-%D8%B4">- فیلتر کشور، استان، شهر، rate در صورت مقدار داشتن
- فیلتر full-text (جزیی) روی: 
    - id
    - en\_title
    - fa\_title
    - address
- where status = 1
- OrderBy id DESC
- **paginate** با page = `$Data->start / $Data->length`

</div>```
paginate(
    $perPage = length,
    columns = "*",
    pageName = "hotels",
    currentPage = (start / length)
)
  
```

<div class="api-docs" id="bkmrk--2"></div>## Enrichment Pipeline (per-item)

برای هر accommodation مراحل زیر اجرا می‌شود:

<div class="api-docs" id="bkmrk-step-description-1.-"><table class="schema-table"><thead><tr><th>Step</th><th>Description</th></tr></thead><tbody><tr><td>1. Load Country (Redis → DB fallback)</td><td>تلاش برای خواندن `countries:{id}` از Redis در صورت نبود: خواندن از DB و ذخیره در Redis</td></tr><tr><td>2. Load City Info</td><td>join cities + states خروجی شامل title\_fa و group\_by (عنوان استان)</td></tr><tr><td>3. Supplier Mapping</td><td>خواندن mapping از جدول mapping\_colleagues سپس دریافت colleague از جدول colleagues</td></tr><tr><td>4. Media (Logo)</td><td>`Media::where('related_id', $request->get('id'))` \*\*نکته:\*\* این مقدار از request-&gt;id دریافت می‌شود، نه id اقامتگاه — رفتار دقیقاً مطابق کد ثبت شد.</td></tr></tbody></table>

</div>## Response Schema

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>`status`</td><td>boolean</td><td>true</td></tr><tr><td>`time`</td><td>integer</td><td>UNIX timestamp</td></tr><tr><td>`draw`</td><td>integer</td><td>echo همان draw ورودی</td></tr><tr><td>`recordsTotal`</td><td>integer</td><td>result-&gt;total() از paginate</td></tr><tr><td>`recordsFiltered`</td><td>integer</td><td>مقدار ورودی length (طبق کد، نه تعداد واقعی فیلتر شده)</td></tr><tr><td>`data`</td><td>array</td><td>لیست اقامتگاه‌ها با اطلاعات کامل enrich شده</td></tr></tbody></table>

</div>## Sample Successful Response

```
{
  "status": true,
  "time": 1710000000,
  "draw": 3,
  "recordsTotal": 1295,
  "recordsFiltered": 20,
  "data": [
    {
      "id": 554,
      "title": "Tehran Grand Hotel",
      "title_fa": "هتل بزرگ تهران",
      "rate": 5,
      "country": {
        "id": 102,
        "fa_name": "ایران",
        "en_name": "Iran",
        "fa_nationality": "ایرانی",
        "en_nationality": "Iranian"
      },
      "state": "تهران",
      "city": "تهران",
      "location": "35.7000,51.4000",
      "address": "خیابان ولیعصر ...",
      "logo": "/media/accommodations/554/logo.png",
      "status": 1,
      "supplier": {
        "system_serial": 88,
        "serial": "C-4401"
      }
    }
  ]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Process Flow

<div class="api-docs" id="bkmrk-authwithjwt-%E2%86%92-valida"><div class="flowchart"><div class="flow-item">AuthWithJWT → Validate Token</div><div class="flow-arrow">↓</div><div class="flow-item">CoreAccess → Check branch == "[0]"</div><div class="flow-arrow">↓</div><div class="flow-item">Decode Body.json</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Pagination (start/length)</div><div class="flow-arrow">↓</div><div class="flow-item">Query hotels with Advanced Filters</div><div class="flow-arrow">↓</div><div class="flow-item">For Each Hotel → Enrichment Pipeline (Redis Country, City join, Mapping, Media)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON Payload</div></div></div>

# POST /v2/core/accommodation/store

# Core Accommodation Store

این اندپوینت یک اقامتگاه جدید (Hotel / Accommodation) را در سیستم ثبت می‌کند. داده‌ها از کلید `data` در بدنه درخواست دریافت شده و به‌صورت کامل روی جدول `hotels` درج می‌شوند. سپس عملیات‌های جانبی شامل ذخیره لوگو و گالری رسانه، ثبت Mapping در جدول `mapping_accommodations`، و اجرای سه Job (UpdateRedis، SystemLog و RunCron) در صف انجام می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/store`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@storeAccommodation</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

دسترسی فقط در صورتی مجاز است که:

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%A8%D8%A7%D8%B4%D8%AF-%28midd">- **JWT معتبر باشد** (middleware: authWithJwt)
- **operator.branch == "\[0\]"** باشد (middleware: core)

</div>## Request Body Schema

تمام ورودی‌ها داخل کلید `data` ارسال می‌شوند.

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>yes</td><td>نوع اقامتگاه (hotel, hostel, …)</td></tr><tr><td>title\_fa</td><td>string</td><td>yes</td><td>نام فارسی</td></tr><tr><td>title\_en</td><td>string</td><td>yes</td><td>نام انگلیسی</td></tr><tr><td>rate</td><td>integer</td><td>no</td><td>تعداد ستاره — اگر مقدار نداشته باشد 0 ذخیره می‌شود</td></tr><tr><td>country</td><td>integer</td><td>yes</td><td>ID کشور</td></tr><tr><td>state</td><td>integer</td><td>yes</td><td>ID استان</td></tr><tr><td>city</td><td>integer</td><td>yes</td><td>ID شهر</td></tr><tr><td>location</td><td>string</td><td>no</td><td>لوکیشن جغرافیایی (latitude,longitude)</td></tr><tr><td>address</td><td>string</td><td>yes</td><td>آدرس کامل</td></tr><tr><td>description</td><td>string</td><td>no</td><td>شرح</td></tr><tr><td>board</td><td>array</td><td>no</td><td>مثلاً: \["BB","HB","FB"\] — در صورت خالی null</td></tr><tr><td>possibilities</td><td>array</td><td>no</td><td>امکانات — در صورت خالی null</td></tr><tr><td>details</td><td>object</td><td>yes</td><td>جزئیات کامل (json\_encode ذخیره می‌شود)</td></tr><tr><td>site</td><td>string</td><td>no</td><td>وب‌سایت</td></tr><tr><td>email</td><td>string</td><td>no</td><td>ایمیل</td></tr><tr><td>mobile</td><td>string</td><td>no</td><td>موبایل مدیر/هتل</td></tr><tr><td>phone</td><td>string</td><td>no</td><td>تلفن ثابت</td></tr><tr><td>leader\_name</td><td>string</td><td>no</td><td>نام مسئول</td></tr><tr><td>leader\_mobile</td><td>string</td><td>no</td><td>موبایل مسئول</td></tr><tr><td>confirm\_status</td><td>integer</td><td>yes</td><td>وضعیت تأیید (0/1)</td></tr><tr><td>priority</td><td>integer</td><td>yes</td><td>اولویت در نمایش</td></tr><tr><td>logo</td><td>string|null</td><td>no</td><td>مسیر عکس لوگو — اگر null باشد ذخیره نمی‌شود</td></tr><tr><td>media</td><td>array</td><td>no</td><td>لیست تصاویر گالری — هر مورد path است</td></tr><tr><td>mapping</td><td>object</td><td>no</td><td>اتصال به سرویس‌های بیرونی (tport, sepehr, snapptrip, ...)</td></tr></tbody></table>

</div>## Core Database Insertion Logic

عملیات اصلی درج:

<div class="api-docs" id="bkmrk-insertgetid-%D8%B1%D9%88%DB%8C-%D8%AC%D8%AF%D9%88%D9%84">- insertGetId روی جدول `hotels`
- json\_encode برای: board، possibilities، details
- پر کردن created\_at و updated\_at با Carbon::now()
- اگر لوگو وجود داشته باشد → درج در Media
- اگر media آرایه داشته باشد → درج تک‌تک در Media
- اگر mapping موجود باشد → درج در mapping\_accommodations

</div>```
id = DB::table('hotels')->insertGetId([...])
  
```

<div class="api-docs" id="bkmrk--1"></div>## Media Insertion Rules

هر فایل مسیر مانند

```
"uploads/hotels/abc.jpg"
```

به شکل زیر پردازش می‌شود:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-extension-%D8%A8%D8%B1">- استخراج extension بر اساس آخرین "."
- نوع برای لوگو: `logo`
- نوع برای گالری: `image`

</div>## Mapping Insertion

شیء `mapping` می‌تواند شامل کلیدهای زیر باشد. هر مقدار خالی (null / "") → در DB مقدار null ذخیره می‌شود.

<div class="api-docs" id="bkmrk-tport-sepehr-snapptr">- tport
- sepehr
- snapptrip
- alibaba
- eghamat24
- iranhotelonline

</div>در جدول `mapping_accommodations` فیلد accommodation = ID درج شده ذخیره می‌شود.

<div class="api-docs" id="bkmrk--2"></div>## Background Jobs

در ادامه سه Job اجرا می‌شود:

<div class="api-docs" id="bkmrk-job-queue-delay-desc"><table class="schema-table"><thead><tr><th>Job</th><th>Queue</th><th>Delay</th><th>Description</th></tr></thead><tbody><tr><td>UpdateRedis</td><td>fastJob</td><td>+1 min</td><td>آپدیت کش Redis برای hotels و hotels\_details</td></tr><tr><td>SystemLog</td><td>snailJob</td><td>+10 min</td><td>ثبت عملیات StoreAccommodation با user-agent و IP</td></tr><tr><td>RunCron</td><td>fastJob</td><td>+1 min</td><td>اجرای فرآیند بروزرسانی اقامتگاه</td></tr></tbody></table>

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "message": "1391 | Accommodation added!"
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "SQLSTATE... (error message)",
  "trace": [ ... ]
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Flowchart

<div class="api-docs" id="bkmrk-authwithjwt-%E2%86%92-valida"><div class="flowchart"><div class="flow-item">AuthWithJWT → Validate Token</div><div class="flow-arrow">↓</div><div class="flow-item">CoreAccess → operator.branch == "[0]"</div><div class="flow-arrow">↓</div><div class="flow-item">Extract data[] From Request</div><div class="flow-arrow">↓</div><div class="flow-item">Insert Main Record Into hotels</div><div class="flow-arrow">↓</div><div class="flow-item">If logo → Insert Into Media</div><div class="flow-arrow">↓</div><div class="flow-item">If media[] → Insert Each Into Media</div><div class="flow-arrow">↓</div><div class="flow-item">If mapping → Insert Into mapping_accommodations</div><div class="flow-arrow">↓</div><div class="flow-item">Dispatch 3 Background Jobs</div><div class="flow-arrow">↓</div><div class="flow-item">Return Success JSON</div></div></div>

# POST /v2/core/accommodation/update

# Core Accommodation Update

این اندپوینت برای به‌روزرسانی اطلاعات یک اقامتگاه موجود استفاده می‌شود. رفتار اندپوینت در دو حالت انجام می‌شود:   
**method = "update"** → به‌روزرسانی کامل رکورد اصلی (hotels)، ثبت لوگو و رسانه، به‌روزرسانی یا ایجاد Mapping، و اجرای Jobها.  
**method = "board"** → فقط آپدیت فیلد Board و اجرای Jobهای مرتبط.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/update`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@updateAccommodation</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-operator.b">- JWT معتبر
- operator.branch == "\[0\]"

</div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>ID اقامتگاه</td></tr><tr><td>method</td><td>string</td><td>yes</td><td>مقدار: update یا board</td></tr><tr><td>data</td><td>object</td><td>yes</td><td>شیء اصلی داده‌های ورودی</td></tr></tbody></table>

</div>در حالت **method = update**، ساختار `data` شامل تمام فیلدهای زیر است:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>نوع اقامتگاه</td></tr><tr><td>title\_fa</td><td>string</td><td>نام فارسی</td></tr><tr><td>title\_en</td><td>string</td><td>نام انگلیسی</td></tr><tr><td>rate</td><td>int</td><td>ستاره</td></tr><tr><td>country</td><td>int</td><td>ID کشور</td></tr><tr><td>state</td><td>int</td><td>ID استان</td></tr><tr><td>city</td><td>int</td><td>ID شهر</td></tr><tr><td>location</td><td>string</td><td>موقعیت جغرافیایی</td></tr><tr><td>address</td><td>string</td><td>آدرس کامل</td></tr><tr><td>description</td><td>string</td><td>توضیحات</td></tr><tr><td>board</td><td>array</td><td>BB/HB/FB … یا خالی</td></tr><tr><td>possibilities</td><td>array</td><td>امکانات هتل</td></tr><tr><td>details</td><td>object</td><td>JSON کامل جزئیات</td></tr><tr><td>site</td><td>string</td><td>وب‌سایت</td></tr><tr><td>email</td><td>string</td><td>ایمیل</td></tr><tr><td>mobile</td><td>string</td><td>موبایل</td></tr><tr><td>phone</td><td>string</td><td>تلفن ثابت</td></tr><tr><td>leader\_name</td><td>string</td><td>نام مدیر</td></tr><tr><td>leader\_mobile</td><td>string</td><td>موبایل مدیر</td></tr><tr><td>confirm\_status</td><td>int</td><td>تأیید/عدم‌تأیید</td></tr><tr><td>priority</td><td>int</td><td>اولویت نمایش</td></tr><tr><td>logo</td><td>string|null</td><td>مسیر عکس لوگو</td></tr><tr><td>media</td><td>array</td><td>گالری تصاویر</td></tr><tr><td>mapping</td><td>object|null</td><td>اطلاعات Supplier Mapping</td></tr></tbody></table>

</div>## Update Logic (method = "update")

<div class="api-docs" id="bkmrk-%D8%A2%D9%BE%D8%AF%DB%8C%D8%AA-%DA%A9%D8%A7%D9%85%D9%84-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AF%D8%B1-">- آپدیت کامل رکورد در جدول `hotels` با Query Builder
- board و possibilities در صورت خالی بودن → null
- details همیشه json\_encode می‌شود
- updated\_at ← Carbon::now()

</div>### Media Update Rules

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-logo-%D8%AC%D8%AF%DB%8C%D8%AF-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-">- اگر logo جدید ارسال شده باشد: 
    - extract extension
    - چک تکراری بودن در Media
    - در صورت عدم وجود → create
- media\[\]: 
    - برای هر آیتم extension استخراج می‌شود
    - چک وجود رکورد
    - در صورت نبودن → create

</div>### Mapping Logic

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-mapping-%D9%88%D8%AC%D9%88%D8%AF-%D8%AF%D8%A7%D8%B4">- اگر mapping وجود داشته باشد: 
    - اگر رکورد mapping\_accommodations وجود داشته باشد → update
    - در غیر اینصورت → insert
    - کلیدهای پشتیبانی‌شده: `tport, sepehr, snapptrip, alibaba, eghamat24, iranhotelonline`

</div>## Update Logic (method = "board")

<div class="api-docs" id="bkmrk-%D9%81%D9%82%D8%B7-%D9%81%DB%8C%D9%84%D8%AF-board-%D8%A2%D9%BE%D8%AF%DB%8C%D8%AA">- فقط فیلد board آپدیت می‌شود
- null در صورت خالی بودن
- updated\_at آپدیت می‌شود

</div>## Background Jobs

<div class="api-docs" id="bkmrk-job-queue-delay-scop"><table class="schema-table"><thead><tr><th>Job</th><th>Queue</th><th>Delay</th><th>Scope</th></tr></thead><tbody><tr><td>UpdateRedis</td><td>fastJob</td><td>none</td><td>method = update → hotels و hotels\_details   
method = board → فقط hotels\_details</td></tr><tr><td>SystemLog</td><td>snailJob</td><td>10 minutes</td><td>update → نوع UpdateAccommodation   
board → نوع BoardAccommodation</td></tr></tbody></table>

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "message": "1402 | Accommodation updated!"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%92-valid"><div class="flowchart"><div class="flow-item">Validate JWT → Validate Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Read method / id / data</div><div class="flow-arrow">↓</div><div class="flow-item">IF method = update</div><div class="flow-arrow">↓</div><div class="flow-item">Update hotels Record</div><div class="flow-arrow">↓</div><div class="flow-item">Insert logo if new</div><div class="flow-arrow">↓</div><div class="flow-item">Insert media images if new</div><div class="flow-arrow">↓</div><div class="flow-item">Insert/Update Supplier Mapping</div><div class="flow-arrow">↓</div><div class="flow-item">Dispatch UpdateRedis + SystemLog</div><div class="flow-arrow">↓</div><div class="flow-item">Return Success</div>  
  
<div class="flow-item">IF method = board</div><div class="flow-arrow">↓</div><div class="flow-item">Update board Field Only</div><div class="flow-arrow">↓</div><div class="flow-item">Dispatch UpdateRedis(board) + SystemLog</div><div class="flow-arrow">↓</div><div class="flow-item">Return Success</div></div></div>

# DELETE /v2/core/accommodation/delete

# Core Accommodation Delete

این اندپوینت برای حذف کامل یک اقامتگاه (Hotel / Accommodation) از سیستم استفاده می‌شود. عملیات حذف فقط رکورد اصلی جدول `hotels` را پاک می‌کند و هیچ عمل پاکسازی روی Media یا Mapping انجام نمی‌دهد. پس از حذف، یک SystemLog با تأخیر ۱۰ دقیقه به صف `snailJob` ارسال می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/delete`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CoreController@deleteAccommodation</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%28authwithj">- JWT معتبر (authWithJwt)
- اجازه دسترسی فقط در صورت `operator.branch == "[0]"` (middleware: core)

</div>## Request Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>ID اقامتگاه برای حذف</td></tr></tbody></table>

</div>پارامتر `id` باید به‌صورت QueryParam یا BodyParam (بسته به ساختار درخواست) ارسال شود.

<div class="api-docs" id="bkmrk--1"></div>## Delete Logic

<div class="api-docs" id="bkmrk-%D8%AD%D8%B0%D9%81-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A7%D8%B5%D9%84%DB%8C-%D8%A7%D9%82%D8%A7%D9%85%D8%AA">- حذف رکورد اصلی اقامتگاه از جدول `hotels`
- هیچگونه عملیات پاکسازی روی: 
    - Media (لوگو، گالری)
    - mapping\_accommodations
    - Redis یا Cache
    
    انجام نمی‌شود.
- پس از حذف، یک SystemLog با نوع `DeleteAccommodation` ثبت و به صف ارسال می‌شود.

</div>### SystemLog Payload

```
{
  "type": "DeleteAccommodation",
  "data": null,
  "goal": ,
  "by": operator.id,
  "agent": HTTP_USER_AGENT,
  "ip": client_ip,
  "datetime": now()
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "message": "1402 | Accommodation deleted!"
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Flowchart

<div class="api-docs" id="bkmrk-authwithjwt-%E2%86%92-valida"><div class="flowchart"><div class="flow-item">AuthWithJWT → Validate Token</div><div class="flow-arrow">↓</div><div class="flow-item">Core Access Check → operator.branch == "[0]"</div><div class="flow-arrow">↓</div><div class="flow-item">Read id From Request</div><div class="flow-arrow">↓</div><div class="flow-item">DELETE FROM hotels WHERE id = ?</div><div class="flow-arrow">↓</div><div class="flow-item">Dispatch SystemLog(DeleteAccommodation) + delay(10m)</div><div class="flow-arrow">↓</div><div class="flow-item">Return Success JSON</div></div></div>

# GET /v2/core/accommodation/view

# Core Accommodation View

این اندپوینت اطلاعات کامل یک اقامتگاه را بر اساس شناسه ارسال‌شده بازگردانی می‌کند. داده‌های هتل، اطلاعات کشور/استان/شهر، مدیا (لوگو + گالری)، جزئیات، امکانات، برد، اطلاعات مدیریت و Mapping Supplierها به‌صورت کامل تجمیع و استانداردسازی می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/view`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@viewAccommodation</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-middleware">- JWT معتبر
- middleware core → شرط: `operator.branch == "[0]"`

</div>## Request Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>ID اقامتگاه برای مشاهده</td></tr></tbody></table>

</div>## Lookup Logic

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%A7%D8%B5%D9%84%DB%8C-%D8%A7%D8%B2">- دریافت رکورد اصلی از جدول `hotels`.
- اگر `country` مقدار null باشد → مقدار پیش‌فرض 118.
- واکشی اطلاعات: 
    - `countries` (با nationality)
    - `states`
    - `cities`
    
    سپس تبدیل ساختار به فرمت استاندارد front‑end: 
    - `title.fa` و `title.en`
    - icon → ISO
- واکشی Mapping از جدول `mapping_accommodations`: 
    - اگر رکورد وجود داشته باشد: id, accommodation, status حذف می‌شود
    - هر مقدار null → false
    - اگر رکورد نبود → خروجی: `false`
- واکشی Media از جدول `media`: 
    - type = logo → ذخیره در logo
    - type != logo → ورود به آرایه media\[\]
    - اگر هیچ رسانه‌ای وجود نداشت: 
        - logo = null
        - media = \[\]
- Decode کردن: 
    - `details`
    - `board`
    - `possibilities`
- تجمیع نهایی در آرایه `$return` و بازگشت.

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "data": {
    "title": {
      "fa": "...",
      "en": "..."
    },
    "type": "...",
    "rate": 4,
    "country": {
      "id": 118,
      "icon": "IR",
      "title": { "fa": "...", "en": "..." },
      "nationality": { "fa": "...", "en": "..." }
    },
    "state": { "id": 1, "title": { "fa": "...", "en": "..." } },
    "city":  { "id": 2, "title": { "fa": "...", "en": "..." } },

    "phone": "...",
    "mobile": "...",
    "email": "...",
    "site": "...",
    "address": "...",
    "location": "...",

    "logo": "... or false",
    "media": ["..."] or false,

    "details": {...},
    "board": [...],
    "possibilities": [...],

    "leader_name": "...",
    "leader_mobile": "...",
    "description": "...",

    "confirm_status": 1,
    "priority": 10,

    "mapping": {
      "tport": true|false,
      "sepehr": true|false,
      "snapptrip": true|false,
      "alibaba": true|false,
      "eghamat24": true|false,
      "iranhotelonline": true|false
    },

    "status": 1
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%26-core-"><div class="flowchart"><div class="flow-item">Validate JWT &amp; Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Read id</div><div class="flow-arrow">↓</div><div class="flow-item">SELECT * FROM hotels WHERE id=?</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch &amp; Normalize Country</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch State &amp; City</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Mapping → Convert null→false</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch Media → logo + media[]</div><div class="flow-arrow">↓</div><div class="flow-item">Decode details/possibilities/board</div><div class="flow-arrow">↓</div><div class="flow-item">Assemble Response Payload</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON</div></div></div>

# POST /v2/core/accommodation/supplier/store

# Core Accommodation Supplier Store

این اندپوینت برای ایجاد یک **Supplier** جدید (نوع colleague) و اتصال آن به یک اقامتگاه استفاده می‌شود. این عملیات شامل ایجاد رکورد جدید در جدول `colleagues` با سریال اختصاصی و سپس ثبت ارتباط در `mapping_colleagues` است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/supplier/store`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@supplierAccommodation</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%AF%D8%B1-middlew">- JWT معتبر
- در middleware core، فقط زمانی اجازه داده می‌شود که `operator.branch == "[0]"`

</div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>data</td><td>object</td><td>yes</td><td>اطلاعات کامل Supplier</td></tr><tr><td>accommodation\_id</td><td>integer</td><td>yes</td><td>ID اقامتگاه که Supplier به آن متصل می‌شود</td></tr></tbody></table>

</div>### Structure of data

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title.fa</td><td>string</td><td>نام فارسی Supplier</td></tr><tr><td>title.en</td><td>string</td><td>نام انگلیسی Supplier</td></tr><tr><td>logo</td><td>string|null</td><td>آدرس یا مسیر لوگو</td></tr></tbody></table>

</div>## Insert Logic

### 1. ایجاد رکورد colleague

<div class="api-docs" id="bkmrk-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AF%D8%B1-%D8%AC%D8%AF%D9%88%D9%84-collea">- رکورد در جدول `colleagues` درج می‌شود.
- **branch** ثابت و برابر **2** است.
- **serial** با فراخوانی: <div class="code-inline">StaticController::getSerialId('colleague', 2)</div>تولید می‌شود.
- **foundation = 1**
- **category = 5**
- **type = 1**
- فیلدهای office و office\_en از: 
    - `data.title.fa`
    - `data.title.en`
- فیلد logo مقداردهی می‌شود.
- **created\_at** و **updated\_at** هر دو با `Carbon::now()->toDateTimeString()` مقدار می‌گیرند.

</div>### 2. درج در mapping\_colleagues

<div class="api-docs" id="bkmrk-colleague-%3D-id-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF">- colleague = ID رکورد ایجادشده
- accommodation = مقدار ارسال‌شده در `accommodation_id`

</div>## Serial Generation Logic (colleague)

فراخوانی زیر انجام می‌شود:

```
StaticController::getSerialId('colleague', 2)
```

منطق:

<div class="api-docs" id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84%3A-colleagues-que">- جدول: `colleagues`
- Query: بر اساس branch = 2
- اگر رکورد وجود داشته باشد → serial آخر + 1
- اگر هیچ رکوردی نباشد → مقدار اولیه = 2

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "message": "1023 | Supplier stored!"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%26-core-"><div class="flowchart"><div class="flow-item">Validate JWT &amp; Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Read data + accommodation_id</div><div class="flow-arrow">↓</div><div class="flow-item">Generate colleague serial (getSerialId)</div><div class="flow-arrow">↓</div><div class="flow-item">Insert colleague into colleagues table</div><div class="flow-arrow">↓</div><div class="flow-item">Insert mapping to mapping_colleagues</div><div class="flow-arrow">↓</div><div class="flow-item">Return success JSON</div></div></div>

# POST /v2/core/airlines/list

# Core Airlines List

این اندپوینت لیست خطوط هوایی را با قابلیت فیلترینگ پیشرفته، Pagination سفارشی و Pull اطلاعات کشور از Redis یا Database باز می‌گرداند. منطق Pagination به صورت DataTables‑Style انجام شده و ساختار پاسخ دقیقاً با نیازهای فرانت هماهنگ است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fairlin"><div class="endpoint-info"><div>**URL:** `/v2/core/airlines/list`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@airlinesList</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%B4%D8%B1%D8%B7-middle">- JWT معتبر
- شرط middleware core → `operator.branch == "[0]"`

</div>## Request Body

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>json</td><td>string (JSON object)</td><td>yes</td><td>پارامترهای کامل DataTables شامل start, length, draw و فیلترهای advanced</td></tr><tr><td>lang.id</td><td>string|integer</td><td>optional</td><td>شناسه زبان انتخاب‌شده (در این روت استفاده‌ی عملی ندارد)</td></tr></tbody></table>

</div>### Structure Inside json

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>start</td><td>integer</td><td>مقدار اولیه offset</td></tr><tr><td>length</td><td>integer</td><td>تعداد رکوردهای هر صفحه</td></tr><tr><td>draw</td><td>integer</td><td>شاخص درخواست DataTables</td></tr><tr><td>advanced.country</td><td>string</td><td>فیلتر بر اساس کشور</td></tr><tr><td>advanced.iata</td><td>string</td><td>فیلتر بر اساس IATA</td></tr><tr><td>advanced.icao</td><td>string</td><td>فیلتر بر اساس ICAO</td></tr><tr><td>advanced.r</td><td>string</td><td>فیلتر جستجو روی en\_title, fa\_title, description</td></tr></tbody></table>

</div>## Pagination Logic

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-start-%3D-0-%D8%A8%D8%A7%D8%B4%D8%AF-%E2%86%92">- اگر start = 0 باشد → start = length
- در غیر این صورت → start = start + length
- مقدار currentPage = start / length
- Query با paginate اجرا می‌شود

</div>## Filtering Logic

تمام فیلترها داخل یک Closure در where قرار گرفته‌اند:

<div class="api-docs" id="bkmrk-country-%E2%86%92-%D8%A7%DA%AF%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-">- country → اگر مقدار خالی نباشد، where('country')
- iata → اگر مقدار خالی نباشد، where('iata')
- icao → اگر مقدار خالی نباشد، where('icao')
- r → applied on: 
    - en\_title LIKE %r%
    - fa\_title LIKE %r%
    - description LIKE %r%
- status = 1
- ORDER BY id DESC

</div>## Country Fetch Logic (Redis + DB Fallback)

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%AA%D9%84%D8%A7%D8%B4-%D9%85%DB%8C%E2%80%8C%D8%B4%D9%88%D8%AF-co">- ابتدا تلاش می‌شود country از Redis با کلید `countries:{country_id}` خوانده شود.
- اگر Redis مقدار نداشت: 
    - query روی جدول countries (status=1, id, iso, fa\_name, en\_name, fa\_nationality, en\_nationality)
    - نتیجه در Redis ذخیره می‌شود
- country نهایی در خروجی فقط **fa\_name** است (در صورت نبود → همان country\_id)

</div>## Final Output Mapping

برای هر airline:

```
{
  "id": ...,
  "title": airline.en_title,
  "title_fa": airline.fa_title,
  "icao": airline.icao,
  "iata": airline.iata,
  "country": resolved_country_fa_name,
  "description": airline.description,
  "logo": airline.logo,
  "status": airline.status
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "draw": ,
  "recordsTotal": ,
  "recordsFiltered": ,
  "data": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%26-core-"><div class="flowchart"><div class="flow-item">Validate JWT &amp; Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Parse json → Data object</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Pagination (start / length)</div><div class="flow-arrow">↓</div><div class="flow-item">Apply Filters (country, iata, icao, r)</div><div class="flow-arrow">↓</div><div class="flow-item">Query airlines with paginate</div><div class="flow-arrow">↓</div><div class="flow-item">For each airline → Fetch country (Redis → DB)</div><div class="flow-arrow">↓</div><div class="flow-item">Assemble Response Rows</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON with draw, recordsTotal, data</div></div></div>

# POST /v2/core/airports/list

# Core Airports List

این اندپوینت برای دریافت لیست فرودگاه‌ها با فیلترهای پیشرفته، Pagination مشابه DataTables، و غنی‌سازی داده‌ها (Country از Redis/DB و City از DB) استفاده می‌شود. خروجی کاملاً استاندارد و سازگار با سیستم‌های مدیریت داده (DataTables‑Compatible Response) است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fairpor"><div class="endpoint-info"><div>**URL:** `/v2/core/airports/list`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@airportsList</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-middleware">- JWT معتبر
- middleware core فقط وقتی اجازه می‌دهد که: <div class="code-inline">operator.branch == "[0]"</div>

</div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>json</td><td>string (JSON)</td><td>yes</td><td>حاوی پارامترهای DataTables (start, length, draw, advanced filters)</td></tr><tr><td>lang.id</td><td>string|integer</td><td>optional</td><td>شناسه زبان (در این متد استفاده عملی ندارد)</td></tr></tbody></table>

</div>### Structure Inside json

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>start</td><td>integer</td><td>Offset اولیه</td></tr><tr><td>length</td><td>integer</td><td>اندازه هر صفحه</td></tr><tr><td>draw</td><td>integer</td><td>شاخص درخواست DataTables</td></tr><tr><td>advanced.country</td><td>string</td><td>فیلتر کشور</td></tr><tr><td>advanced.state</td><td>string</td><td>فیلتر استان</td></tr><tr><td>advanced.city</td><td>string</td><td>فیلتر شهر</td></tr><tr><td>advanced.iata</td><td>string</td><td>فیلتر IATA</td></tr><tr><td>advanced.type</td><td>string</td><td>فیلتر نوع فرودگاه</td></tr><tr><td>advanced.r</td><td>string</td><td>جستجوی آزاد روی title, title\_fa, description, address</td></tr></tbody></table>

</div>## Pagination Logic

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-start-%3D-0-%E2%86%92-star">- اگر start = 0 → start = length
- در غیر این صورت → start = start + length
- page = (start / length)
- کوئری paginate با pageName = "airports"

</div>## Filtering Logic

تمام فیلترها داخل Closure:

<div class="api-docs" id="bkmrk-country-%E2%86%92-where%28%27cou">- country → where('country')
- state → where('state')
- city → where('city')
- iata → where('iata')
- type → where('type')
- r → applied on: 
    - title LIKE %r%
    - title\_fa LIKE %r%
    - description LIKE %r%
    - address LIKE %r%
- status = 1
- ORDER BY id DESC

</div>## Country &amp; City Enrichment

### Country (Redis → DB Fallback)

<div class="api-docs" id="bkmrk-redis-key%3A-countries">- Redis key: `countries:{country_id}`
- اگر موجود نبود: 
    - اطلاعات از DB خوانده می‌شود (جدول countries)
    - در Redis ذخیره می‌شود
- در خروجی: فقط fa\_name (در صورت نبود → id اصلی)

</div>### City Resolution

Lookup مستقیم DB:

<div class="api-docs" id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84-cities-%D8%A8%D8%A7-join-">- جدول cities با join روی states
- خروجی شامل: 
    - city.title\_fa → نمایش نام فارسی شهر
    - state.fa\_name → نمایش استان
- اگر City یافت نشد → فیلدهای state و city از DB اصلی airport (city/state ID) استفاده می‌شود.

</div>## Final Output Mapping

```
{
  "id": airport.id,
  "title": airport.title,
  "title_fa": airport.title_fa,
  "iata": airport.iata,
  "country": resolved_country,
  "state": resolved_state_name,
  "city": resolved_city_name,
  "location": airport.location,
  "description": airport.description,
  "logo": airport.logo,
  "image": airport.image,
  "status": airport.status
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "draw": ,
  "recordsTotal": ,
  "recordsFiltered": ,
  "data": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "(Exception message)",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%26-core-"><div class="flowchart"><div class="flow-item">Validate JWT &amp; Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Parse json → Data object</div><div class="flow-arrow">↓</div><div class="flow-item">Compute Pagination (start / length)</div><div class="flow-arrow">↓</div><div class="flow-item">Apply Filters (country/state/city/iata/type/r)</div><div class="flow-arrow">↓</div><div class="flow-item">Query airports with paginate</div><div class="flow-arrow">↓</div><div class="flow-item">Resolve Country (Redis → DB)</div><div class="flow-arrow">↓</div><div class="flow-item">Resolve City + State from tables</div><div class="flow-arrow">↓</div><div class="flow-item">Assemble Response Array</div><div class="flow-arrow">↓</div><div class="flow-item">Return DataTables-Compatible JSON</div></div></div>

# POST /v2/core/socket/send

# Core Socket: Send Message

این اندپوینت یک پیام را از طریق سیستم Broadcasting لاراول ارسال می‌کند. پیام به Event **TradeOperation** پاس داده می‌شود و با `toOthers()` فقط به کلاینت‌های دیگر (غیر از فرستنده) Broadcast می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fsocket"><div class="endpoint-info"><div>**URL:** `/v2/core/socket/send`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@socketSendMessage</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA-%D9%85">- JWT معتبر لازم است
- میان‌افزار core تنها وقتی اجازه می‌دهد که: <div class="code-inline">operator.branch == "[0]"</div>

</div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>message</td><td>string</td><td>yes</td><td>متن پیام که باید از طریق WebSocket (Broadcast) ارسال شود</td></tr></tbody></table>

</div>## Broadcast Logic

پس از دریافت message:

<div class="api-docs" id="bkmrk-%DB%8C%DA%A9-event-%D8%A7%D8%B2-%D9%86%D9%88%D8%B9-trad">- یک Event از نوع `TradeOperation` ساخته می‌شود:

</div>```
broadcast(new TradeOperation($message))
```

<div class="api-docs" id="bkmrk-%D9%81%D9%82%D8%B7-%D8%A8%D9%87-%D8%AF%DB%8C%DA%AF%D8%B1-%DA%A9%D9%84%D8%A7%DB%8C%D9%86%D8%AA%E2%80%8C%D9%87">- فقط به دیگر کلاینت‌ها ارسال می‌شود (نه فرستنده):

</div>```
->toOthers()
```

<div class="api-docs" id="bkmrk-broadcasting-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%AF%D8%B1">- Broadcasting باید در کانال‌های Laravel Echo/Pusher/SocketServer پیکربندی شده باشد

</div>## Response (Success)

```
{
  "status": "Message Sent!"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

در این نسخه از کد هندل‌کردن Exception وجود ندارد، اما در صورت بروز خطا در سطح سیستم (مثلاً عدم کارکرد Broadcast Driver)، لاراول پاسخ خطا برمی‌گرداند.

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%26-core-"><div class="flowchart"><div class="flow-item">Validate JWT &amp; Core Middleware</div><div class="flow-arrow">↓</div><div class="flow-item">Extract message from request</div><div class="flow-arrow">↓</div><div class="flow-item">broadcast(new TradeOperation(message))</div><div class="flow-arrow">↓</div><div class="flow-item">Send to Others (WebSocket Clients)</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON: "Message Sent!"</div></div></div>

# GET /v2/core/changelogs

# Core System Changelogs

این اندپوینت وظیفه ارائه نسخه‌بندی کامل تغییرات سیستم (Change Log) را بر اساس ساختار SemVer توسعه‌یافته (major.minor.patch) بر عهده دارد. داده‌ها از جدول `change_logs` دریافت می‌شوند، سپس در قالب گروه‌بندی‌شده بر اساس نسخه، بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fchange"><div class="endpoint-info"><div>**URL:** `/v2/core/changelogs`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@changeLogs</div><div>**Middleware Stack:** authWithJwt → core</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AA%D9%88%DA%A9%D9%86-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3">- توکن JWT معتبر
- دسترسی فقط برای branch = "\[0\]" (براساس middleware core)

</div>## Database Query Logic

دریافت تمامی لاگ‌ها از جدول `change_logs` با مرتب‌سازی دقیق نسخه:

<div class="api-docs" id="bkmrk-orderby-major-desc-o">- orderBy major DESC
- orderBy minor DESC
- orderBy patches DESC
- orderBy created\_at ASC (برای نمایش ترتیب وقوع تغییرات در هر نسخه)

</div>```
DB::table('change_logs')
    ->orderBy('major', 'DESC')
    ->orderBy('minor', 'DESC')
    ->orderBy('patches', 'DESC')
    ->orderBy('created_at')
    ->get();
  
```

<div class="api-docs" id="bkmrk--1"></div>## Version Grouping Logic

هر رکورد داخل گروه مخصوص نسخه قرار می‌گیرد. کلید گروه نسخه برابر است با: `major . minor . patches` بدون نقطه (مثلاً 231 → نسخه 02.03.01).

نسخه نهایی با صفرهای سمت چپ فرمت می‌شود:

<div class="api-docs" id="bkmrk-major-%E2%86%92-%D8%AF%D9%88-%D8%B1%D9%82%D9%85%DB%8C-mino">- major → دو رقمی
- minor → دو رقمی
- patch → دو رقمی

</div>```
$changeLogs[$major.$minor.$patch]['version']
    = sprintf("%02d", major)
    . '.' . sprintf("%02d", minor)
    . '.' . sprintf("%02d", patch);
  
```

### ساختار داده هر آیتم

```
[
  "title"       => row.title,
  "content"     => row.content,
  "created_at"  => CalendarUtils::strftime('Y/m/d', strtotime(row.created_at)),
  "tag"         => row.tag
]
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "data": {
    "020301": {
      "version": "02.03.01",
      "data": [
        {
          "title": "Fix Accounting Issues",
          "content": "Detail text ...",
          "created_at": "1403/07/12",
          "tag": "fix"
        },
        ...
      ]
    },
    ...
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Response (Error)

در این متد try/catch وجود ندارد؛ هر خطای دیتابیس یا رانتایم به‌صورت Exception خام از طرف لاراول برگشته می‌شود (HTTP 500).

<div class="api-docs" id="bkmrk--4"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%2B-core-"><div class="flowchart"><div class="flow-item">Validate JWT + Core Access</div><div class="flow-arrow">↓</div><div class="flow-item">Query change_logs (ordered)</div><div class="flow-arrow">↓</div><div class="flow-item">Group by Version (major/minor/patch)</div><div class="flow-arrow">↓</div><div class="flow-item">Format Version &amp; Date</div><div class="flow-arrow">↓</div><div class="flow-item">Return Structured JSON</div></div></div>

# GET /v2/core/accommodation/view

# Core Accommodation: View Details

این اندپوینت برای دریافت جزئیات کامل یک هتل در سیستم Core استفاده می‌شود. داده‌ها از جداول اصلی (hotels، countries، states، cities)، جدول mapping\_accommodations و جدول media بارگذاری شده و سپس در یک ساختار استاندارد و نرمال‌سازی شده بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Faccomm"><div class="endpoint-info"><div>**URL:** `/v2/core/accommodation/view`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@viewAccommodation</div><div>**Middleware Stack:** authWithJwt (بدون core)</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D8%AF%D8%B1">- نیازمند JWT معتبر
- در این روت middleware `core` وجود ندارد → برای تمام کاربران معتبر قابل مشاهده است

</div>## Request Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>شناسه هتل در جدول `hotels`</td></tr></tbody></table>

</div>## Hotel Record Loading

جزئیات اولیه هتل از جدول `hotels` بارگذاری می‌شود:

```
$item = DB::table('hotels')
    ->where('id', request->id)
    ->first();
  
```

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-country-%D8%A8%D8%B1">- اگر مقدار country برابر null باشد، مقدار پیش‌فرض 118 جایگزین می‌شود

</div>## Country Resolution

کشور با شرط‌های زیر لود می‌شود:

<div class="api-docs" id="bkmrk-status-%3D-1-fa_nation">- status = 1
- fa\_nationality != null

</div>```
[
  "id" => country.id,
  "icon" => country.iso,
  "title" => [
    "fa" => country.fa_name,
    "en" => country.en_name
  ],
  "nationality" => [
    "fa" => country.fa_nationality,
    "en" => country.en_nationality
  ]
]
  
```

<div class="api-docs" id="bkmrk--1"></div>## State Resolution

```
$state = [
  "id" => state.id,
  "title" => [
    "fa" => state.title_fa,
    "en" => state.title_en
  ]
];
  
```

<div class="api-docs" id="bkmrk--2"></div>## City Resolution

```
$city = [
  "id" => city.id,
  "title" => [
    "fa" => city.title_fa,
    "en" => city.title_en
  ]
];
  
```

<div class="api-docs" id="bkmrk--3"></div>## Mapping Resolution

اگر رکورد فعال وجود داشته باشد، فیلدهای null به `false` تبدیل می‌شوند؛ در غیر این صورت مقدار نهایی `false` است.

```
{
  "tport": value or false,
  "sepehr": value or false,
  "snapptrip": value or false,
  "alibaba": value or false,
  "eghamat24": value or false,
  "iranhotelonline": value or false
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Media Resolution

همه مدیاها با شرط‌های زیر لود می‌شوند:

<div class="api-docs" id="bkmrk-related_id-%3D-id-rela">- related\_id = id
- related\_category = accommodations

</div>### ساختار خروجی نهایی مدیا:

```
{
  "logo": "/path/to/logo.jpg" or false,
  "media": ["/path/a.jpg","/path/b.jpg"] or false
}
  
```

<div class="api-docs" id="bkmrk--5"></div>## Final Response Mapping

ساختار کلی:

```
{
  "title": {
    "fa": item.fa_title,
    "en": item.en_title
  },
  "type": item.type,
  "rate": item.rate,
  "country": country_object,
  "state": state_object,
  "city": city_object,
  "phone": item.phone,
  "mobile": item.mobile,
  "email": item.email,
  "site": item.site,
  "address": item.address,
  "location": item.location,
  "logo": logo or false,
  "media": media_list or false,
  "details": json_decode(item.details),
  "board": json_decode(item.board),
  "possibilities": json_decode(item.possibilities),
  "leader_name": item.leader_name,
  "leader_mobile": item.leader_mobile,
  "description": item.description,
  "confirm_status": item.confirm_status,
  "priority": item.priority,
  "mapping": mapping_object or false,
  "status": item.status
}
  
```

<div class="api-docs" id="bkmrk--6"></div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "data": { ... }
}
  
```

<div class="api-docs" id="bkmrk--7"></div>## Response (Error)

```
{
  "status": false,
  "code": "1003",
  "message": "Exception message",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--8"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-load-"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Load hotel record</div><div class="flow-arrow">↓</div><div class="flow-item">Resolve country/state/city</div><div class="flow-arrow">↓</div><div class="flow-item">Load mapping_accommodations</div><div class="flow-arrow">↓</div><div class="flow-item">Load media (logo + gallery)</div><div class="flow-arrow">↓</div><div class="flow-item">Assemble standard response</div><div class="flow-arrow">↓</div><div class="flow-item">Return final JSON</div></div></div>

# POST /v2/core/system/report

# Core System: Add System Report

این اندپوینت برای ثبت گزارش سیستمی (System Report) در بخش Core استفاده می‌شود. داده‌ها به متد `Visa::AddSystemReport()` ارسال شده و پس از ثبت، یک کد رهگیری (tracking\_code) تولید و برگردانده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fsystem"><div class="endpoint-info"><div>**URL:** `/v2/core/system/report`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@addSystemReport</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA-%D8%AF">- JWT معتبر لازم است
- در این روت هیچ محدودیت branch یا middleware core وجود ندارد
- اطلاعات operator از داخل JWT توسط سیستم تزریق می‌شود: <div class="code-inline">$request-&gt;get('operator')</div>

</div>## Request Body Schema

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>data</td><td>mixed</td><td>yes</td><td>داده خام گزارش. محتوای آن ۱۰۰٪ وابسته به `Visa::AddSystemReport()` است.</td></tr><tr><td>operator</td><td>object</td><td>auto</td><td>به صورت خودکار از JWT استخراج می‌شود؛ شامل اطلاعات کاربری. از این object فقط `id` استفاده می‌شود.</td></tr></tbody></table>

</div>## Core Processing Logic

ورودی‌ها مستقیماً وارد سرویس ثبت گزارش می‌شوند:

```
$insert = Visa::AddSystemReport(
    $request->get('data'),
    $request->get('operator')->id
);
  
```

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D8%A7%D9%88%D9%84%3A-%D9%85%D8%AD%D8%AA%D9%88%D8%A7%DB%8C-">- پارامتر اول: محتوای گزارش (data)
- پارامتر دوم: ID اپراتور ارسال کننده گزارش
- خروجی متد شامل کلید `Code` است که نقش tracking\_code را دارد

</div>## Response (Success)

کد وضعیت HTTP: **201 Created**

```
{
  "payload": {
    "tracking_code": "ABC123456"
  },
  "meta": {
    "timestamp": 1710000000
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

در این متد هیچ try/catch وجود ندارد، بنابراین هرگونه Exception از طرف Visa::AddSystemReport یا دیتابیس → خطای 500 بازگشت داده می‌شود.

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-extra"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Extract: data + operator.id</div><div class="flow-arrow">↓</div><div class="flow-item">Call Visa::AddSystemReport(data, operator_id)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive tracking_code (insert['Code'])</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON (201 Created)</div></div></div>

# GET /v2/charter (Multi‑Mode Charter Loader)

# Charter: Get Charter(s) Details

این اندپوینت، هسته اصلی بازیابی اطلاعات چارترهاست. بسته به ورودی، می‌تواند یک چارتر واحد را برگرداند، یا چندین چارتر را به‌صورت آرایه، و همچنین بسته به مقدار `action` می‌تواند چهار سطح مختلف از داده‌ها را برگرداند: **all / list / base / calculations / capacity**. این Endpoint از کلاس پیچیده `CharterResource` استفاده می‌کند که شامل غنی‌سازی بسیار سنگین دیتاست.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter-met"><div class="endpoint-info"><div>**URL:** `/v2/charter`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@indexCharter</div><div>**Middleware:** authWithJwt</div></div></div>## Behavior Summary

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-id-%DB%8C%DA%A9-%D8%B9%D8%AF%D8%AF-%D8%A8%D8%A7%D8%B4%D8%AF-%E2%86%92">- اگر `id` یک عدد باشد → فقط یک چارتر را لود می‌کند
- اگر `id` آرایه باشد → چندین چارتر را لود می‌کند
- تمامی رکوردها از جدول `charters` خوانده می‌شوند
- داده نهایی با `new CharterResource()` تبدیل می‌شود

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer | array</td><td>yes</td><td>شناسه یک چارتر یا چند چارتر</td></tr><tr><td>action</td><td>string</td><td>optional</td><td>نوع خروجی را تعیین می‌کند: <div class="code-inline">all | list | base | calculations | capacity</div></td></tr><tr><td>item\_id</td><td>integer</td><td>optional</td><td>فقط در action = capacity</td></tr></tbody></table>

</div>## Single Charter Mode

```
if (is_numeric($request->id)) {
    $charter = DB::table('charters')->where('id', $request->id)->first();
}
  
```

اگر یافت نشود:

```
{
  "status": false,
  "time": 1710000000,
  "message": "item not found!"
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Multiple Charter Mode

```
$charters = DB::table('charters')->whereIn('id', $request->id)->get();
  
```

همه رکوردها map می‌شوند:

```
$charters->map(fn($item) => new CharterResource($item))
  
```

<div class="api-docs" id="bkmrk--2"></div>## CharterResource Overview

این کلاس بسیار بزرگ مسئول تبدیل رکورد دیتابیس به ساختارهای کامل، شامل اطلاعات اپراتور، محاسبات، قوانین، آیتم‌ها، ظرفیت رزرو، ویژگی‌ها، و ده‌ها نوع داده متفاوت دیگر است.

### Modes (action)

<div class="api-docs" id="bkmrk-all%3A-%DA%A9%D8%A7%D9%85%D9%84%E2%80%8C%D8%AA%D8%B1%DB%8C%D9%86-%D9%85%D9%88%D8%AF.-">- **all**: کامل‌ترین مود. شامل همه موارد: items, calculations, rules, schedule, reservation و …
- **list**: نسخه سبک برای لیست‌گیری
- **base**: اطلاعات پایه + قوانین + آیتم‌ها
- **calculations**: فقط آیتم‌ها و محاسبات مالی
- **capacity**: فقط ظرفیت‌های رزرو (با item\_id اختیاری)

</div>## Shared Data Loaders (Resource Level)

<div class="api-docs" id="bkmrk-%D8%A7%D9%BE%D8%B1%D8%A7%D8%AA%D9%88%D8%B1%3A-%D8%AC%D8%AF%D9%88%D9%84-operat">- اپراتور: جدول `operators`
- آیتم‌ها: جدول `charter_items`
- محاسبات: جدول `{table}_calculation`
- قوانین استرداد: `charter_refund_rules`
- قوانین عمومی: `charter_public_rules`
- ظرفیت رزرو: `ReservationController::capacityItemCharter()`
- اطلاعات کلی: `getInformation()`
- نوتیفیکشن زمان‌بندی شده: جدول `scheduled_notifications`

</div>## sortItems() Logic

این تابع یکی از سنگین‌ترین بخش‌هاست و بسته به method/submethod آیتم دیتا را enrich می‌کند:

<div class="api-docs" id="bkmrk-method-%3D-route-%28airc">- method = route (aircraft, train, bus, ship)
- method = accommodation
- method = service
- method = custom

</div>جزئیات شامل:

<div class="api-docs" id="bkmrk-%D9%84%DB%8C%D9%86%DA%A9%E2%80%8C%DA%A9%D8%B1%D8%AF%D9%86-airline%D8%8C-a">- لینک‌کردن airline، airports، aircraft
- محاسبه duration از روی دقیقه → فرمت HH:MM
- لوک‌آپ شرکت‌ها، supplierها و شهرها
- normalize کردن vehicle و company

</div>## sortCalculations() Logic

این قسمت شامل:

<div class="api-docs" id="bkmrk-%D9%84%D9%88%D8%AF-%D9%85%D8%A7%D9%84%DB%8C%D8%A7%D8%AA%E2%80%8C%D9%87%D8%A7%3A-%D8%AC%D8%AF%D9%88%D9%84-">- لود مالیات‌ها: جدول charter\_taxes
- لود خدمات: charter\_services + charter\_service\_categories
- محاسبات مبتنی بر type و subtype
- rule mapping برای: <div class="code-inline">commission / markup / citizenship</div>از جدول charter\_financial\_handling
- financial.base = قیمت‌ها
- financial.staircases
- financial.taxes
- financial.commissions
- financial.markups
- financial.citizenship

</div>## getInformation() Logic

بسته به type → title و blocks متفاوت تولید می‌کند. مقادیر در Redis کش می‌شوند تحت کلید:

```
charter:{id}:information:title
  
```

<div class="api-docs" id="bkmrk-tour-%E2%86%92-origin%2C-desti">- tour → origin, destination, start, end
- route → origin, destination, start
- accommodation → destination, start, end (+ hotel title)
- service → اطلاعات سرویس مثل بیمه/ویزا/CIP

</div>## Response Structure (Single Charter, action=all)

```
{
  "status": true,
  "time": 1710000000,
  "data": {
     "id": 123,
     "serial": 10000 + serial,
     "title": { fa, en },
     "type": "...",
     "subtype": "...",
     "capacity": ...,
     "buy": ...,
     "deadline": ...,
     "information": {...},
     "reservation": {...},
     "calculations": [...],
     "rules": { refund: [...], public: [...] },
     "operator": {...},
     "items": [...],
     "details": {...},
     "plan": ...,
     "slug": ...,
     "hub": ...,
     "schedule": {...} | false,
     "status": ...
  }
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Error Response

```
{
  "status": false,
  "time": 1710000000,
  "message": "item not found!"
}
  
```

<div class="api-docs" id="bkmrk--4"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-check"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Check id type (numeric | array)</div><div class="flow-arrow">↓</div><div class="flow-item">Load charter(s)</div><div class="flow-arrow">↓</div><div class="flow-item">If empty → return 'item not found'</div><div class="flow-arrow">↓</div><div class="flow-item">For each → new CharterResource()</div><div class="flow-arrow">↓</div><div class="flow-item">Inside Resource → action switch</div><div class="flow-arrow">↓</div><div class="flow-item">Load items, rules, calculations, information</div><div class="flow-arrow">↓</div><div class="flow-item">SortItems() + SortCalculations()</div><div class="flow-arrow">↓</div><div class="flow-item">Return Final JSON</div></div></div>

# POST /v2/charter

# Charter: Create Inventory (Store)

این اندپوینت قلب تپنده سیستم تعریف موجودی (Inventory) است. وظیفه آن دریافت یک الگوی زمانی و قیمتی، و تبدیل آن به صدها رکورد فیزیکی در دیتابیس است. این متد از یک **Replication Engine** داخلی استفاده می‌کند تا تاریخ‌های پرواز یا رزرو هتل را بر اساس الگوهای هفتگی، دوره‌ای یا تاریخ‌های خاص تولید کرده و تمام وابستگی‌های مالی و قانونی را در قالب **تراکنش‌های اتمیک** ذخیره کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter-met"><div class="endpoint-info"><div>**URL:** `/v2/charter`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@storeCharter</div><div>**Middleware:** authWithJwt</div></div></div>## Key Features &amp; Behavior

<div class="api-docs" id="bkmrk-poly-morphic-creatio">- **Poly-morphic Creation:** پشتیبانی همزمان از پرواز (Route) و هتل (Accommodation) با یک ساختار واحد.
- **Auto-Inversion:** تولید خودکار مسیر برگشت (Outbound) با جابجایی مبدا/مقصد و محاسبه اختلاف روز.
- **Smart Replication:** تولید انبوه رکوردها بر اساس روزهای هفته (شمسی) یا بازه‌های زمانی.
- **Financial Complexity:** مدیریت پلکانی قیمت‌ها، مارک‌آپ (Markup)، کمیسیون و قوانین ملیت.
- **Data Integrity:** استفاده از تراکنش دیتابیس برای هر تاریخ (Fail-safe per date).

</div>## Payload Schema (Root Level)

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>Yes</td><td>نوع موجودی: <div class="code-inline">route | accommodation</div></td></tr><tr><td>subtype</td><td>string</td><td>Cond.</td><td>برای route الزامی است: <div class="code-inline">aircraft | train | bus</div></td></tr><tr><td>branch</td><td>integer</td><td>Yes</td><td>شناسه شعبه ایجاد کننده</td></tr><tr><td>items</td><td>array</td><td>Yes</td><td>اطلاعات فیزیکی (شماره پرواز، ترمینال، هتل) و مسیر برگشت</td></tr><tr><td>repeat</td><td>object</td><td>No</td><td>تنظیمات موتور تولید تاریخ (اگر نرسد، فقط یک رکورد ثبت می‌شود)</td></tr><tr><td>calculations</td><td>array</td><td>Yes</td><td>آرایه‌ای از کلاس‌های نرخی، قیمت پایه، مالیات و قوانین مالی</td></tr><tr><td>rules</td><td>object</td><td>No</td><td>قوانین استرداد (Refund) و قوانین عمومی (Public Rules)</td></tr></tbody></table>

</div>## Logic 1: Route &amp; Object Resolution

سیستم ابتدا داده‌های ورودی را نرمال‌سازی می‌کند:

```
IF type == 'route':
   IF subtype == 'aircraft':
       Origin/Dest IDs -> Look up 'airports' table -> Fetch City Name
   ELSE (train/bus):
       Origin/Dest IDs -> Used directly as City Name

IF type == 'accommodation':
   Object -> Hotel ID -> Look up 'hotels' table -> Fetch City Name
   Origin = Destination = City Name
   Start/End Time -> Set strictly to 00:00:00 / 23:59:59
  
```

<div class="api-docs" id="bkmrk--1"></div>## Logic 2: Outbound Handling (Round Trip)

اگر در اولین آیتم آرایه `items`، کلید `outbound` وجود داشته باشد:

<div class="api-docs" id="bkmrk-%D9%85%D8%B3%DB%8C%D8%B1-%D8%A8%D8%B1%DA%AF%D8%B4%D8%AA-%D8%A8%D9%87-%D8%B9%D9%86%D9%88%D8%A7%D9%86-">- مسیر برگشت به عنوان یک موجودیت جداگانه اما وابسته (Linked) ساخته می‌شود.
- جای `origin` و `destination` تعویض (Swap) می‌شود.
- پارامتر `diff` محاسبه می‌شود: فاصله زمانی بین رفت و برگشت (برای استفاده در تولید تاریخ‌های بعدی).

</div>## Logic 3: The Replication Engine

متغیر کلیدی `$charterDates` بر اساس آبجکت `repeat` پر می‌شود. این بخش هوشمند سیستم است:

### Type: Dates (Manual)

```
{ "type": "dates", "dates": ["2024-01-01", "2024-01-05"] }
// دقیقاً برای همین تاریخ‌ها رکورد تولید می‌شود.
  
```

### Type: Weekly (Pattern)

```
{
  "type": "weekly",
  "days": [1, 3, 5], // 1=شنبه, 3=دوشنبه ...
  "from": "2024-01-01",
  "to": "2024-03-01"
}
// حلقه روی بازه زمانی می‌چرخد و روزهای هفته شمسی را چک می‌کند.
  
```

### Type: Periodic (Interval)

```
{
  "type": "periodic",
  "repeat_day": 2, // یک روز در میان
  "from": "...", "to": "..."
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Logic 4: Financial Calculations

داده‌های مالی در جداول جداگانه بر اساس `type` ذخیره می‌شوند. همچنین جداول واسط زیر پر می‌شوند:

<div class="api-docs" id="bkmrk-table-description-ch"><table class="schema-table"><thead><tr><th>Table</th><th>Description</th></tr></thead><tbody><tr><td>charter\_taxes</td><td>مالیات‌ها به تفکیک بزرگسال، کودک و نوزاد.</td></tr><tr><td>charter\_financial\_handling</td><td>مدیریت سه نوع داده: **Markup**, **Commission**, **Citizenship** rules.</td></tr><tr><td>charter\_accommodation\_rooms</td><td>(فقط هتل) نگاشت شماره اتاق‌ها و طبقات به کلاس نرخی.</td></tr><tr><td>mapping\_accommodations</td><td>(فقط هتل) اتصال شناسه هتل لوکال به شناسه چارتر تولید شده (Airplus ID).</td></tr></tbody></table>

</div>## Execution Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-reso"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item">Resolve Cities &amp; Objects</div><div class="flow-arrow">↓</div><div class="flow-item">**Replication Engine**  
<small>Generate List of Dates</small></div><div class="flow-arrow">↓</div><div class="flow-item">**Main Loop (Foreach Date)**</div><div class="flow-arrow">↓</div><div class="flow-item" style="border-style: dashed; border-color: #4caf50;">Begin Transaction</div><div class="flow-arrow">↓</div><div class="flow-item">Insert 'Charters' (Header)</div><div class="flow-arrow">↓</div><div class="flow-item">Insert 'Scheduled Notifications'</div><div class="flow-arrow">↓</div><div class="flow-item">Insert 'Charter Items'</div><div class="flow-arrow">↓</div><div class="flow-item">**Financial Loop**  
<small>Calc / Tax / Markup / Rooms</small></div><div class="flow-arrow">↓</div><div class="flow-item">Insert Rules (Refund/Public)</div><div class="flow-arrow">↓</div><div class="flow-item" style="border-style: dashed; border-color: #4caf50;">Commit Transaction</div><div class="flow-arrow">↓</div><div class="flow-item">Return Success Response</div></div></div>## Response Example

```
// Success
{
    "status": true,
    "time": 1715432100
}

// Error (Exception handled)
{
    "status": false,
    "time": 1715432105,
    "message": "SQL Error: Column 'x' not found...",
    "trace": [...]
}
  
```

# PUT /v2/charter

# Charter: Update Inventory (Operation)

این اندپوینت مسئول مدیریت تغییرات یک چارتر ثبت‌شده است. این متد یک **Action Dispatcher** داخلی دارد که بر اساس مقدار `action` درخواست را به یکی از سه رفتار مستقل هدایت می‌کند: `base`، `calculations` و `copy`. این عملیات‌ها اتمیک و تحت تراکنش انجام می‌شوند و سیستم از **Capacity Protection** برای جلوگیری از تناقض ظرفیت و فروش استفاده می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter-met"><div class="endpoint-info" dir="ltr"><div>**URL:** /v2/charter</div><div>**Method:** PUT</div><div>**Controller:** CharterController@operationCharter</div><div>**Middleware:** authWithJwt</div></div></div>## Key Features &amp; Behavior

<div class="api-docs" id="bkmrk-action-based-logic%3A-">- **Action-Based Logic:** سه مسیر عملیاتی مستقل (Base, Calculations, Copy).
- **Capacity Protection:** ظرفیت جدید نمی‌تواند کمتر از تعداد رزروهای قطعی/موقت باشد.
- **Transactional:** هر عملیات کامل در یک تراکنش انجام می‌شود.
- **Deep Cloning:** در حالت Copy تمام جداول وابسته با Serial جدید تکثیر می‌شوند.
- **Financial Sync:** مالیات، کمیسیون، قوانین استرداد و اتاق‌ها برای هر کلاس نرخی مدیریت می‌شود.

</div>## Payload Schema (Root Level)

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>action</td><td>string</td><td>Yes</td><td dir="rtl">مقدار ثابت: `base` یا `calculations` یا `copy`</td></tr><tr><td>id</td><td>int</td><td>Cond.</td><td dir="rtl">شناسه چارتر (برای base و copy الزامی است)</td></tr><tr><td>main\_id</td><td>int</td><td>Cond.</td><td dir="rtl">شناسه چارتر (در حالت calculations الزامی است)</td></tr><tr><td>items</td><td>array</td><td>Cond.</td><td dir="rtl">برای calculations مورد نیاز است</td></tr></tbody></table>

</div>## Logic 1: Base Operation

این حالت برای ویرایش اطلاعات عمومی چارتر استفاده می‌شود.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>status</td><td>bool</td><td>فعال/غیرفعال</td></tr><tr><td>capacity</td><td>int</td><td>ظرفیت کلی (چک ظرفیت اعمال می‌شود)</td></tr><tr><td>commission</td><td>float</td><td>کمیسیون پایه</td></tr><tr><td>get\_passenger</td><td>bool</td><td>نیاز به اطلاعات مسافر</td></tr><tr><td>notification</td><td>object</td><td>{ mobile, text, date, time }</td></tr></tbody></table>

</div>```
// Capacity Guard Example
$cap = CharterHelper::capacityItemCharter($id);
if ($new_capacity < $cap['reserved_total']) {
    throw new Exception("New capacity cannot be lower than sold seats.");
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Logic 2: Calculations Operation

مدیریت کلاس‌های نرخی، قیمت‌ها، ظرفیت اتاق‌ها، مالیات‌ها و قوانین کنسلی.

<div class="api-docs" id="bkmrk-create%3A-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%DA%A9%D9%84%D8%A7%D8%B3-%D8%AC">- `create`: ایجاد کلاس جدید
- `update`: ویرایش قیمت/ظرفیت (با چک فروش)
- `delete`: حذف کلاس (تنها اگر فروش نداشته باشد)

</div>```
{
  "action": "calculations",
  "main_id": 150,
  "items": [
    {
      "mode": "update",
      "id": 455,
      "price": 3200000,
      "capacity": 12,
      "taxes": [{ "amount": 10000, "description": "Tax" }],
      "rules": [{ "percent": 30, "from_time": 0, "to_time": 12 }]
    }
  ]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Logic 3: Copy Operation

عملیات کپی یک چارتر شامل:

<div class="api-docs" id="bkmrk-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-serial-%D8%AC%D8%AF%DB%8C%D8%AF-%DA%A9%D9%BE">- تولید Serial جدید
- کپی جدول اصلی Charter
- کپی Charter Items
- کپی Calculations + Taxes + Rules
- کپی Mapping ها و وابستگی‌ها

</div>```
{
  "action": "copy",
  "id": 150
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Execution Flowchart

<div class="api-docs" id="bkmrk-start-%28put%29-%E2%86%93-valida"><div class="flowchart" dir="rtl"><div class="flow-item">Start (PUT)</div><div class="flow-arrow">↓</div><div class="flow-item">Validate Action</div><div class="flow-arrow">↓</div><div class="flow-item">Switch(action)</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Base | Calc | Copy</div><div class="flow-arrow">↓</div><div class="flow-item">Commit Transaction</div><div class="flow-arrow">↓</div><div class="flow-item">Return Response</div></div></div>## Response Example

```
// Success
{
  "status": true,
  "data": { "id": 150, "status": true }
}

// Error
{
  "status": false,
  "message": "Capacity cannot be less than sold seats"
}
  
```

`

# PATCH /v2/charter (updateCharter)

# Charter: Update Charter (Status / Sell)

این اندپوینت جهت تغییر وضعیت کلی چارتر (Status) و یا مدیریت وضعیت فروش (Sell) برای همکاران و HUB طراحی شده است. این متد برخلاف ویرایش کامل، عملیات سبک‌تری انجام می‌دهد اما قوانین سخت‌گیرانه‌ای برای حذف (Delete) دارد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter-met"><div class="endpoint-info"><div>**URL:** `/v2/charter`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** CharterController@updateCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%D8%A7%D8%B1%D8%B3">- دسترسی معتبر JWT
- ارسال پارامتر `action` الزامی است.

</div>## Request Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>شناسه رکورد چارتر</td></tr><tr><td>action</td><td>string</td><td>yes</td><td>مقادیر مجاز: `status` یا `sell`</td></tr><tr><td>data</td><td>object</td><td>yes</td><td>حاوی فیلدهای مرتبط با اکشن انتخابی</td></tr></tbody></table>

</div>## Logic: Action "status"

برای تغییر وضعیت چارتر (فعال/غیرفعال/حذف) استفاده می‌شود.

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D9%BE%D8%A7%DB%8C%D8%A7%D9%86-%DB%8C%D8%A7">- **بررسی وضعیت پایان یافته:** اگر وضعیت فعلی چارتر `3` باشد، امکان ویرایش وجود ندارد (Error: چارتر مورد نظر در وضعیت پایان یافته می باشد).
- **بررسی حذف (Status 4):**
    - فراخوانی متد `capacityItemCharter`.
    - اگر `capacity.total` با `capacity.capacity` برابر نباشد (یعنی رزرو قطعی، موقت یا گارانتی وجود دارد):
    - جلوگیری از حذف → Error Code 1000.
- در غیر این صورت وضعیت آپدیت می‌شود.

<table class="schema-table" dir="rtl"><thead><tr><th>data.field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>status</td><td>integer</td><td>وضعیت جدید (مثلاً 4 برای حذف)</td></tr></tbody></table>

</div>## Logic: Action "sell"

برای کنترل سوئیچ‌های فروش استفاده می‌شود.

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-data.colleague-%D8%A7">- اگر `data.colleague` ارسال شود → فیلد `colleague_sell` آپدیت می‌شود.
- اگر `data.hub` ارسال شود → فیلد `hub_sell` آپدیت می‌شود.
- پس از موفقیت، کش مربوط به عنوان چارتر در Redis پاک می‌شود.

<table class="schema-table" dir="rtl"><thead><tr><th>data.field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>colleague</td><td>boolean</td><td>فعال/غیرفعال‌سازی فروش همکار</td></tr><tr><td>hub</td><td>boolean</td><td>فعال/غیرفعال‌سازی فروش HUB</td></tr></tbody></table>

</div>## Response (Success)

در صورت موفقیت، هیچ محتوایی برگردانده نمی‌شود (HTTP 204).

```
HTTP/1.1 204 No Content
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

```
// Error: Deletion failed due to existing reservations
{
  "error": {
    "code": 1000,
    "message": "بر روی این چارتر رزرو انجام شده است و امکان حذف وجود ندارد."
  }
}

// Error: Charter Not Found
{
  "error": {
    "code": 1000,
    "message": "چارتر مورد نظر یافت نشد."
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28patch-%2Fv2%2Fcha"><div class="flowchart"><div class="flow-item">Start (PATCH /v2/charter)</div><div class="flow-arrow">↓</div><div class="flow-item">Find Charter by ID</div><div class="flow-arrow">↓</div><div class="flow-item">Check Action Type</div><div class="flow-arrow">↓</div><div style="display: flex; justify-content: center; gap: 20px;"><div style="display: flex; flex-direction: column; align-items: center;"><div class="flow-item">Action: Status</div><div class="flow-arrow">↓</div><div class="flow-item">Is Status = 4 (Delete)?</div><div class="flow-arrow">↓</div><div class="flow-item">Check Capacity (Reservations)</div><div class="flow-arrow">↓</div><div class="flow-item">If Has Reserve → Error</div><div class="flow-arrow">↓</div><div class="flow-item">Update Status</div></div><div style="display: flex; flex-direction: column; align-items: center;"><div class="flow-item">Action: Sell</div><div class="flow-arrow">↓</div><div class="flow-item">Update colleague_sell / hub_sell</div><div class="flow-arrow">↓</div><div class="flow-item">Clear Redis Cache</div></div></div><div class="flow-arrow">↓</div><div class="flow-item">Return 204 No Content</div></div></div>

# DELETE /v2/charter

# Charter: Delete (Soft Deactivate)

این اندپوینت مسئول حذف نرم (Soft Delete) یا غیرفعال‌سازی یک چارتر است. توجه داشته باشید که این متد رکورد را از دیتابیس **پاک نمی‌کند**، بلکه وضعیت (Status) آن را به مقدار `2` تغییر می‌دهد (که معمولاً به معنای غیرفعال یا بایگانی است).

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter-met"><div class="endpoint-info"><div>**URL:** `/v2/charter`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CharterController@deleteCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>yes</td><td>شناسه چارتری که باید غیرفعال شود</td></tr></tbody></table>

</div>## Logic Details

<div class="api-docs" id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D9%85%D8%B3%D8%AA%D9%82%DB%8C%D9%85-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C">- **عملیات مستقیم دیتابیس:** این متد از Eloquent Model استفاده نمی‌کند و مستقیماً روی جدول `charters` کوئری آپدیت می‌زند.
- **تغییر وضعیت:** مقدار فیلد `status` برای رکورد مورد نظر به عدد `2` تغییر می‌یابد.
- **مدیریت خطا:** کلیه عملیات داخل بلوک `try-catch` قرار دارد. در صورت بروز خطا (مثلاً مشکل دیتابیس)، متن خطا و Trace برگردانده می‌شود.

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Exception Error)

```
{
  "status": false,
  "time": 1710000000,
  "message": "SQLSTATE[...]: Integrity constraint violation...",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Fv2%2Fch"><div class="flowchart"><div class="flow-item">Start (DELETE /v2/charter)</div><div class="flow-arrow">↓</div><div class="flow-item">Get Request ID</div><div class="flow-arrow">↓</div><div class="flow-item">DB Transaction: Update `charters` SET `status` = 2</div><div class="flow-arrow">↓</div><div style="display: flex; justify-content: center; gap: 20px;"><div style="display: flex; flex-direction: column; align-items: center;"><div class="flow-item" style="border-color: green;">Success</div><div class="flow-arrow">↓</div><div class="flow-item">Return { status: true }</div></div><div style="display: flex; flex-direction: column; align-items: center;"><div class="flow-item" style="border-color: red;">Exception</div><div class="flow-arrow">↓</div><div class="flow-item">Return { status: false, message... }</div></div></div></div></div>

# GET /v2/charter/list

# Charter: List &amp; Search

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Flis"><div class="endpoint-info"><div>**URL:** `/v2/charter/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%DA%A9%D8%A7%D8%B1">- دسترسی معتبر JWT
- کاربران با `branch_id = 1` به همه رکوردها دسترسی دارند؛ سایرین فقط رکوردهای شعبه خود را می‌بینند.

</div>## Request Parameters (Query String)

کلیه پارامترها به صورت Query Param ارسال می‌شوند.

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>serial</td><td>string</td><td>فیلتر دقیق بر اساس شماره سریال</td></tr><tr><td>type</td><td>string</td><td>نوع چارتر (مثلاً `accommodation`, `flight`)</td></tr><tr><td>action</td><td>string</td><td>اگر مقدار `list` ارسال شود، ترتیب نمایش آیتم‌ها در صفحه جاری معکوس می‌شود.</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>تعداد آیتم در هر صفحه (پیش‌فرض: 30)</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>آفست شروع (پیش‌فرض: 0)</td></tr><tr><td colspan="3" style="background-color: #f9f9f9; text-align: center;">**Advanced Filters (آرایه advanced)**</td></tr><tr><td>advanced\[id\]</td><td>integer</td><td>جستجو بر اساس ID (سیستم به صورت خودکار ۱۰,۰۰۰ واحد از این عدد کم می‌کند)</td></tr><tr><td>advanced\[from\]</td><td>date</td><td>تاریخ شروع بازه جستجو</td></tr><tr><td>advanced\[to\]</td><td>date</td><td>تاریخ پایان بازه جستجو</td></tr><tr><td>advanced\[origin\]</td><td>integer</td><td>شناسه مبدا</td></tr><tr><td>advanced\[destination\]</td><td>integer</td><td>شناسه مقصد</td></tr><tr><td>advanced\[roundtrip\]</td><td>boolean</td><td>حالت دوطرفه (اگر true باشد، مسیر برگشت هم جستجو می‌شود)</td></tr><tr><td>advanced\[route\]</td><td>string</td><td>فیلتر بر اساس `subtype`</td></tr><tr><td>advanced\[status\]</td><td>string</td><td>`active`: وضعیت‌های 1 و 3  
`all`: همه وضعیت‌ها (غیر از 1)</td></tr></tbody></table>

</div>## Complex Logic Details

### ۱. منطق تاریخ (Date Logic)

<div class="api-docs" id="bkmrk-%D8%AD%D8%A7%D9%84%D8%AA-%D8%B9%D8%A7%D8%AF%DB%8C-%28general%29%3A">- **حالت عادی (General):** جستجو می‌کند که `start` چارتر دقیقاً بین بازه `from` و `to` باشد.
- **حالت اقامتگاه (Accommodation):** اگر `type=accommodation` باشد، سیستم **تداخل زمانی (Overlap)** را بررسی می‌کند: 
    - تاریخ شروع چارتر در بازه باشد.
    - یا تاریخ پایان چارتر در بازه باشد.
    - یا چارتر کل بازه درخواستی را پوشش دهد (شروع چارتر &lt;= شروع درخواست و پایان چارتر &gt;= پایان درخواست).

</div>### ۲. جستجوی دوطرفه (Roundtrip)

اگر `advanced[roundtrip]` برابر true باشد و مبدا و مقصد مشخص شده باشند، کوئری به صورت `OR` اجرا می‌شود:   
(مبدا = Origin AND مقصد = Dest) **یا** (مبدا = Dest AND مقصد = Origin).

### ۳. صفحه‌بندی و مرتب‌سازی

<div class="api-docs" id="bkmrk-%D9%85%D8%B1%D8%AA%D8%A8%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%DB%8C%D8%B4%E2%80%8C%D9%81%D8%B1%D8%B6%3A-%D8%A8">- مرتب‌سازی پیش‌فرض: بر اساس تاریخ شروع (ASC) و سپس شناسه (ASC).
- اگر `action=list` باشد، آیتم‌های فچ شده در صفحه جاری، توسط کالکشن لاراول معکوس (Reverse) می‌شوند (نمایش از آخر به اول در همان صفحه).

</div>## Response (Success)

```
{
  "items": [
    {
      "id": 10050,
      "serial": "CHR-123",
      "type": "flight",
      "origin": "THR",
      "destination": "MHD",
      "start": "2024-05-20 10:00:00",
      "status": 1,
      // ... other resource fields
    }
  ],
  "meta": {
    "timestamp": 1715000000,
    "table": {
      "total": 150
    }
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fv2%2Fchart"><div class="flowchart"><div class="flow-item">Start (GET /v2/charter/list)</div><div class="flow-arrow">↓</div><div class="flow-item">Set Default Pagination (30 items)</div><div class="flow-arrow">↓</div><div class="flow-item">Apply Basic Filters (Serial, Type)</div><div class="flow-arrow">↓</div><div style="border: 1px dashed #ccc; padding: 10px; margin: 10px 0; border-radius: 8px;">**Advanced Filters Logic**<div style="display: flex; justify-content: space-around; align-items: flex-start; margin-top: 10px;"><div style="width: 45%; text-align: center;"><div class="flow-item" style="font-size: 0.8em;">Date Logic</div><div class="flow-arrow">↓</div><div style="font-size: 0.8em; color: #444;">If Accomm: Check Overlap  
Else: Check Start Date</div></div><div style="width: 45%; text-align: center;"><div class="flow-item" style="font-size: 0.8em;">Roundtrip Logic</div><div class="flow-arrow">↓</div><div style="font-size: 0.8em; color: #444;">If true: (A→B) OR (B→A)  
Else: (A→B)</div></div></div></div><div class="flow-arrow">↓</div><div class="flow-item">Check Branch (Filter if not ID 1)</div><div class="flow-arrow">↓</div><div class="flow-item">Execute Query &amp; Paginate</div><div class="flow-arrow">↓</div><div class="flow-item">Action = 'list'?</div><div class="flow-arrow">↓</div><div style="display: flex; justify-content: center; gap: 20px;"><div style="text-align: center;"><div class="flow-item">Yes</div><div class="flow-arrow">↓</div><div class="flow-item">Reverse Items Collection</div></div><div style="text-align: center;"><div class="flow-item">No</div><div class="flow-arrow">↓</div><div class="flow-item">Keep Order</div></div></div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON Resource</div></div></div>

# GET /v2/charter/communications

# Charter: List Communications

این اندپوینت لیست "ارتباطات" (Communications) تعریف شده بین چارترها را برمی‌گرداند. این جدول معمولاً برای تعریف مسیرهای متصل (Connecting Flights) یا ارتباط بین یک چارتر اصلی و چارترهای وابسته استفاده می‌شود. خروجی شامل دو بخش اصلی `src` (چارتر مبدا) و `dst` (چارتر مقصد/متصل) است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fcom"><div class="endpoint-info"><div>**URL:** `/v2/charter/communications`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listCommunicationsCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>origin</td><td>integer</td><td>شناسه شهر مبدا.  
<small>سیستم ابتدا چارترهایی که `origin` آنها برابر این مقدار است را پیدا کرده و سپس در جدول ارتباطات جستجو می‌کند.</small></td></tr><tr><td>destination</td><td>integer</td><td>شناسه شهر مقصد (واسط).  
<small>توجه: سیستم چارترهایی که **origin** (مبدا) آن‌ها برابر با این مقدار است را پیدا می‌کند (منطق پرواز کانکشن: پرواز دوم از اینجا شروع می‌شود).</small></td></tr><tr><td>main\_id</td><td>integer</td><td>شناسه مستقیم چارتر اصلی (Source Main ID)</td></tr><tr><td>item\_id</td><td>integer</td><td>شناسه آیتم/کلاس چارتر اصلی (Source Item ID)</td></tr><tr><td>destination\_main\_id</td><td>integer</td><td>شناسه چارتر مقصد (Destination Main ID)</td></tr><tr><td>destination\_item\_id</td><td>integer</td><td>شناسه آیتم/کلاس چارتر مقصد (Destination Item ID)</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>تعداد آیتم در صفحه</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>آفست شروع</td></tr></tbody></table>

</div>## Logic Details

### ۱. فیلتر هوشمند مبدا و مقصد

این متد مستقیماً روی جدول `charter_communications` جستجو نمی‌کند، بلکه ابتدا شناسه‌های معتبر را از جدول `charters` استخراج می‌کند:

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-origin-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-%D8%B4%D9%88%D8%AF">- **اگر `origin` ارسال شود:** لیست ID چارترهایی که از این شهر شروع می‌شوند را می‌گیرد و در فیلد `main_id` جستجو می‌کند.
- **اگر `destination` ارسال شود:** لیست ID چارترهایی که از این شهر شروع می‌شوند (به عنوان لگ دوم سفر) را می‌گیرد و در فیلد `communication_main_id` جستجو می‌کند.

</div>### ۲. غنی‌سازی داده‌ها (Data Enrichment)

پس از دریافت لیست خام ارتباطات، برای هر رکورد توابع زیر صدا زده می‌شوند تا جزئیات کامل برگردانده شود:

<div class="api-docs" id="bkmrk-getcommunicationscha">- `getCommunicationsCharter`: اطلاعات کلی پرواز/قطار (مبدا، مقصد، زمان).
- `getCommunicationsCalculation`: اطلاعات کلاس نرخی (Business/Economy) یا نوع قطار (4 تخته و ...).

</div>## Response Structure

```
{
  "items": {
    "communication": [
      {
        "id": 501,
        "title": "Tehran to Mashhad (Connection)",
        "src": {
          "main": {
             "id": 100,
             "type": "route",
             "subtype": "aircraft",
             "information": { "origin": 1, "destination": 2, ... },
             "calculations": [...]
          },
          "item": {
             "class": { "iata": "Y", "title_en": "Economy", ... }
          },
          "type": "system" // source_type
        },
        "dst": {
          "main": { ... }, // اطلاعات چارتر دوم
          "item": { ... }, // اطلاعات کلاس چارتر دوم
          "type": "charter" // destination_type
        }
      }
    ]
  },
  "meta": {
    "timestamp": 1715000000,
    "table": { "total": 10, ... }
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fcommunic"><div class="flowchart"><div class="flow-item">Start (GET /communications)</div><div class="flow-arrow">↓</div><div style="border: 1px dashed #ccc; padding: 10px; border-radius: 8px;">**ID Resolution**<div class="flow-item">If Origin set? → Find Charters starting at Origin → Get IDs</div><div class="flow-arrow">↓</div><div class="flow-item">If Dest set? → Find Charters starting at Dest → Get IDs</div></div><div class="flow-arrow">↓</div><div class="flow-item">Query `charter_communications` (Apply Filters)</div><div class="flow-arrow">↓</div><div class="flow-item">Paginate Results</div><div class="flow-arrow">↓</div><div class="flow-item">Loop through items (Map)</div><div class="flow-arrow">↓</div><div style="display: flex; justify-content: space-around; gap: 10px;"><div style="width: 45%; text-align: center;"><div class="flow-item" style="font-size: 0.8em;">Fetch SRC Charter Details</div><div class="flow-arrow">↓</div><div class="flow-item" style="font-size: 0.8em;">Get Class/Train Info</div></div><div style="width: 45%; text-align: center;"><div class="flow-item" style="font-size: 0.8em;">Fetch DST Charter Details</div><div class="flow-arrow">↓</div><div class="flow-item" style="font-size: 0.8em;">Get Class/Train Info</div></div></div><div class="flow-arrow">↓</div><div class="flow-item">Format JSON Response</div></div></div>

# GET /v2/charter/reservation/{type}

# Charter: List Reservations &amp; Reports

این اندپوینت لیست رزروها را بر اساس `type` (نوع گزارش) فیلتر می‌کند. این متد قلب تپنده گزارش‌گیری سیستم چارتر است و حالت‌های مختلفی از جمله رزروهای قطعی، موقت (لاگین شده)، استردادی و نمای گرافیکی "Plan" (مخصوص هتل) را پوشش می‌دهد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/{type}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Parameters

### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string (enum)</td><td>نوع لیست درخواستی. مقادیر مجاز: - `definite`: رزروهای قطعی و فعال
- `temporary`: رزروهای موقت (در حال پرداخت/قفل شده)
- `refund`: رزروهای استرداد شده
- `warranty`: رکوردهای گارانتی
- `deleted`: رزروهای حذف شده (Soft Delete)
- `plan`: نمای جامع تقویم (مخصوص اقامتگاه)

</td></tr></tbody></table>

</div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه چارتر اصلی (Main Charter ID)</td></tr><tr><td>search\[from\]</td><td>date (Y-m-d)</td><td>تاریخ شروع بازه گزارش</td></tr><tr><td>search\[to\]</td><td>date (Y-m-d)</td><td>تاریخ پایان بازه گزارش</td></tr><tr><td>search\[report\_type\]</td><td>string</td><td>**فقط برای type=definite:**  
نحوه فیلتر تاریخ: - `check_in`: بر اساس تاریخ ورود
- `check_out`: بر اساس تاریخ خروج
- `guests`: مسافرین حاضر در هتل (Guest in House)
- `null`: پیش‌فرض (ورود یا خروج در بازه باشد)

</td></tr></tbody></table>

</div>## Logic Details

### ۱. منطق فیلتر Definite (رزروهای قطعی)

در این حالت، سیستم رزروهایی که `refund_id` ندارند و حذف نشده‌اند را برمی‌گرداند. منطق تاریخ بر اساس `report_type` متغیر است:

<div class="api-docs" id="bkmrk-check_in%3A-checkin_da">- **check\_in:** `checkin_date BETWEEN from AND to`
- **check\_out:** `checkout_date BETWEEN from AND to`
- **guests:** رزروهایی که بازه اقامتشان با بازه انتخابی همپوشانی دارد (مهمانان مقیم).  
    `checkin <= to AND checkout > from`

</div>### ۲. منطق Temporary (رزروهای موقت)

این بخش رکوردهایی را از جدول `charter_temporary_reservation` می‌خواند که هنوز منقضی نشده‌اند.   
شرط انقضا: `created_at + duration > NOW()`

### ۳. منطق Plan (نمای تقویم اقامتگاه)

این حالت پیچیده‌ترین بخش است و داده‌ها را از ۴ منبع تجمیع می‌کند. اگر نوع چارتر `accommodation` نباشد، خطای **409** برمی‌گرداند.

<div class="api-docs" id="bkmrk-reservations%3A-%D8%B1%D8%B2%D8%B1%D9%88%D9%87%D8%A7">- **Reservations:** رزروهای قطعی در بازه زمانی.
- **Warranties:** گارانتی‌های تعریف شده.
- **Locks:** قفل‌های موقت (Temporary Reservations) فعال.
- **Disable Rooms:** اتاق‌های غیرفعال شده (خرابی و ...).

</div>

# POST /v2/charter/reservation

# Charter: Insert Bulk Reservations

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@insertListCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه چارتر اصلی</td></tr><tr><td>item\_id</td><td>integer</td><td>**(الزامی)** شناسه آیتم محاسباتی (کلاس پروازی یا نوع اتاق)</td></tr><tr><td>checkin\_date</td><td>string (Y-m-d)</td><td>**(فقط برای اقامتگاه)** تاریخ ورود</td></tr><tr><td>checkout\_date</td><td>string (Y-m-d)</td><td>**(فقط برای اقامتگاه)** تاریخ خروج</td></tr><tr><td>passengers</td><td>array\[object\]</td><td>**(الزامی)** لیست مسافرانی که باید برایشان رزرو ثبت شود.</td></tr></tbody></table>

</div>### ساختار آبجکت Passenger

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>identity.id</td><td>string</td><td>کد ملی برای مسافر ایرانی</td></tr><tr><td>identity.nationality</td><td>string</td><td>کد ۲ حرفی ISO کشور (مثلاً 'IR')</td></tr><tr><td>passport.id</td><td>string</td><td>شماره پاسپورت برای مسافر خارجی</td></tr><tr><td>fullname.first\_name.en</td><td>string</td><td>نام (انگلیسی)</td></tr><tr><td>fullname.last\_name.en</td><td>string</td><td>نام خانوادگی (انگلیسی)</td></tr><tr><td>birth</td><td>string</td><td>تاریخ تولد (Y-m-d)</td></tr><tr><td>mobile / email</td><td>string</td><td>اطلاعات تماس (اختیاری)</td></tr><tr><td>status</td><td>string</td><td>(اختیاری) اگر مقدار آن `refund` باشد، رزرو مستقیماً به عنوان استرداد ۱۰۰٪ ثبت می‌شود.</td></tr><tr><td>financial</td><td>object</td><td>**(اختیاری)** برای ثبت قیمت به صورت دستی. اگر ارسال نشود، قیمت به صورت خودکار محاسبه می‌شود.</td></tr><tr><td colspan="3">- `financial.supplier`: نام تأمین‌کننده
- `financial.sale_price`: مبلغ قابل پرداخت
- `financial.commission`: مبلغ کمیسیون

</td></tr></tbody></table>

</div>## Logic Details

### ۱. اعتبارسنجی گسترده

پیش از هر اقدامی، سیستم تمام مسافران موجود در آرایه `passengers` را بررسی می‌کند. در صورت وجود اولین خطا، عملیات متوقف و کد خطای `422 Unprocessable Entity` بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-%D9%85%D9%84%DB%8C%D8%AA%3A-%D8%A8%D8%A7%DB%8C%D8%AF-%DB%8C%DA%A9-%DA%A9%D8%AF-%D9%85%D8%B9%D8%AA">- **ملیت:** باید یک کد معتبر دو حرفی باشد (کدهای ۳ حرفی به ۲ حرفی تبدیل می‌شوند). (خطای 1019)
- **کد ملی / پاسپورت:** برای ایرانیان الگوریتم کد ملی و برای خارجی‌ها فرمت پاسپورت بررسی می‌شود. (خطای 1011)
- **اطلاعات تماس:** فرمت شماره موبایل و ایمیل بررسی می‌شود. (خطای 1013, 1014)
- **تاریخ تولد:** باید با فرمت `Y-m-d` باشد. (خطای 1021)

</div>### ۲. بررسی ظرفیت

پس از اعتبارسنجی، سیستم تعداد مسافران بزرگسال و کودک را شمارش کرده و با ظرفیت باقی‌مانده آیتم مقایسه می‌کند.   
اگر ظرفیت کافی نباشد، عملیات متوقف و کد خطای `409 Conflict` به همراه پیام "ظرفیت تکمیل است" (کد 1008) بازگردانده می‌شود.

### ۳. تفاوت منطق Route و Accommodation

هسته اصلی این متد بر اساس نوع چارتر عمل می‌کند:

<div class="api-docs" id="bkmrk-%D9%86%D9%88%D8%B9-route-%28%D9%BE%D8%B1%D9%88%D8%A7%D8%B2%D8%8C-%D9%82%D8%B7">- **نوع Route (پرواز، قطار و...):**   
    \- برای هر مسافر در آرایه یک رزرو **جداگانه** ایجاد می‌شود.   
    \- قبل از ایجاد، بررسی می‌شود که آیا مسافر قبلاً برای همین چارتر رزرو فعال دارد یا خیر (جلوگیری از ثبت تکراری). در صورت تکراری بودن، یک آیتم خطا در خروجی نهایی قرار می‌گیرد.
- **نوع Accommodation (هتل):**   
    \- تمام مسافران در آرایه به عنوان مهمانان **یک رزرو واحد** (یک اتاق) در نظر گرفته می‌شوند.   
    \- ابتدا بررسی می‌شود که آیا برای بازه `checkin_date` تا `checkout_date` اتاق خالی وجود دارد یا خیر. در غیر این صورت، خطای `409 Conflict` (کد 1026) بازگردانده می‌شود. - پس از ایجاد رزرو، سیستم به صورت خودکار یک اتاق در دسترس را برای تمام شب‌های اقامت به این رزرو اختصاص می‌دهد.

</div>### ۴. محاسبه مالی و ثبت

برای هر رزرو، اگر آبجکت `financial` در اطلاعات مسافر ارسال شده باشد، قیمت‌ها به صورت دستی ثبت می‌شوند. در غیر این صورت، سیستم با استفاده از `financialCalculation` قیمت نهایی را بر اساس قوانین قیمت‌گذاری (پله‌ای، کارمزد، مارکاپ) محاسبه می‌کند. در نهایت، برای هر رزرو موفق، یک PNR محلی و یک شناسه یکتا تولید و در دیتابیس ذخیره می‌شود.

<div class="api-docs" id="bkmrk--1"></div>## Response Structure

### پاسخ موفق

در صورت موفقیت، کد `201 Created` به همراه آرایه‌ای از نتایج بازگردانده می‌شود. این آرایه می‌تواند شامل آیتم‌های موفق و ناموفق (مثلاً مسافر تکراری) باشد.

```
{
  "items": [
    {
      "status": true,
      "pnr": {
        "local": "XG7H5A2B", // PNR مشترک برای این بچ
        "original": "K9L4M1N0", // Slug یکتای این رزرو
        "id": 10845        // شناسه رزرو + 10000
      },
      // این بخش فقط برای رزرو اقامتگاه وجود دارد
      "accommodation_rooms": [
        {
          "reservation_id": 10845,
          "room_id": 10021,
          "date": "2025-12-25",
          "number": "205"
        }
      ]
    },
    {
      "status": false,
      "item_id": 45,
      "details": "0012345678",
      "code": 1010,
      "message": "برای این مسافر قبلا این آیتم خریداری شده است.",
      "solution": "..."
    }
  ],
  "meta": { "timestamp": 1715000000 }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Freserva"><div class="flowchart"><div class="flow-item">Start (POST /reservation)</div><div class="flow-arrow">↓</div><div class="flow-item" style="background-color: #ffe8e8;">**Validation Loop (Passengers):**  
<small>Nationality → ID/Passport → Contact → Birth Date</small></div><div class="flow-arrow">Fail → Halt &amp; Return 422</div><div class="flow-arrow">Pass ↓</div><div class="flow-item" style="background-color: #fff4e0;">**Capacity Check:**  
<small>Required (ADT+CHD) vs. Available Balance</small></div><div class="flow-arrow">Fail → Halt &amp; Return 409</div><div class="flow-arrow">Pass ↓</div><div class="flow-item">Check Charter Type</div><div style="display: flex; justify-content: space-around; gap: 10px; margin-top: 10px;"><div style="width: 48%; border: 1px dashed #007bff; padding: 10px; border-radius: 8px;">**Type: Route**<div class="flow-item">Loop Each Passenger</div><div class="flow-arrow">↓</div><div class="flow-item">Check for Duplicates</div><div class="flow-arrow">↓</div><div class="flow-item">Create Individual Reservation</div><div class="flow-arrow">↓</div><div class="flow-item">Add to `insertQuery` Array</div></div><div style="width: 48%; border: 1px dashed #28a745; padding: 10px; border-radius: 8px;">**Type: Accommodation**<div class="flow-item" style="background-color: #fff4e0;">Check Room Availability</div><div class="flow-arrow">Fail → Halt &amp; Return 409</div><div class="flow-arrow">Pass ↓</div><div class="flow-item">Create One Reservation for All Guests</div><div class="flow-arrow">↓</div><div class="flow-item">Add to `insertQuery` Array</div></div></div><div class="flow-arrow">↓</div><div class="flow-item" style="background-color: #e2f0d9;">**Final Insertion Loop:**  
<small>Insert DB Record → Handle Refund Status → For Accommodation, Assign Room per Night</small></div><div class="flow-arrow">↓</div><div class="flow-item">Return 201 with Result Array</div></div></div>

# PUT /v2/charter/reservation

# Charter: Update Reservation(s)

این اندپوینت دو قابلیت مجزا اما مرتبط را فراهم می‌کند. بسته به پارامتر `apply_all`، می‌توان یک رزرو خاص را با تمام جزئیاتش به‌روزرسانی کرد، یا فقط اطلاعات مالی (مبلغ) را برای تمام رزروهای فعال یک چارتر به صورت یکجا تغییر داد. این متد برای اصلاح اطلاعات مسافر یا اعمال تغییرات قیمت کلی بسیار کاربردی است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** CharterController@updateCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

ساختار بدنه درخواست بسته به مقدار فیلد `apply_all` متفاوت است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th><th>Mode</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه چارتر اصلی</td><td>هر دو</td></tr><tr><td>apply\_all</td><td>boolean</td><td>اگر `true` باشد، حالت به‌روزرسانی گروهی فعال می‌شود. در غیر این صورت (یا عدم وجود)، حالت تکی فعال است.</td><td>هر دو</td></tr><tr><td>reserve\_id</td><td>integer</td><td>**(الزامی در حالت تکی)** شناسه رزرواسیون مورد نظر برای ویرایش.</td><td>تکی</td></tr><tr><td>passenger</td><td>object</td><td>**(الزامی در حالت تکی)** آبجکت کامل و به‌روز شده اطلاعات مسافر.</td><td>تکی</td></tr><tr><td>financial</td><td>object</td><td>**(الزامی)** شامل کلید `payable` برای به‌روزرسانی مبلغ.</td><td>هر دو</td></tr><tr><td>mobile</td><td>string</td><td>شماره موبایل جدید (اختیاری).</td><td>تکی</td></tr><tr><td>email</td><td>string</td><td>ایمیل جدید (اختیاری).</td><td>تکی</td></tr><tr><td>guarantor</td><td>string</td><td>شناسه ضامن (در صورت وجود). این فیلد اطلاعات `warranty` را تنظیم می‌کند.</td><td>تکی</td></tr></tbody></table>

</div>## Logic Details

این متد بر اساس فیلد `apply_all` به دو شاخه منطقی اصلی تقسیم می‌شود:

### ۱. حالت به‌روزرسانی تکی (`apply_all: false`)

این حالت پیش‌فرض است و برای ویرایش جزئیات یک رزرو مشخص به کار می‌رود.

<div class="api-docs" id="bkmrk-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D8%B1%D8%B2%D8%B1%D9%88%3A-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B3%DB%8C">- **یافتن رزرو:** ابتدا سیستم با استفاده از `main_id` و `reserve_id` رزرو مورد نظر را در جدول مربوطه جستجو می‌کند. اگر یافت نشود، خطای `422` با پیام "آیتم یافت نشد" بازگردانده می‌شود.
- **بررسی وضعیت:** یک شرط حیاتی وجود دارد: رزرو فقط در صورتی قابل ویرایش است که وضعیت (`status`) آن `1` (خرید قطعی) یا `3` (استفاده شده) باشد. در غیر این صورت، خطای `422` با پیام "آیتم در وضعیت خرید قطعی نمی باشد" بازگردانده می‌شود.
- **آماده‌سازی داده‌ها:**
    - آبجکت `passenger` به صورت JSON در فیلد مربوطه ذخیره می‌شود.
    - مبلغ نهایی از `financial.payable` استخراج و در فیلد `amount` ذخیره می‌شود.
    - فیلدهای `mobile` و `email` به‌روزرسانی می‌شوند.
    - اگر فیلد `guarantor` ارسال شده باشد، فیلد `warranty_type` به 'colleague' و فیلد `warranty` با مقدار `guarantor` پر می‌شود.
- **عملیات پایگاه داده:** اطلاعات جدید در رکورد مربوطه در پایگاه داده جایگزین می‌شود.

</div>### ۲. حالت به‌روزرسانی گروهی (`apply_all: true`)

این حالت برای تغییر یکپارچه قیمت تمام رزروهای یک چارتر استفاده می‌شود.

<div class="api-docs" id="bkmrk-%D9%87%D8%AF%D9%81%E2%80%8C%DA%AF%DB%8C%D8%B1%DB%8C-%D8%B1%D8%B2%D8%B1%D9%88%D9%87%D8%A7%3A-%D8%B3%DB%8C%D8%B3">- **هدف‌گیری رزروها:** سیستم تمام رکوردهایی را که `main_id` آن‌ها با مقدار ارسالی برابر است و **استرداد نشده‌اند** (`refund_id IS NULL`) انتخاب می‌کند. این شرط تضمین می‌کند که رزروهای کنسل شده تحت تأثیر قرار نگیرند.
- **آماده‌سازی داده‌ها:** تنها فیلدهایی که برای به‌روزرسانی آماده می‌شوند عبارتند از: 
    - `amount` که از `financial.payable` گرفته می‌شود.
    - `updated_at` که با زمان فعلی تنظیم می‌شود.
- **عملیات پایگاه داده:** یک دستور `UPDATE` گروهی بر روی تمام رکوردهای انتخاب شده اجرا می‌شود و مبلغ آن‌ها را تغییر می‌دهد.

</div>## Response Structure

### پاسخ موفق

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

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-422-unprocessable">- **کد `422 Unprocessable Entity`:** در صورتی که رزرو یافت نشود یا وضعیت آن برای ویرایش مناسب نباشد.
- **کد `400 Bad Request`:** در صورت بروز هرگونه خطای پیش‌بینی نشده در سرور. این پاسخ شامل جزئیات خطا (`trace`) برای اشکال‌زدایی است.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28put-%2Freservat"><div class="flowchart"><div class="flow-item">Start (PUT /reservation)</div><div class="flow-arrow">↓</div><div class="flow-decision">apply_all == true?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item" style="background-color: #e3f2fd;">**Mode: Single Update**  
<small>Find reservation by `reserve\_id`</small></div><div class="flow-arrow">↓</div><div class="flow-decision">Reservation Found?</div><div class="flow-arrow">No → Return 422 (Not Found)</div><div class="flow-arrow">Yes ↓</div><div class="flow-decision" style="background-color: #fff9c4;">Status == 1 OR 3?</div><div class="flow-arrow">No → Return 422 (Invalid Status)</div><div class="flow-arrow">Yes ↓</div><div class="flow-item">Prepare data:  
<small>passenger, amount, mobile, email, warranty...</small></div><div class="flow-arrow">↓</div><div class="flow-item" style="background-color: #e8f5e9;">DB: UPDATE single record</div></div><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item" style="background-color: #e3f2fd;">**Mode: Bulk Update**  
<small>Prepare data: `amount`, `updated\_at`</small></div><div class="flow-arrow">↓</div><div class="flow-item" style="background-color: #e8f5e9;">DB: UPDATE all records  
<small>WHERE main\_id = ? AND refund\_id IS NULL</small></div></div></div><div class="flow-join">  
</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div></div></div>

# DELETE /v2/charter/reservation

# Charter: Soft Delete Reservation(s)

این اندپوینت برای حذف نرم (soft delete) یک یا چند رزرو به صورت همزمان طراحی شده است. عملیات حذف به صورت فیزیکی رکوردها را از پایگاه داده پاک نمی‌کند، بلکه وضعیت (`status`) آن‌ها را به `2` (حذف شده) تغییر داده و فیلد `deleted_at` را با زمان فعلی پر می‌کند. این متد همچنین رزرو اتاق‌های مرتبط با چارترهای اقامتی را نیز مدیریت می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CharterController@deleteCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>**(الزامی)** نوع چارتر (مثلاً 'route' یا 'accommodation'). این فیلد برای تعیین جدول صحیح رزروها در پایگاه داده استفاده می‌شود.</td></tr><tr><td>reserves\_id</td><td>array\[integer\]</td><td>**(الزامی)** آرایه‌ای از شناسه‌های رزروهایی که باید حذف شوند.</td></tr></tbody></table>

</div>## Logic Details

فرآیند حذف در دو مرحله کلیدی انجام می‌شود تا از هماهنگی داده‌ها اطمینان حاصل شود:

### ۱. به‌روزرسانی جدول اصلی رزروها

ابتدا، سیستم با استفاده از پارامتر `type`، نام جدول مربوط به رزروها (مثلاً `charter_reservation_route`) را از طریق متد `getTableCharter` به دست می‌آورد. سپس یک دستور `UPDATE` گروهی اجرا می‌کند که تمام رکوردهای موجود در آرایه `reserves_id` را پیدا کرده و فیلدهای زیر را در آن‌ها به‌روزرسانی می‌کند:

<div class="api-docs" id="bkmrk-status-%D8%A8%D9%87-%D9%85%D9%82%D8%AF%D8%A7%D8%B1-2-%D8%AA%D8%BA">- `status` به مقدار **2** تغییر می‌کند.
- `deleted_at` با تاریخ و زمان فعلی سرور پر می‌شود.

</div>### ۲. به‌روزرسانی جدول اتاق‌های اقامتی

بلافاصله پس از مرحله اول، یک دستور `UPDATE` دیگر بر روی جدول `charter_reservation_accommodation_rooms` اجرا می‌شود. این عملیات نیز تمام رکوردهایی را که `reservation_id` آن‌ها در آرایه `reserves_id` وجود دارد، پیدا کرده و آن‌ها را نیز به صورت نرم حذف می‌کند (`status=2` و `deleted_at=now()`).

**نکته مهم:** این مرحله دوم همیشه اجرا می‌شود، حتی اگر نوع چارتر `route` باشد. در این حالت، چون هیچ رکوردی در جدول اتاق‌ها با شناسه‌های رزرو پرواز مطابقت ندارد، این دستور تأثیری نخواهد داشت، اما وجود آن برای مدیریت صحیح رزروهای اقامتی ضروری است.

<div class="api-docs" id="bkmrk--1"></div>## Response Structure

### پاسخ موفق

اگر هر دو عملیات به‌روزرسانی با موفقیت انجام شوند، سرور یک پاسخ خالی با کد وضعیت `204 No Content` باز می‌گرداند. این نشان می‌دهد که درخواست با موفقیت پردازش شده و نیازی به بازگرداندن محتوا نیست.

### پاسخ خطا

در صورت بروز هرگونه استثنا (Exception) در حین اجرای عملیات پایگاه داده، سرور با کد `400 Bad Request` پاسخ می‌دهد. بدنه پاسخ شامل یک آبجکت خطا با جزئیاتی مانند کد خطا، پیام و ردپای آن (trace) برای کمک به فرآیند اشکال‌زدایی خواهد بود.

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Freser"><div class="flowchart"><div class="flow-item">Start (DELETE /reservation)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body:  
<small>`type` and `reserves_id` (array)</small></div><div class="flow-arrow">↓</div><div class="flow-item" style="background-color: #f3e5f5;">Get Reservation Table Name using `type`</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Step 1: Update Main Reservation Table**  
<small> `UPDATE charter_reservation_[type]`  
`SET status = 2, deleted_at = NOW()`  
`WHERE id IN (reserves_id)` </small></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Step 2: Update Accommodation Rooms Table**  
<small> `UPDATE charter_reservation_accommodation_rooms`  
`SET status = 2, deleted_at = NOW()`  
`WHERE reservation_id IN (reserves_id)` </small></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div><div class="flow-error-path"><div class="flow-arrow-error">→ On Any Exception</div><div class="flow-item-error">Return 400 Bad Request with Error Details</div></div></div></div>

# PATCH /v2/charter/reservation/undo

# Charter: Undo Reservation Deletion

این اندپوینت برای بازگردانی یک رزرو که قبلاً به صورت نرم (soft delete) حذف شده است، استفاده می‌شود. عملیات اصلی، تغییر وضعیت (`status`) رزرو از `2` (حذف شده) به `1` (قطعی) و پاک کردن مقدار فیلد `deleted_at` است. نکته بسیار مهم در این فرآیند، بررسی مجدد ظرفیت خالی قبل از بازگردانی است که دارای منطق متفاوتی برای مسافران نوزاد و غیرنوزاد می‌باشد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/undo`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** CharterController@undoDeleteCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه چارتر اصلی. این شناسه برای تشخیص نوع چارتر (مثلاً 'route' یا 'accommodation') و پیدا کردن جدول صحیح رزروها به کار می‌رود.</td></tr><tr><td>reserves\_id</td><td>integer</td><td>**(الزامی)** شناسه رزرو مشخصی که باید بازگردانی شود. (توجه: با وجود نام جمع، این فیلد فقط یک شناسه را می‌پذیرد).</td></tr></tbody></table>

</div>## Logic Details

فرآیند بازگردانی با یک بررسی ظرفیت حیاتی همراه است تا از فروش بیش از حد جلوگیری شود.

### ۱. یافتن رزرو و بررسی اولیه

ابتدا سیستم با استفاده از `main_id` و `reserves_id`، رکورد رزرو حذف شده را از جدول مربوطه پیدا می‌کند. اگر رکوردی یافت نشود، یک خطای عمومی رخ داده و پاسخ `400` بازگردانده می‌شود.

### ۲. بررسی ظرفیت (Capacity Check)

این مهم‌ترین بخش منطق است. سیستم متد `ReservationController::capacityItemCharter` را فراخوانی می‌کند تا ظرفیت باقی‌مانده واقعی را محاسبه کند. این متد با کسر کردن رزروهای قطعی، گارانتی و قفل‌های موقت از ظرفیت کل، عدد نهایی را به دست می‌آورد. سپس، یک شرط بر اساس نوع مسافر اعمال می‌شود:

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%B3%D8%A7%D9%81%D8%B1%D8%A7%D9%86-%D8%A8%D8%B2%D8%B1%DA%AF%D8%B3%D8%A7%D9%84">- **برای مسافران بزرگسال و کودک (Non-Infant):**   
    سیستم بررسی می‌کند که آیا ظرفیت باقی‌مانده `>= 0` است. از آنجایی که ظرفیت نمی‌تواند منفی باشد، این شرط **همیشه درست است**. این یعنی بازگردانی رزرو بزرگسال/کودک به بررسی ظرفیت نیازی ندارد و همیشه موفقیت‌آمیز خواهد بود. فرض بر این است که ظرفیت این مسافر قبلاً اشغال شده بوده و اکنون صرفاً به حالت فعال بازمی‌گردد.
- **برای مسافران نوزاد (Infant):**   
    سیستم بررسی می‌کند که آیا ظرفیت باقی‌مانده `>= 1` است. این بدان معناست که برای بازگرداندن یک رزرو نوزاد، باید حداقل یک جای خالی وجود داشته باشد. این منطق نشان می‌دهد که نوزادان ممکن است ظرفیت متفاوتی (مثلاً ظرفیت صندلی پرواز) را اشغال کنند که باید مجدداً بررسی شود.

</div>### ۳. عملیات پایگاه داده

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%85%D9%88%D9%81%D9%82%DB%8C%D8%AA%E2%80%8C%D8%A2%D9%85%DB%8C%D8%B2-">- **در صورت موفقیت‌آمیز بودن بررسی ظرفیت:** سیستم یک دستور `UPDATE` اجرا کرده و مقادیر زیر را برای رزرو مورد نظر تنظیم می‌کند: 
    - `status` به **1** (قطعی)
    - `updated_at` به زمان فعلی
    - `deleted_at` به **NULL**
- **در صورت شکست در بررسی ظرفیت (فقط برای نوزاد):** عملیات متوقف شده و یک خطای مشخص به کاربر بازگردانده می‌شود.

</div><div class="api-docs" id="bkmrk--1"></div>## Response Structure

### پاسخ موفق

اگر بازگردانی با موفقیت انجام شود، سرور یک پاسخ خالی با کد وضعیت `204 No Content` ارسال می‌کند.

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-400-bad-request-%D8%A8">- **کد `400 Bad Request` با `code: 1008`:** این خطا فقط زمانی رخ می‌دهد که یک رزرو نوزاد در حال بازگردانی باشد اما ظرفیت آیتم تکمیل شده باشد.   
    متن خطا: "ظرفیت این آیتم تکمیل شده است"
- **کد `400 Bad Request` (عمومی):** در صورت بروز هر خطای پیش‌بینی نشده دیگر، مانند یافت نشدن رزرو، یک پاسخ خطای عمومی شامل جزئیات استثنا (trace) برای اشکال‌زدایی بازگردانده می‌شود.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28patch-%2Freserv"><div class="flowchart"><div class="flow-item">Start (PATCH /reservation/undo)</div><div class="flow-arrow">↓</div><div class="flow-item">Find reservation using `main_id` &amp; `reserves_id`.</div><div class="flow-arrow">↓</div><div class="flow-decision">Reservation Found?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">Check remaining capacity via `capacityItemCharter`</div><div class="flow-arrow">↓</div><div class="flow-decision">Is passenger an infant?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ No (Adult/Child)</div><div class="flow-item" style="background-color: #f1f8e9;">Proceed to update (Capacity check is bypassed)</div></div><div class="flow-path"><div class="flow-arrow">↓ Yes (Infant)</div><div class="flow-decision" style="background-color: #fff9c4;">Capacity &gt;= 1?</div><div class="flow-arrow">No → Return 400 (Code 1008)</div><div class="flow-arrow">Yes ↓</div></div></div><div class="flow-join">  
</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**DB UPDATE:**  
<small> SET status = 1  
SET updated\_at = NOW()  
SET deleted\_at = NULL </small></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Exception Occurs  
(e.g., Cannot read property of null)</div><div class="flow-arrow">↓</div><div class="flow-item-error">Return 400 Bad Request  
with trace</div></div></div></div></div>

# PUT /v2/charter/reservation/transfer

# Charter: Transfer Reservations

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/transfer`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** CharterController@transferCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>goal</td><td>object</td><td>**(الزامی)** آبجکتی که اطلاعات چارتر و آیتم مقصد را مشخص می‌کند.</td></tr><tr><td>goal.main\_id</td><td>integer</td><td>شناسه چارتر مقصد.</td></tr><tr><td>goal.item\_id</td><td>integer</td><td>شناسه آیتم (مانند اتاق یا پرواز) در چارتر مقصد.</td></tr><tr><td>items</td><td>array\[integer\]</td><td>**(الزامی)** آرایه‌ای شامل شناسه‌های رزروهایی که باید به مقصد جدید منتقل شوند.</td></tr></tbody></table>

</div>## Logic Details

منطق اصلی این اندپوینت بر پایه یک بررسی ظرفیت و سپس اجرای یک سری به‌روزرسانی‌های متوالی استوار است.

### ۱. بررسی ظرفیت مقصد

اولین و مهم‌ترین گام، فراخوانی متد `ReservationController::capacityItemCharter` برای چارتر و آیتم مقصد (`goal.main_id` و `goal.item_id`) است. این متد ظرفیت کل را محاسبه کرده و تعداد رزروهای قطعی، گارانتی و قفل‌های موقت را از آن کم می‌کند تا ظرفیت باقی‌مانده واقعی (`balance`) را به دست آورد.

### ۲. تصمیم‌گیری بر اساس ظرفیت

سپس، سیستم ظرفیت باقی‌مانده (`$capacity['balance']`) را با تعداد رزروهایی که قرار است منتقل شوند (`count($request->items)`) مقایسه می‌کند.

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%B8%D8%B1%D9%81%DB%8C%D8%AA-%DA%A9%D8%A7%D9%81%DB%8C-%D8%A8%D8%A7%D8%B4%D8%AF-">- **اگر ظرفیت کافی باشد (`balance >= count(items)`):**   
    عملیات انتقال ادامه می‌یابد. سیستم برای هر شناسه رزرو در آرایه `items`، یک دستور `UPDATE` مجزا در پایگاه داده اجرا می‌کند. در هر دستور، فیلدهای زیر برای رکورد رزرو مربوطه به‌روزرسانی می‌شوند: 
    - `main_id` به شناسه چارتر مقصد تغییر می‌کند.
    - `item_id` به شناسه آیتم مقصد تغییر می‌کند.
    - `updated_at` با زمان فعلی سرور تنظیم می‌شود.
    
    **نکته:** به‌روزرسانی‌ها به صورت تکی در یک حلقه (loop) انجام می‌شوند، نه به صورت یک دستور گروهی (bulk update).
- **اگر ظرفیت کافی نباشد:**   
    عملیات متوقف شده و یک خطای `422 Unprocessable Entity` به کاربر بازگردانده می‌شود تا اعلام کند که مقصد فضای کافی برای پذیرش این تعداد رزرو را ندارد.

</div>## Response Structure

### پاسخ موفق

در صورتی که ظرفیت کافی باشد و تمام عملیات‌های به‌روزرسانی با موفقیت انجام شوند، سرور یک پاسخ خالی با کد وضعیت `204 No Content` باز می‌گرداند.

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-422-unprocessable">- **کد `422 Unprocessable Entity`:** این خطا زمانی رخ می‌دهد که ظرفیت باقی‌مانده در چارتر مقصد کمتر از تعداد رزروهایی باشد که برای انتقال درخواست شده‌اند.   
    \- `code`: 1000   
    \- `message`: "ظرفیت چارتر مورد نظر تکمیل می باشد."
- **کد `400 Bad Request`:** در صورت بروز هرگونه استثنا (Exception) در حین اجرای منطق (مانند خطای پایگاه داده)، این پاسخ به همراه جزئیات کامل خطا (trace) برای اشکال‌زدایی بازگردانده می‌شود.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28put-%2Freservat"><div class="flowchart"><div class="flow-item">Start (PUT /reservation/transfer)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body:  
<small>`goal` (main\_id, item\_id) and `items` (array of IDs)</small></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Calculate available capacity (`balance`) for destination item (`goal.item_id`)</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is `balance` &gt;= `count(items)`?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Loop through each `item` ID:**  
<small> `UPDATE reservation_table`  
`SET main_id = goal.main_id,`  
`item_id = goal.item_id`  
`WHERE id = item_id` </small></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return 422 Unprocessable Entity  
<small>Message: "ظرفیت چارتر مورد نظر تکمیل می باشد."</small></div></div></div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Any General Exception</div><div class="flow-item-error">Return 400 Bad Request with Error Trace</div></div></div></div>

# PATCH /character/reservation/refund

# Charter: Process Reservation Refund

این اندپوینت برای پردازش بازپرداخت (Refund) برای یک یا چند رزرو قطعی (`status=1`) طراحی شده است. فرآیند شامل محاسبه جریمه (به صورت درصدی یا مبلغ ثابت)، به‌روزرسانی اطلاعات مالی رزرو، ثبت رکورد بازپرداخت در جدول مجزا، و در نهایت تغییر وضعیت رزرو به "مسترد شده" (`status=3`) است. این عملیات به صورت دسته‌ای (batch) برای لیستی از رزروها قابل اجراست.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/refund`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** CharterController@storeRefundCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>**(الزامی)** نوع چارتر را مشخص می‌کند تا جداول پایگاه داده مربوطه هدف قرار گیرند. مقادیر مجاز: `"accommodation"` یا `"route"`.</td></tr><tr><td>items</td><td>array\[integer\]</td><td>**(الزامی)** آرایه‌ای از شناسه‌های رزروهایی که باید مسترد شوند.</td></tr><tr><td>financial</td><td>object</td><td>**(الزامی)** آبجکتی شامل جزئیات جریمه کنسلی.</td></tr><tr><td>financial.value\_type</td><td>string</td><td>**(الزامی)** نوع محاسبه جریمه. مقادیر مجاز: `"percent"` (درصدی از مبلغ قابل پرداخت) یا `"currency"` (مبلغ ثابت).</td></tr><tr><td>financial.value</td><td>number</td><td>**(الزامی)** مقدار جریمه. اگر `value_type` برابر `percent` باشد، این عدد بین 0 تا 100 است.</td></tr><tr><td>financial.description</td><td>string</td><td>**(الزامی)** توضیحی برای دلیل استرداد و جریمه.</td></tr></tbody></table>

</div>## Logic Details

منطق این اندپوینت در دو فاز اصلی اجرا می‌شود: فاز اعتبارسنجی و آماده‌سازی، و فاز اجرای تغییرات در پایگاه داده.

### فاز ۱: اعتبارسنجی و محاسبه جریمه (در یک حلقه)

سیستم در یک حلقه، هر شناسه رزرو ارسال شده در آرایه `items` را پردازش می‌کند:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%B1%D8%B2%D8%B1%D9%88%3A">1. **دریافت اطلاعات رزرو:** ابتدا رزرو مربوط به `item` فعلی از پایگاه داده خوانده می‌شود. 
    - **اگر رزرو یافت نشود:** عملیات فوراً متوقف شده و خطای `422` با کد `1000` و پیام "آیتم \[ID+10000\] یافت نشد" بازگردانده می‌شود.
2. **بررسی وضعیت رزرو:** وضعیت (`status`) رزرو بررسی می‌شود. 
    - **اگر وضعیت برابر `1` (قطعی) نباشد:** عملیات متوقف شده و خطای `422` با کد `1000` و پیام "آیتم \[ID+10000\] در وضعیت خرید قطعی نمی باشد" بازگردانده می‌شود.
3. **محاسبه جریمه (Penalty):**
    - اطلاعات مالی فعلی رزرو از فیلد `financial` (که به صورت JSON ذخیره شده) استخراج می‌شود.
    - بر اساس `request->financial['value_type']`: 
        - اگر `percent` باشد: `penalty = (original_payable * value) / 100`
        - اگر `currency` باشد: `penalty = value`
4. **آماده‌سازی داده‌های جدید:** یک آبجکت اطلاعاتی جدید برای این رزرو ساخته شده و به صورت موقت در یک آرایه به نام `$queryInsert` نگهداری می‌شود. این آبجکت شامل اطلاعاتی است که باید در جدول `charter_refunds` درج شود، از جمله آبجکت مالی جدید که در آن مبلغ قابل پرداخت (`payable`) به صورت `original_payable - penalty` محاسبه شده است.

</div>**نکته مهم:** اعتبارسنجی به صورت متوالی انجام می‌شود. در صورت بروز خطا برای هر یک از آیتم‌ها، کل فرآیند متوقف شده و هیچ تغییری در پایگاه داده اعمال نمی‌گردد.

### فاز ۲: اجرای تغییرات در پایگاه داده

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

<div class="api-docs" id="bkmrk-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%B1%D9%88%DB%8C-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%24que">1. سیستم روی آرایه `$queryInsert` (که در فاز اول آماده شده) حلقه می‌زند.
2. **درج رکورد استرداد:** برای هر آیتم، یک رکورد جدید در جدول `charter_refunds` با اطلاعات محاسبه شده درج می‌شود و شناسه (ID) رکورد جدید دریافت می‌گردد (`$refundId`).
3. **به‌روزرسانی رزرو اصلی:** رزرو اصلی در جدول `charter_reservations` به‌روزرسانی می‌شود: 
    - فیلد `status` به `3` (مسترد شده) تغییر می‌کند.
    - فیلد `refund_id` با شناسه رکورد استرداد (`$refundId`) پر می‌شود.
4. **عملیات ویژه برای اقامتگاه:** اگر نوع چارتر `accommodation` باشد، یک عملیات اضافی انجام می‌شود: 
    - رکورد(های) مرتبط با این رزرو در جدول `charter_reservation_accommodation_rooms` نیز حذف نرم (soft delete) می‌شوند (`status` به `2` تغییر کرده و `deleted_at` تنظیم می‌شود). این کار باعث آزاد شدن اتاق‌های اختصاص داده شده به این رزرو می‌شود.

</div>## Response Structure

### پاسخ موفق

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

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-422-unprocessable">- **کد `422 Unprocessable Entity`:** این خطا در یکی از دو حالت زیر در فاز اعتبارسنجی رخ می‌دهد:   
    \- `code`: 1000, `message`: "آیتم \[ID\] یافت نشد." (زمانی که شناسه رزرو نامعتبر است)   
    \- `code`: 1000, `message`: "آیتم \[ID\] در وضعیت خرید قطعی نمی باشد." (زمانی که رزرو قبلاً کنسل، حذف یا مسترد شده است)
- **پاسخ استثنا (Exception):** در صورت بروز هرگونه خطای پیش‌بینی نشده در حین اجرای منطق (مانند خطای پایگاه داده)، یک پاسخ با ساختار سفارشی بازگردانده می‌شود که برای اشکال‌زدایی مفید است. (معمولاً با کد وضعیت 500 یا 400) ```
    {
        "status": false,
        "time": 1670154000,
        "message": "Error message details...",
        "trace": [...]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28patch-%2Freserv"><div class="flowchart"><div class="flow-item">Start (PATCH /reservation/refund)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body:  
<small>`type`, `items`, `financial`</small></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Start Loop:** For each `id` in `items`</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Reservation with `id` exists?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-decision" style="background-color: #fff9c4;">Reservation `status` == 1?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e8f5e9;">1. Calculate penalty  
2. Prepare new financial data  
3. Add to temporary `$queryInsert` array</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return 422: Invalid Status</div></div></div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return 422: Not Found</div></div></div><div class="flow-arrow" style="clear: both;">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**End Loop**</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is `$queryInsert` array not empty?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Start DB Update Loop:**  
For each item in `$queryInsert`</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">1. Insert into `charter_refunds` table  
2. Update `charter_reservations`: `status=3`, `refund_id=new_id`</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is `type` == 'accommodation'?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e8f5e9;">Soft-delete from `..._rooms` table</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div></div></div><div class="flow-arrow" style="clear: both;">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**End DB Update Loop**</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div></div></div><div class="flow-arrow" style="clear: both;">↓</div><div class="flow-item-success">Return 204 No Content</div></div></div>

# PATCH /v2/charter/reservation/refund/undo

# Charter: Undo Reservation Refund

این اندپوینت برای لغو عملیات استرداد یک رزرو خاص و بازگرداندن آن به وضعیت "قطعی" (`status = 1`) استفاده می‌شود. قبل از بازگردانی، سیستم ظرفیت آیتم مربوطه (پرواز یا اتاق) را بررسی می‌کند. یک منطق خاص برای نوزادان (infant) وجود دارد که بازگردانی رزرو آن‌ها تنها در صورت وجود حداقل یک ظرفیت خالی امکان‌پذیر است، در حالی که برای بزرگسالان و کودکان این بررسی ظرفیت اعمال نمی‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/refund/undo`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** CharterController@undoRefundCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه چارتر اصلی. این فیلد برای تعیین نوع چارتر (route یا accommodation) و انتخاب جدول صحیح از پایگاه داده استفاده می‌شود.</td></tr><tr><td>reserve\_id</td><td>integer</td><td>**(الزامی)** شناسه رزروی که عملیات استرداد آن باید لغو شود.</td></tr></tbody></table>

</div>## Logic Details

فرآیند لغو استرداد شامل سه گام اصلی است: دریافت اطلاعات، بررسی ظرفیت، و اجرای به‌روزرسانی‌ها.

### ۱. دریافت اطلاعات رزرو

با استفاده از `main_id` و `reserve_id` ارسالی، سیستم ابتدا رکورد کامل رزرو مورد نظر را از جدول مربوطه (`charter_route_reservations` یا `charter_accommodation_reservations`) استخراج می‌کند. اگر رزرو یافت نشود، یک استثنا (Exception) رخ داده و عملیات متوقف می‌شود.

### ۲. بررسی ظرفیت (Capacity Check)

این مهم‌ترین بخش منطق است.

<div class="api-docs" id="bkmrk-%D9%85%D8%AA%D8%AF-reservationcontr">- متد `ReservationController::capacityItemCharter` فراخوانی می‌شود تا ظرفیت باقی‌مانده آیتمی که رزرو در اصل به آن تعلق داشته، محاسبه گردد.
- **منطق شرطی بر اساس سن مسافر:**
    - **اگر مسافر بزرگسال یا کودک باشد (`passenger_age_title != 'infant'`):** سیستم بررسی ظرفیت را نادیده می‌گیرد و فرض می‌کند که بازگرداندن رزرو همیشه امکان‌پذیر است. در واقع، این مسافران فضایی را اشغال نکرده بودند که اکنون نیاز به بازپس‌گیری آن باشد.
    - **اگر مسافر نوزاد باشد (`passenger_age_title == 'infant'`):** سیستم بررسی می‌کند که آیا ظرفیت باقی‌مانده آیتم (`$capacity['total']`) حداقل `1` است یا خیر. این بدان معناست که برای بازگرداندن یک رزرو نوزاد، باید حتماً یک جای خالی وجود داشته باشد.

</div>### ۳. اجرای عملیات

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D9%88%D8%AC%D9%88%D8%AF-%D8%B8%D8%B1%D9%81%DB%8C%D8%AA-%DA%A9">- **در صورت وجود ظرفیت کافی (یا عدم نیاز به بررسی):**
    1. **به‌روزرسانی رزرو اصلی:** در جدول رزروها، رکورد مربوط به `reserve_id` به‌روزرسانی می‌شود: 
        - `status` به `1` (قطعی) بازمی‌گردد.
        - `refund_id` به `NULL` تغییر می‌کند تا ارتباط با رکورد استرداد قطع شود.
    2. **به‌روزرسانی رکورد استرداد:** در جدول `charter_refunds`، رکوردی که قبلاً برای این رزرو ثبت شده بود، وضعیتش به `2` تغییر می‌کند. این کار به معنای "باطل شدن" یا "لغو شدن" آن رکورد استرداد است.
    
    **نکته:** رکورد استرداد حذف نمی‌شود، بلکه وضعیت آن تغییر می‌کند تا سوابق عملیات حفظ شود.
- **در صورت عدم وجود ظرفیت کافی (فقط برای نوزادان):**
    - عملیات متوقف شده و یک خطای `400 Bad Request` با کد `1008` بازگردانده می‌شود که نشان‌دهنده تکمیل بودن ظرفیت است.

</div>## Response Structure

### پاسخ موفق

در صورتی که عملیات با موفقیت انجام شود، سرور یک پاسخ خالی با کد وضعیت `204 No Content` باز می‌گرداند.

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-400-bad-request%3A-">- **کد `400 Bad Request`:** این خطا در یکی از دو حالت زیر رخ می‌دهد:   
    \- \*\*کمبود ظرفیت برای نوزاد:\*\* 
    - `code`: 1008
    - `message`: "ظرفیت آیتم مورد نظر تکمیل شده است." (یا پیام مشابه از تابع `staticGetErrorDetails`)
    
      
    \- \*\*خطای عمومی یا استثنا (Exception):\*\* 
    - اگر `reserve_id` نامعتبر باشد یا هر خطای دیگری در پایگاه داده رخ دهد.
    - ساختار پاسخ شامل جزئیات کامل خطا برای اشکال‌زدایی خواهد بود.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28patch-%2Freserv"><div class="flowchart"><div class="flow-item">Start (PATCH /reservation/refund/undo)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body:  
<small>`main_id`, `reserve_id`</small></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Fetch the full reservation record using `reserve_id`</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is capacity check required?  
<small>(Only if `passenger\_age\_title` is 'infant')</small></div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes (Infant)</div><div class="flow-decision" style="background-color: #fff9c4;">Is `capacity['total']` &gt;= 1?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e8f5e9;">1. Update reservation: `status=1`, `refund_id=NULL`  
2. Update refund record: `status=2`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return 400 - Code 1008 (Capacity Full)</div></div></div></div><div class="flow-path"><div class="flow-arrow">↓ No (Adult/Child)</div><div class="flow-item-process" style="background-color: #e8f5e9;">1. Update reservation: `status=1`, `refund_id=NULL`  
2. Update refund record: `status=2`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 204 No Content</div></div></div><div class="flow-error-path" style="margin-top: 20px; clear: both;"><div class="flow-arrow-error">→ On Any General Exception</div><div class="flow-item-error">Return 400 Bad Request with Error Trace</div></div></div></div>

# DELETE /v2/charter/reservation/temporary

# Charter: Soft Delete Temporary Reservation

این اندپوینت برای حذف نرم (soft-delete) یک رزرو موقت از سیستم استفاده می‌شود. رزروهای موقت در جدول جداگانه‌ای به نام `charter_temporary_reservation` نگهداری می‌شوند. این عملیات رکورد را به طور کامل از پایگاه داده حذف نمی‌کند، بلکه فقط ستون `status` آن را به مقدار `2` (به معنای لغو شده یا حذف شده) تغییر می‌دهد تا در پردازش‌های بعدی نادیده گرفته شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/temporary`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CharterController@deleteTemporaryCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Parameters

شناسه رزرو موقت باید به عنوان یک پارامتر کوئری (Query Parameter) در URL ارسال شود.

<div class="api-docs" id="bkmrk-field-type-location-"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>Query</td><td>**(الزامی)** شناسه یکتای رزرو موقت در جدول `charter_temporary_reservation` که قصد حذف نرم آن را دارید.</td></tr></tbody></table>

</div>#### Example URL

<div class="api-docs" id="bkmrk-%2Fv2%2Fcharter%2Freservat">`/v2/charter/reservation/temporary?id=451` </div>## Logic Details

فرآیند این اندپوینت بسیار ساده و سرراست است و در یک بلوک `try...catch` برای مدیریت خطاهای احتمالی پایگاه داده محصور شده است.

### ۱. اجرای عملیات حذف نرم (Soft Delete)

<div class="api-docs" id="bkmrk-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-">- سیستم با استفاده از کوئری‌بیلدر لاراول، یک دستور `UPDATE` بر روی جدول `charter_temporary_reservation` اجرا می‌کند.
- این دستور، رکوردی را که مقدار ستون `id` آن با `id` ارسال شده در درخواست برابر است، پیدا می‌کند.
- سپس مقدار ستون `status` آن رکورد را به `2` به‌روزرسانی می‌کند.
- این عملیات به صورت اتمیک انجام می‌شود. اگر رکوردی با شناسه مورد نظر یافت نشود، هیچ خطایی رخ نمی‌دهد و عملیات بدون هیچ تغییری در پایگاه داده به پایان می‌رسد و پاسخ موفقیت‌آمیز بازگردانده می‌شود.

</div>### ۲. مدیریت خطا

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%AF%D8%B1-%D8%AD%DB%8C%D9%86-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%DA%A9%D9%88%D8%A6">- اگر در حین اجرای کوئری `UPDATE` هرگونه خطای پایگاه داده (مانند قطع ارتباط، خطای سینتکس و...) رخ دهد، اجرای کد به بلوک `catch` منتقل می‌شود.
- در این حالت، یک پاسخ خطا شامل پیام دقیق استثنا (Exception) و ردپای آن (Trace) برای اهداف اشکال‌زدایی (Debugging) بازگردانده می‌شود.

</div>## Response Structure

برخلاف بسیاری از اندپوینت‌های RESTful که در پاسخ به متد `DELETE` موفق، کد وضعیت `204 No Content` را برمی‌گردانند، این اندپوینت همیشه یک بدنه پاسخ (JSON) با کد وضعیت `200 OK` باز می‌گرداند. وضعیت موفقیت یا شکست عملیات از طریق فیلد `status` در بدنه JSON مشخص می‌شود.

### پاسخ موفق

در صورتی که کوئری `UPDATE` بدون خطا اجرا شود (حتی اگر هیچ رکوردی برای به‌روزرسانی پیدا نشود)، پاسخ زیر بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1733284200
    }
    ```

</div>### پاسخ خطا

در صورت بروز هرگونه استثنا (Exception) در سطح پایگاه داده، پاسخ زیر بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok--1">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": false,
      "time": 1733284205,
      "message": "SQLSTATE[...]: Base table or view not found: ... (or any other DB error)",
      "trace": [
        // ... Stack trace for debugging ...
      ]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Freser"><div class="flowchart"><div class="flow-item">Start (DELETE /reservation/temporary)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request  
<small>Query Parameter: `id`</small></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Execute DB Query:  
`UPDATE charter_temporary_reservationSET status = 2WHERE id = ?`</div><div class="flow-decision" style="background-color: #fff9c4;">Did a DB Exception Occur?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-success">Return 200 OK  
<small>`{"status": true, "time": ...}`</small></div></div><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-error">Return 200 OK  
<small>`{"status": false, "message": ..., "trace": ...}`</small></div></div></div><div class="flow-item" style="clear: both; margin-top: 20px;">End</div></div></div>

# POST /v2/charter/reservation/temporary

# Charter: Create Temporary Reservation (Lock)

این اندپوینت برای ایجاد یک "رزرو موقت" یا "قفل" روی ظرفیت یک آیتم چارتر (مانند صندلی پرواز یا اتاق هتل) برای یک مدت زمان مشخص (به دقیقه) طراحی شده است. هدف اصلی آن جلوگیری از فروش همزمان یک ظرفیت توسط چند کاربر است. منطق این اندپوینت بر اساس نوع چارتر (`type`) به دو شاخه اصلی تقسیم می‌شود: اقامتگاهی (`accommodation`) و غیر اقامتگاهی (مانند مسیر `route`).

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/temporary`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@storeTemporaryCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

ساختار بدنه درخواست شامل اطلاعات عمومی قفل و دو شیء کلیدی `requester` و `capacity` است که ساختار `capacity` بسته به نوع چارتر متفاوت است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه چارتر اصلی (از جدول `charters`). برای تشخیص نوع چارتر استفاده می‌شود.</td></tr><tr><td>item\_id</td><td>integer</td><td>**(الزامی)** شناسه آیتم خاص چارتر (مثلاً شناسه یک پرواز یا یک نوع اتاق).</td></tr><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه رزرو کننده. این مقدار از درخواست دریافت می‌شود.</td></tr><tr><td>operator</td><td>object</td><td>**(تزریق شده)** این شیء توسط میدلور `authWithJwt` به درخواست اضافه می‌شود. شناسه اپراتور (`operator.id`) برای ثبت رزرو کننده استفاده می‌شود.</td></tr><tr><td>requester</td><td>object</td><td>**(الزامی)** شیء حاوی اطلاعات درخواست کننده نهایی.  
ساختار: `{ "type": string, "id": integer }`</td></tr><tr><td>duration</td><td>integer</td><td>**(الزامی)** مدت زمان اعتبار قفل به **دقیقه**. پس از این زمان، قفل به صورت خودکار منقضی می‌شود.</td></tr><tr><td>description</td><td>string</td><td>(اختیاری) توضیحات مربوط به این رزرو موقت.</td></tr><tr><td>checkin\_date</td><td>string</td><td>(شرطی) تاریخ ورود با فرمت `YYYY-MM-DD`. **فقط برای چارترهای اقامتگاهی الزامی است.**</td></tr><tr><td>checkout\_date</td><td>string</td><td>(شرطی) تاریخ خروج با فرمت `YYYY-MM-DD`. **فقط برای چارترهای اقامتگاهی الزامی است.**</td></tr><tr><td>capacity</td><td>object</td><td>**(الزامی)** شیء حاوی تعداد ظرفیت درخواستی. ساختار آن بسته به نوع چارتر تغییر می‌کند.</td></tr></tbody></table>

</div>#### ساختار شیء `capacity`

\- **برای چارترهای غیر اقامتگاهی (`route`, ...):**

```json
{
  "capacity": {
    "adult": 2,
    "child": 1,
    "infant": 0
  }
}
```

\- **برای چارترهای اقامتگاهی (`accommodation`):**

```json
{
  "capacity": {
    "room": 3
  }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

پس از دریافت درخواست، سیستم ابتدا نوع چارتر را با کوئری زدن به جدول `charters` از روی `main_id` تشخیص می‌دهد. سپس منطق بر اساس نوع چارتر اجرا می‌شود.

### ۱. منطق برای چارتر اقامتگاهی (Accommodation)

این حالت پیچیده‌تر است و نیازمند بررسی دقیق ظرفیت اتاق‌ها در بازه زمانی مشخص است.

<div class="api-docs" id="bkmrk-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-%D8%AA%D8%A7%D8%A8%D8%B9-%DA%A9%D9%85%DA%A9%DB%8C%3A-">- **فراخوانی تابع کمکی:** متد `ReservationController::getAccommodationRooms` با پارامترهای `item_id`، `checkin_date` و `checkout_date` فراخوانی می‌شود.
- **عملکرد `getAccommodationRooms`:**
    1. تمام اتاق‌های فیزیکی مرتبط با آیتم (`calc\_id`) را از جدول `charter_accommodation_rooms` استخراج می‌کند.
    2. برای هر اتاق، در تمام طول بازه زمانی درخواستی (از `checkin` تا یک روز قبل از `checkout`) بررسی می‌کند که آیا اتاق در آن تاریخ خاص آزاد است یا خیر.
    3. یک اتاق در یک تاریخ خاص "اشغال" محسوب می‌شود اگر: 
        - در یک رزرو قطعی (`charter\_reservation\_accommodation\_rooms`) ثبت شده باشد.
        - تحت یک گارانتی فعال (`charter\_warranties`) باشد.
        - در یک رزرو موقت دیگر (قفل) که هنوز منقضی نشده (`charter\_temporary\_reservation`) قرار داشته باشد.
    4. در نهایت، لیستی از اتاق‌هایی را برمی‌گرداند که در **تمام روزهای بازه درخواستی** آزاد هستند (`available\_all\_dates`).
    
    **نکته مهم:** کد کنترلر فعلی فقط از خروجی `available_all_dates` استفاده می‌کند و قابلیت‌های پیچیده‌تر تابع (مانند ترکیب اتاق‌های مختلف برای یک اقامت) را به کار نمی‌گیرد.
- **بررسی ظرفیت:** سیستم تعداد اتاق‌های کاملاً آزاد (`count($charterRooms\['available\_all\_dates'\])`) را با تعداد اتاق‌های درخواستی (`$request-&gt;capacity\['room'\]`) مقایسه می‌کند.
- **در صورت وجود ظرفیت کافی:**
    - به تعداد اتاق‌های درخواستی، یک حلقه تکرار می‌شود.
    - در هر تکرار، یک رکورد رزرو موقت مجزا برای یکی از اتاق‌های آزاد ایجاد می‌شود. یعنی اگر ۳ اتاق درخواست شود، ۳ رکورد مجزا در جدول `charter_temporary_reservation` درج خواهد شد که هر کدام به یک `room\_id` متفاوت اشاره دارند.
    - این رکوردها به صورت دسته‌ای (Bulk Insert) در پایگاه داده ذخیره می‌شوند.
- **در صورت عدم وجود ظرفیت کافی:** عملیات متوقف شده و یک پاسخ خطا با پیام "اتاق خالی به تعداد درخواست شده در بازه زمانی مورد نظر وجود ندارد." بازگردانده می‌شود.

</div>### ۲. منطق برای چارتر غیر اقامتگاهی (Route)

این حالت بسیار ساده‌تر است و مبتنی بر اعتماد به درخواست‌دهنده است.

<div class="api-docs" id="bkmrk-%D8%B9%D8%AF%D9%85-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B8%D8%B1%D9%81%DB%8C%D8%AA%3A-%D8%AF%D8%B1-">- **عدم بررسی ظرفیت:** در این حالت، API هیچ‌گونه بررسی ظرفیتی انجام نمی‌دهد و فرض می‌کند که ظرفیت لازم قبلاً در سمت کلاینت (UI) بررسی شده است.
- مقادیر `adult`، `child` و `infant` از شیء `capacity` به داده‌های آماده برای درج اضافه می‌شوند.
- یک رکورد **واحد** در جدول `charter_temporary_reservation` با استفاده از `insertGetId` درج می‌شود.
- شناسه رکورد درج شده (`$id`) با عدد `20,000` جمع شده و در پاسخ بازگردانده می‌شود. این یک قانون تجاری برای متمایز کردن شناسه‌های رزرو موقت است.

</div>## Response Structure

مشابه اندپوینت قبلی، این API نیز در همه موارد (موفقیت یا شکست) کد وضعیت `200 OK` را به همراه یک بدنه JSON برمی‌گرداند.

### پاسخ موفق (چارتر اقامتگاهی)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1733285100
    }
    ```

</div>### پاسخ موفق (چارتر غیر اقامتگاهی)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok--1">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1733285105,
      "data": 20123
    }
    ```
    
    **توجه:** مقدار `data` همان شناسه رکورد درج شده در پایگاه داده به علاوه `20,000` است.

</div>### پاسخ خطا (ظرفیت ناکافی در چارتر اقامتگاهی)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok--2">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": false,
      "time": 1733285110,
      "message": "اتاق خالی به تعداد درخواست شده در بازه زمانی مورد نظر وجود ندارد."
    }
    ```

</div>### پاسخ خطا (استثنای عمومی)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok--3">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": false,
      "time": 1733285115,
      "message": "An error occurred ...",
      "trace": [ ... ]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Freserva"><div class="flowchart"><div class="flow-item">Start (POST /reservation/temporary)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Fetch charter `type` from DB using `main_id`</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is `type` == 'accommodation'?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">Call `getAccommodationRooms` to find fully available rooms</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Available rooms &gt;= Requested rooms?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e8f5e9;">Create &amp; Bulk Insert one record per requested room</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success (no data)</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return Error (Capacity Message)</div></div></div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #e8f5e9;">1. Add seat counts to data  
2. Insert single record &amp; Get ID</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success (with `data = ID + 20000`)</div></div></div></div></div>

# POST /v2/charter/reservation/plan/update

# Charter: Update Accommodation Reservation Room Plan

این اندپوینت برای تغییر چینش اتاق‌های تخصیص داده شده به یک رزرو اقامتگاهی موجود استفاده می‌شود. کاربرد اصلی آن این است که به اپراتور اجازه می‌دهد اتاق فیزیکی (`room\_id`) مربوط به یک یا چند تاریخ (`date`) خاص از یک رزرو (`reservation\_id`) را تغییر دهد. برای مثال، اگر یک رزرو برای سه شب در اتاق ۱۰۱ ثبت شده، می‌توان با استفاده از این اندپوینت، شب دوم اقامت را به اتاق ۱۰۵ منتقل کرد.   
**نکته مهم تجاری:** شناسه‌های ورودی (`id` و `room_id`) شناسه‌های عمومی هستند و سیستم به صورت داخلی عدد `10,000` را از آن‌ها کم می‌کند تا به شناسه‌های واقعی در پایگاه داده دسترسی پیدا کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fres"><div class="endpoint-info"><div>**URL:** `/v2/charter/reservation/plan/update`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@updatePlanCharterReservation</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body (JSON)

بدنه درخواست باید شامل یک فیلد `data` باشد که آرایه‌ای از اشیاء است. هر شیء در این آرایه نشان‌دهنده یک تغییر در تخصیص اتاق برای یک تاریخ مشخص است.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>data</td><td>array of objects</td><td>**(الزامی)** آرایه‌ای از دستورات به‌روزرسانی. هر شیء در این آرایه باید شامل فیلدهای زیر باشد.</td></tr></tbody></table>

</div>#### ساختار شیء در آرایه `data`

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه عمومی رزرو. سیستم به صورت خودکار مقدار `10,000` را از این عدد کم می‌کند تا `reservation_id` واقعی را به دست آورد.</td></tr><tr><td>date</td><td>string</td><td>**(الزامی)** تاریخ مشخصی که تخصیص اتاق برای آن باید تغییر کند. فرمت باید `YYYY-MM-DD` باشد.</td></tr><tr><td>room\_id</td><td>integer</td><td>**(الزامی)** شناسه عمومی **اتاق جدید**. سیستم به صورت خودکار مقدار `10,000` را از این عدد کم می‌کند تا شناسه واقعی اتاق در جدول `charter_accommodation_rooms` را به دست آورد.</td></tr></tbody></table>

</div>#### Example Request Body

در مثال زیر، برای رزرو با شناسه عمومی `10123`، اتاق تخصیص داده شده برای تاریخ `2025-10-20` به اتاقی با شناسه عمومی `10055` تغییر می‌کند. همچنین برای همین رزرو، اتاق تاریخ `2025-10-21` به اتاق `10056` منتقل می‌شود.

```json
{
  "data": [
    {
      "id": 10123,
      "date": "2025-10-20",
      "room_id": 10055
    },
    {
      "id": 10123,
      "date": "2025-10-21",
      "room_id": 10056
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

فرآیند در یک بلوک `try...catch` برای مدیریت خطاها اجرا می‌شود.

### ۱. اعتبارسنجی ورودی

<div class="api-docs" id="bkmrk-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%DB%8C">- سیستم ابتدا بررسی می‌کند که آیا فیلد `data` در درخواست وجود دارد و خالی نیست (`isset($request->data) && $request->data`).
- اگر `data` وجود نداشته باشد یا یک آرایه خالی باشد، یک خطای `400 Bad Request` با پیام مشخص بازگردانده می‌شود.

</div>### ۲. پردازش حلقه به‌روزرسانی

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%85%D9%88%D9%81%D9%82%DB%8C">- اگر اعتبارسنجی موفقیت‌آمیز باشد، سیستم بر روی هر شیء (`$data`) در آرایه `request->data` یک حلقه اجرا می‌کند.
- در هر تکرار، یک دستور `UPDATE` در پایگاه داده بر روی جدول `charter_reservation_accommodation_rooms` اجرا می‌شود.
- **شرط‌های به‌روزرسانی (WHERE Clause):**
    - `'reservation_id'` باید برابر با `$data['id'] - 10000` باشد.
    - `'date'` باید برابر با `$data['date']` باشد.
    
    این دو شرط با هم، رکورد دقیق مربوط به یک شب خاص از یک رزرو خاص را هدف قرار می‌دهند.
- **مقادیر جدید (UPDATE Clause):**
    - ستون `room_id` به مقدار `$data['room_id'] - 10000` به‌روزرسانی می‌شود.
    - ستون `updated_at` با تاریخ و زمان فعلی پر می‌شود.

</div>### ۳. مدیریت خطا

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D8%AF%D8%B1-%D9%87%D8%B1-%D9%85%D8%B1%D8%AD%D9%84%D9%87-%D8%A7%D8%B2-%D8%A7">- اگر در هر مرحله از اجرای کوئری‌های پایگاه داده خطایی (مانند عدم وجود رکورد، خطای کلید خارجی و...) رخ دهد، اجرای کد به بلوک `catch` منتقل شده و یک پاسخ خطای `400 Bad Request` با جزئیات کامل استثنا (Exception) بازگردانده می‌شود.

</div>## Response Structure

### پاسخ موفق

در صورتی که تمام دستورات به‌روزرسانی با موفقیت اجرا شوند، سرور یک پاسخ `200 OK` با ساختار زیر بازمی‌گرداند.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "payload": true,
      "meta": {
        "timestamp": 1733286000
      }
    }
    ```

</div>### پاسخ خطا (ورودی نامعتبر)

اگر فیلد `data` در درخواست ارسال نشود یا خالی باشد.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "error": {
        "message": "The data field is required."
      },
      "meta": {
        "timestamp": 1733286005
      }
    }
    ```

</div>### پاسخ خطا (استثنای عمومی)

در صورت بروز هرگونه خطای پایگاه داده یا خطای دیگر در حین اجرا.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad-1">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "error": {
        "code": "23000",
        "message": "SQLSTATE[23000]: Integrity constraint violation: ... (or any other DB error)",
        "trace": [
          // ... Stack trace for debugging ...
        ]
      }
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Freserva"><div class="flowchart"><div class="flow-item">Start (POST /reservation/plan/update)</div><div class="flow-arrow">↓</div><div class="flow-item">Receive Request Body</div><div class="flow-arrow">↓</div><div class="flow-decision" style="background-color: #fff9c4;">Is `data` field present &amp; not empty?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">Loop through each item in `data` array:  
- Execute `UPDATE charter_reservation_accommodation_rooms`  
- `SET room_id = (item.room_id - 10000)`  
- `WHERE reservation_id = (item.id - 10000) AND date = item.date`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with `{"payload": true, ...}`</div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-error">Return 400 with `{"error": {"message": "The data field is required."}, ...}`</div></div></div><div class="flow-error-path" style="margin-top: 20px; clear: both;"><div class="flow-arrow-error">→ On Any DB Exception</div><div class="flow-item-error">Return 400 with Full Exception Trace</div></div></div></div>

# GET /v2/charter/financial

# Charter: Get Financial Report

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

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%DA%A9%D9%84%D8%A7%D8%B3%2F%D8%A2%DB%8C%D8%AA%D9%85-%28c">1. **بر اساس کلاس/آیتم (Classes):** تفکیک مالی بر اساس هر آیتم (مانند کلاس پروازی Y یا اتاق دو تخته).
2. **بر اساس متعهدین (Pledgers):** تفکیک مالی برای رزروهایی که توسط یک همکار خاص تعهد شده‌اند.
3. **بر اساس گارانتی‌کنندگان (Warranties):** تفکیک مالی برای رزروهایی که تحت گارانتی یک شخص یا شرکت خاص (ثبت شده در سیستم) هستند.

این گزارش برای تحلیل فروش، حسابداری و مدیریت درآمد چارترها کاربرد حیاتی دارد.</div><div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Ffin"><div class="endpoint-info"><div>**URL:** `/v2/charter/financial`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@financialCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه اصلی چارتر (`main_id`) که گزارش مالی برای آن درخواست شده است.</td></tr></tbody></table>

</div>#### Example Request

```bash
GET /v2/charter/financial?id=451
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

فرآیند تولید گزارش در چندین مرحله و با تجمیع داده‌ها از جداول مختلف انجام می‌شود.

### ۱. شناسایی جداول و واکشی داده‌های اولیه

<div class="api-docs" id="bkmrk-%D8%A7%D9%86%D8%AA%D8%AE%D8%A7%D8%A8-%D8%AF%DB%8C%D9%86%D8%A7%D9%85%DB%8C%DA%A9-%D8%AC%D8%AF%D8%A7%D9%88%D9%84">- **انتخاب دینامیک جداول:** ابتدا تابع کمکی `getTableCharter(id)` فراخوانی می‌شود تا بر اساس نوع چارتر (پروازی یا اقامتی)، نام جداول صحیح (مانند `charter_flight_reservations` و `charter_flight_calculations`) مشخص شود.
- **واکشی رزروها:** تمام رکوردهای رزرو (که حذف نرم نشده‌اند) مربوط به `main_id` از جدول رزروها (`reserves`) خوانده می‌شوند.
- **واکشی قیمت خرید:** تمام رکوردهای مربوط به آیتم‌های چارتر از جدول محاسبات (`calculations`) خوانده می‌شوند. این رکوردها حاوی اطلاعات **قیمت خرید** برای هر آیتم هستند.

</div>### ۲. ساخت نقشه قیمت خرید (Buy Price Map)

<div class="api-docs" id="bkmrk-%DB%8C%DA%A9-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%D8%A7%D9%86%D8%AC%D9%85%D9%86%DB%8C-%28map">- یک آرایه انجمنی (map) با نام `$buyPrice` ساخته می‌شود.
- سیستم روی رکوردهای `calculations` حلقه می‌زند و برای هر `item\_id` (که در اینجا `calculation-&gt;id` است)، یک ورودی در این نقشه ایجاد می‌کند.
- هر ورودی شامل قیمت خرید (`buy\_adult`, `buy\_child`, `buy\_infant`) و عنوان آیتم است.
- **پردازش عنوان:**
    - اگر عنوان آیتم یک حرفی باشد (مانند کلاس پروازی 'Y')، تابع `Functions::getClassName` فراخوانی می‌شود تا نام کامل و خوانای کلاس پروازی به دو زبان فارسی و انگلیسی تولید شود.
    - در غیر این صورت (برای آیتم‌های غیر پروازی)، همان عنوان ثبت شده در دیتابیس استفاده می‌شود.

</div>### ۳. حلقه اصلی و تجمیع داده‌ها

سیستم یک حلقه بر روی تمام رزروهای واکشی شده (`reserves`) اجرا می‌کند و در هر تکرار، عملیات زیر را انجام می‌دهد:

<div class="api-docs" id="bkmrk-%D8%AA%D8%AC%D9%85%DB%8C%D8%B9-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%DA%A9%D9%84%D8%A7%D8%B3%2F%D8%A2">1. **تجمیع بر اساس کلاس/آیتم (Grouping by Class):**
    - داده‌های مالی هر رزرو در آرایه `$return['classes']` بر اساس `item_id` آن رزرو تجمیع می‌شود.
    - درون هر `item_id`، داده‌ها مجدداً بر اساس رده سنی مسافر (`passenger\_age\_title` که به حروف کوچک تبدیل شده: `adl`, `chd`, `inf`) دسته‌بندی می‌شوند.
    - برای هر رده سنی، مقادیر زیر محاسبه و جمع زده می‌شوند: 
        - `count`: تعداد مسافران.
        - `buy`: مجموع قیمت خرید (از نقشه `$buyPrice` خوانده می‌شود).
        - `payable`: مجموع مبلغ قابل پرداخت (از فیلد JSON `financial` در رکورد رزرو).
        - `taxes`, `commissions`, `markups`: مجموع مالیات، کمیسیون و مارکاپ. این مقادیر با استفاده از تابع کمکی `ReservationController::priceHandle` محاسبه می‌شوند که می‌تواند مقادیر درصدی یا ثابت را بر اساس مبلغ قابل پرداخت محاسبه کند.
    - یک جمع کل نیز برای هر `item_id` (بدون تفکیک سنی) در فیلد `financial` نگهداری می‌شود.

  
1. **تجمیع بر اساس گارانتی/تعهد (Grouping by Warranty/Pledger):**
    - سیستم بررسی می‌کند که آیا رزرو دارای `warranty_type` و `warranty` است.
    - **اگر `warranty_type` برابر با `colleague` باشد:**
        - این رزرو یک "تعهد" (Pledger) است.
        - اطلاعات همکار از جدول `colleagues` واکشی می‌شود.
        - داده‌های مالی رزرو در آرایه `$return['pledgers']` با کلید شناسه همکار (`colleague-&gt;id`) تجمیع می‌شود. ساختار تجمیع دقیقا مشابه تجمیع بر اساس کلاس است (با تفکیک سنی و جمع کل).
    - **اگر `warranty_type` برابر با `system` باشد:**
        - این رزرو یک "گارانتی" (Warranty) است.
        - اطلاعات گارانتی‌کننده از جدول `charter\_warranties` و سپس با join به جدول `colleagues` (برای یافتن مشخصات گارانتی‌کننده) واکشی می‌شود.
        - داده‌های مالی رزرو در آرایه `$return['warranties']` با کلید شناسه گارانتی‌کننده تجمیع می‌شود.

</div>### ۴. خروجی نهایی

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1-%D9%86%D9%87%D8%A7%DB%8C%D8%AA%D8%8C-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-%D8%AF%D8%A7%D8%AF">- در نهایت، ساختار داده تجمیع شده `$return` در فیلد `payload` یک پاسخ `200 OK` قرار گرفته و به کاربر بازگردانده می‌شود.
- فیلد `changes` در ساختار خروجی مقداردهی اولیه می‌شود اما در کد فعلی، هیچ داده‌ای در آن قرار نمی‌گیرد و همیشه یک آرایه خالی است.

</div>## Response Structure

### پاسخ موفق

در صورت موفقیت، سرور یک پاسخ `200 OK` با ساختار پیچیده زیر بازمی‌گرداند. این ساختار شامل سه بخش اصلی `classes`, `pledgers`, و `warranties` است.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "payload": {
        "classes": {
          "1234": { // Key is the item_id
            "title": {
              "en": "Y | Economy/Coach",
              "fa": "Y | اکونومی - Economy/Coach"
            },
            "age": {
              "adl": { "count": 10, "buy": 10000000, "payable": 12000000, "taxes": 1200000, "commissions": 600000, "markups": 0 },
              "chd": { "count": 2, "buy": 1600000, "payable": 2000000, "taxes": 200000, "commissions": 100000, "markups": 0 },
              "inf": { "count": 1, "buy": 100000, "payable": 150000, "taxes": 15000, "commissions": 0, "markups": 0 }
            },
            "financial": {
              "count": 13,
              "buy": 11700000,
              "payable": 14150000,
              "taxes": 1415000,
              "commissions": 700000,
              "markups": 0
            }
          }
          // ... other classes
        },
        "pledgers": {
          "56": { // Key is the colleague_id
            "title": {
              "fa": "آژانس همکار - علی رضایی",
              "en": "Partner Agency - Ali Rezaei"
            },
            "age": {
              "adl": { "count": 2, "buy": 2000000, "payable": 2400000, "taxes": 240000, "commissions": 120000, "markups": 0 }
              // ... chd, inf
            },
            "financial": {
              "count": 2, "buy": 2000000, "payable": 2400000, "taxes": 240000, "commissions": 120000, "markups": 0
            }
          }
          // ... other pledgers
        },
        "warranties": {
          // Structure is identical to "pledgers", key is guarantor's colleague_id
        },
        "changes": [] // Always an empty array in the current implementation
      },
      "meta": {
        "timestamp": 1733288400
      }
    }
    ```

</div>### پاسخ خطا

در صورت بروز هرگونه خطا (مانند عدم یافتن چارتر یا خطای دیتابیس) سرور یک پاسخ `400 Bad Request` بازمی‌گرداند.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "message": "Error details...",
      "trace": [
        // ... Stack trace for debugging ...
      ]
    }
    ```

</div>## Helper Functions

دو تابع کمکی نقش کلیدی در منطق این اندپوینت دارند:

#### `Functions::getClassName($class, $lang)`

این تابع یک کد کلاس پروازی تک حرفی (مانند 'Y', 'C') را به نام کامل و خوانای آن در زبان‌های مختلف (en, fa, ar) تبدیل می‌کند.

```php

```

#### `ReservationController::priceHandle($price, $values)`

این تابع برای محاسبه مقدار پولی مالیات، کمیسیون یا مارکاپ استفاده می‌شود. ورودی آن مبلغ پایه (`price`) و آرایه‌ای از قوانین (`values`) است. هر قانون می‌تواند بر اساس درصد (`percent`) یا یک مبلغ ثابت (`currency`) باشد. تابع مجموع مقادیر محاسبه شده را بازمی‌گرداند.

```php
 0 && $values) {
        foreach ($values as $value) {
            if ($value && isset($value['value'])) { // Direct value structure
                if ($value['value'] > 0) {
                    if ($value['value_type'] == 'percent') {
                        $return += ($price * $value['value']) / 100;
                    } else if ($value['value_type'] == 'currency') {
                        $return += $value['value'];
                    }
                }
            } else { // Nested structure
                foreach ($value as $item) {
                    if (isset($item['value']) && $item['value']) {
                        if ($item['value_type'] == 'percent') {
                            $return += ($price * $item['value']) / 100;
                        } else if ($item['value_type'] == 'currency') {
                            $return += $item['value'];
                        }
                    }
                }
            }
        }
    }
    return $return;
}
  ?>
```

<div class="api-docs" id="bkmrk--3"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fcharter%2F"><div class="flowchart"><div class="flow-item">Start (GET /charter/financial)</div><div class="flow-arrow">↓</div><div class="flow-item">Get `id` from query</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">1. `getTableCharter(id)` to find table names  
2. Fetch all reservations (`reserves`)  
3. Fetch all calculation items (`calculations`)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Loop `calculations` to build `$buyPrice` map.  
Use `getClassName` for titles.</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7; border: 1px solid #fbc02d;">Loop through each reservation in `reserves`</div><div class="flow-split" style="padding-left: 20px; border-left: 2px dashed #ccc;"><div class="flow-path-short"><div class="flow-arrow">↳</div><div class="flow-item">Aggregate financial data into `$return['classes']` based on `item_id` and `age_title`.</div></div><div class="flow-path-short" style="clear: none;"><div class="flow-arrow">↳</div><div class="flow-decision" style="background-color: #fff9c4;">Has `warranty_type`?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-decision-small">Type is 'colleague'?</div><div class="flow-split"><div class="flow-path"><div class="flow-arrow">↓ Yes</div><div class="flow-item">Aggregate into `$return['pledgers']`</div></div><div class="flow-path"><div class="flow-arrow">↓ No ('system')</div><div class="flow-item">Aggregate into `$return['warranties']`</div></div></div></div><div class="flow-path"><div class="flow-arrow">↓ No</div><div class="flow-item-small">Do nothing for this group.</div></div></div></div></div><div class="flow-item" style="clear: both; margin-top: 20px;">End Loop</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with `payload` containing aggregated data</div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Any Exception</div><div class="flow-item-error">Return 400 with Exception Details</div></div></div></div>

# GET /v2/charter/financial/completion

# Charter: Get Completion Financial Report

این اندپوینت برای ارائه یک گزارش **تکمیل مالی** و تحلیلی از یک چارتر طراحی شده است. این گزارش فراتر از تجمیع ساده داده‌های فروش رفته و با محاسبه شاخص‌های کلیدی عملکرد (KPIs) مانند **هزینه کل خرید ظرفیت (Paid)**، **سود یا زیان (Profit)**، و **هزینه ظرفیت فروخته‌نشده (Burned)**، یک دید ۳۶۰ درجه از وضعیت مالی چارتر ارائه می‌دهد.   
خروجی این اندپوینت در چهار بخش اصلی سازماندهی شده است:

<div class="api-docs" id="bkmrk-classes%3A-%DA%AF%D8%B2%D8%A7%D8%B1%D8%B4-%D8%AF%D9%82%DB%8C%D9%82-">1. **Classes:** گزارش دقیق مالی به تفکیک هر آیتم (کلاس پروازی/نوع اتاق) به همراه محاسبات سود و زیان.
2. **Pledgers:** گزارش تجمیعی فروش برای رزروهای تعهد شده توسط همکاران.
3. **Warranties:** گزارش تجمیعی فروش برای رزروهای گارانتی شده.
4. **Total:** یک شیء جامع که کل شاخص‌های مالی چارتر را در خود جمع‌بندی می‌کند.

</div><div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Ffin"><div class="endpoint-info"><div>**URL:** `/v2/charter/financial/completion`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@getCompletionFinancialCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه اصلی چارتر (`main_id`) که گزارش برای آن درخواست شده است.</td></tr></tbody></table>

</div>#### Example Request

```bash
GET /v2/charter/financial/completion?id=451
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

منطق این اندپوینت در دو فاز اصلی اجرا می‌شود: فاز اول تجمیع داده‌های اولیه و فاز دوم محاسبه شاخص‌های مالی پیشرفته.

### فاز اول: واکشی و تجمیع داده‌های اولیه

<div class="api-docs" id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D8%A7%DB%8C%DB%8C-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%88-%D9%88%D8%A7%DA%A9%D8%B4">- **شناسایی جداول و واکشی داده‌ها:** مانند اندپوینت قبلی، ابتدا جداول مربوطه شناسایی شده و تمام رزروها (`reserves`) و آیتم‌های محاسباتی (`calculations`) واکشی می‌شوند.
- **ساخت نقشه قیمت خرید (`$buyPrice`):** یک نقشه از قیمت‌های خرید برای هر آیتم و رده سنی ساخته می‌شود. همچنین ظرفیت کل چارتر (`total.capacity`) با جمع کردن ظرفیت تمام آیتم‌ها محاسبه می‌شود.
- **حلقه اصلی روی رزروها:** سیستم روی تمام رزروها حلقه می‌زند و داده‌ها را تجمیع می‌کند: 
    - **تجمیع در `classes`:** داده‌های مالی هر رزرو (شامل `count`, `payable`, `buy`, `taxes`, `commissions`, `markups`) بر اساس `item\_id` و رده سنی در `return\['classes'\]` جمع می‌شوند.
    - **تجمیع در `total`:** مقادیر کلیدی هر رزرو (`count`, `payable`, `buy`, `taxes`, و ...) مستقیماً به شیء `return\['total'\]` نیز اضافه می‌شوند تا جمع کل چارتر به دست آید.
    - **تجمیع در `pledgers` و `warranties`:** اگر رزرو دارای گارانتی یا تعهد باشد، داده‌های فروش آن (تعداد و مبلغ قابل پرداخت) در دسته‌بندی مربوطه نیز تجمیع می‌شود.

</div>### فاز دوم: محاسبه شاخص‌های سود و زیان ( حلقه دوم)

پس از اتمام حلقه اول و تجمیع داده‌های پایه، یک **حلقه دوم** روی آرایه تجمیع‌شده `return['classes']` اجرا می‌شود تا محاسبات پیشرفته برای هر آیتم انجام شود:

<div class="api-docs" id="bkmrk-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87-%D9%87%D8%B2%DB%8C%D9%86%D9%87-%DA%A9%D9%84-%D8%A2%DB%8C%D8%AA%D9%85">1. **محاسبه هزینه کل آیتم (Paid):**
    - **فرمول:** `(Total Buy Price / Items Sold) * Total Capacity`
    - **توضیح:** این مقدار، هزینه واقعی است که شرکت برای **کل ظرفیت** یک آیتم (چه فروخته شده و چه نشده) پرداخت کرده است. این محاسبه بر اساس میانگین قیمت خرید صندلی/اتاق‌های فروخته شده انجام می‌شود.
2. **محاسبه هزینه سوخت‌شده (Burned):**
    - **فرمول:** `Average Buy Price * (Total Capacity - Items Sold)`
    - **توضیح:** این مقدار نشان‌دهنده هزینه ظرفیت فروخته‌نشده یا "سوخت‌شده" است. این یکی از مهم‌ترین شاخص‌ها برای ارزیابی عملکرد فروش است.
3. **محاسبه سود/زیان (Profit):**
    - **فرمول:** `Total Payable - Total Item Cost (Paid)`
    - **توضیح:** این مقدار، تفاوت بین کل درآمد حاصل از فروش یک آیتم و کل هزینه پرداخت شده برای آن آیتم است. اگر مثبت باشد سود و اگر منفی باشد زیان را نشان می‌دهد.
4. **تشخیص وضعیت مالی (Diagnosis):**
    - بر اساس مقدار `profit`، یک وضعیت متنی تعیین می‌شود: 
        - `creditor`: بستانکار (سودده)
        - `debtor`: بدهکار (زیان‌ده)
        - `neutral`: سربه‌سر
5. **تجمیع نهایی در `total`:** مقادیر محاسبه شده (`paid`, `burned`, `profit`) برای هر آیتم، به مقادیر متناظر در شیء `return\['total'\]` اضافه می‌شوند تا جمع‌بندی نهایی برای کل چارتر به دست آید.

</div>### محاسبه نهایی

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AA%D9%87%D8%A7%D8%8C-%DB%8C%DA%A9-%60diagno">- در انتها، یک `diagnosis` نهایی برای کل چارتر بر اساس مقادیر `total.payable` و `total.paid` محاسبه و در `return\['total'\]` قرار می‌گیرد.

</div>## Response Structure

### پاسخ موفق

پاسخ موفق `200 OK` حاوی یک شیء `payload` با ساختاری بسیار غنی است که شامل بخش `total` در کنار سه بخش دیگر می‌باشد.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "payload": {
        "classes": {
          "1234": { // Key is the item_id
            "title": { "en": "Y | Economy/Coach", "fa": "Y | اکونومی - Economy/Coach" },
            "age": {
              "adl": { "count": 10, "payable": 12000000, "buy": 10000000, "buy_per": 1000000, "taxes": 1080000, "commissions": 600000, "markups": 0 },
              "chd": { "count": 2, "payable": 2000000, "buy": 1600000, "buy_per": 800000, "taxes": 180000, "commissions": 100000, "markups": 0 },
              "inf": { "count": 1, "payable": 150000, "buy": 100000, "buy_per": 100000, "taxes": 0, "commissions": 0, "markups": 0 }
            },
            "financial": {
              "count": 13,
              "capacity": 20,
              "payable": 14150000,
              "buy": 11700000,
              "burned": 6300000,  // <-- New Field
              "paid": 18000000,     // <-- New Field
              "profit": -3850000,   // <-- New Field
              "diagnosis": "debtor", // <-- New Field
              "taxes": 1260000,
              "commissions": 700000,
              "markups": 0
            }
          }
          // ... other classes
        },
        "pledgers": { /* ... Same structure as previous endpoint ... */ },
        "warranties": { /* ... Same structure as previous endpoint ... */ },
        "changes": [],
        "total": { // <-- New Object
          "count": 13,
          "capacity": 20,
          "payable": 14150000,
          "buy": 11700000,
          "burned": 6300000,
          "profit": -3850000,
          "diagnosis": "debtor",
          "paid": 18000000,
          "taxes": 1260000,
          "commissions": 700000,
          "markups": 0
        }
      },
      "meta": { "timestamp": 1733290800 }
    }
    ```

</div>### پاسخ خطا

در صورت بروز خطا، پاسخ `400 Bad Request` با جزئیات استثنا بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "message": "Error details...",
      "trace": [
        // ... Stack trace for debugging ...
      ]
    }
    ```

</div>## Helper Functions

این اندپوینت از همان توابع کمکی اندپوینت قبلی برای ترجمه نام کلاس و محاسبه مقادیر درصدی/ثابت استفاده می‌کند.

#### `Functions::getClassName($class, $lang)`

```php

```

#### `ReservationController::priceHandle($price, $values)`

```php

```

<div class="api-docs" id="bkmrk--4"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Ffinancia"><div class="flowchart"><div class="flow-item">Start (GET /financial/completion)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Phase 1: Aggregation**  
1. Fetch `reserves` &amp; `calculations`.  
2. Build `$buyPrice` map.  
3. Loop `reserves` to populate initial `classes`, `pledgers`, `warranties`, and `total` (buy, payable, counts).</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7; border: 1px solid #fbc02d;">**Phase 2: Profit Calculation**  
1. Loop through aggregated `classes`.  
2. For each class, calculate:  
- `paid` (Total Item Cost)  
- `burned` (Unsold Cost)  
- `profit` (Payable - Paid)  
- `diagnosis` (creditor/debtor)  
3. Add these results to `total` object.</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Final Step**  
Calculate final `diagnosis` for the `total` object.</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with full `payload` (including `total` and calculated fields)</div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Any Exception</div><div class="flow-item-error">Return 400 with Exception Details</div></div></div></div>

# PATCH /v2/charter/financial/completion

# Charter: Finalize Completion Financial Report

این اندپوینت برای **نهایی‌سازی (Finalization)** گزارش مالی تکمیلی یک چارتر طراحی شده است. پس از اینکه یک چارتر به پایان می‌رسد و تمام داده‌های فروش و هزینه‌ها مشخص می‌شود، این اندپوینت فراخوانی می‌شود تا یک "اسنپ‌شات" (Snapshot) از وضعیت مالی نهایی (شامل سود، زیان، هزینه سوخت‌شده و...) تهیه و آن را در پایگاه داده ثبت کند. این عمل معادل "بستن گزارش مالی" برای آن چارتر است و معمولاً پس از آن، امکان تغییر در رزروها یا داده‌های مالی مرتبط با آن چارتر محدود یا مسدود می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Ffin"><div class="endpoint-info"><div>**URL:** `/v2/charter/financial/completion`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** CharterController@setCompletionFinancialCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%28%D8%AA%D9%88">- دسترسی معتبر JWT
- (توصیه می‌شود) دسترسی مبتنی بر نقش (Role-Based Access) فقط برای مدیران مالی یا ادمین‌های سیستم.

</div>## Request Body

برای مشخص کردن اینکه کدام چارتر باید نهایی شود، شناسه اصلی آن باید در بدنه درخواست ارسال گردد.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه اصلی چارتر (`main_id`) که گزارش مالی آن باید نهایی و ثبت شود.</td></tr></tbody></table>

</div>#### Example Request

```json
{
  "main_id": 451
}
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

**نکته مهم:** کد ارائه شده صرفاً یک ساختار اولیه است. منطق واقعی این اندپوینت بسیار پیچیده‌تر بوده و شامل مراحل زیر خواهد بود:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%3A-%D8%B3%DB%8C">1. **اعتبارسنجی ورودی:** سیستم بررسی می‌کند که `main_id` در بدنه درخواست وجود داشته و معتبر است.
2. **بررسی وضعیت چارتر:**
    - ابتدا چارتر با `main_id` مشخص شده از پایگاه داده واکشی می‌شود.
    - سیستم بررسی می‌کند که آیا این چارتر قبلاً نهایی شده است یا خیر (مثلاً با چک کردن یک فیلد `finalized_at` در جدول اصلی چارتر). اگر قبلاً نهایی شده باشد، خطای `409 Conflict` بازگردانده می‌شود تا از ثبت مجدد جلوگیری شود.
3. **تولید داده‌های گزارش:**
    - سیستم به صورت داخلی منطق اندپوینت `GET /v2/charter/financial/completion` را فراخوانی می‌کند تا گزارش مالی کامل و تحلیلی (شامل `profit`, `burned`, `paid` و ...) را برای `main_id` مورد نظر تولید کند.
4. **ذخیره‌سازی اسنپ‌شات (Snapshot):**
    - کل شیء گزارش تولید شده (که خروجی اندپوینت GET است) به فرمت JSON تبدیل می‌شود.
    - این رشته JSON در یک جدول جدید و اختصاصی به نام `charter_financial_snapshots` یا مشابه آن، به همراه `main_id`، شناسه کاربری که عملیات را انجام داده (`finalized_by`)، و تاریخ/زمان فعلی (`finalized_at`) ذخیره می‌شود. این کار تضمین می‌کند که یک نسخه بایگانی‌شده و غیرقابل تغییر از گزارش نهایی همیشه در دسترس است.
5. **به‌روزرسانی وضعیت چارتر اصلی:**
    - پس از ذخیره موفقیت‌آمیز اسنپ‌شات، رکورد مربوط به چارتر در جدول اصلی (مثلاً `charter_main`) به‌روزرسانی شده و فیلد `finalized_at` آن با تاریخ و زمان فعلی پر می‌شود. این کار به عنوان یک پرچم (flag) عمل کرده و از نهایی‌سازی مجدد جلوگیری می‌کند.
6. **پاسخ نهایی:** در صورت موفقیت تمام مراحل، یک پاسخ `204 No Content` بازگردانده می‌شود که به کلاینت اعلام می‌کند عملیات با موفقیت انجام شده است.

</div>## Response Structure

### پاسخ موفق

یک پاسخ موفق به این معنی است که گزارش مالی با موفقیت تولید و بایگانی شده است. طبق استاندارد REST، برای عملیات `PATCH` یا `PUT` که منجر به به‌روزرسانی موفقی شده اما بدنه پاسخی برای برگرداندن ندارد، از کد وضعیت `204` استفاده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-204-no-">- **Status Code:** `204 No Content`
- **Body:** (Empty)

</div>### پاسخ‌های خطا

خطاهای مختلفی ممکن است در این فرآیند رخ دهد:

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`  
    **دلیل:** فیلد `main_id` در بدنه درخواست ارسال نشده یا نوع داده آن صحیح نیست.  
    ```json
    {
      "message": "The main id field is required."
    }
    ```
- **Status Code:** `404 Not Found`  
    **دلیل:** چارتری با `main_id` ارسال شده در سیستم وجود ندارد.  
    ```json
    {
      "message": "Charter not found."
    }
    ```
- **Status Code:** `409 Conflict`  
    **دلیل:** این گزارش مالی قبلاً نهایی شده است و امکان نهایی‌سازی مجدد وجود ندارد.  
    ```json
    {
      "message": "Financial report for this charter has already been finalized."
    }
    ```

</div>## PHP Implementation

کد زیر یک ساختار اولیه برای این اندپوینت است. منطق اصلی که در بخش "Logic Details" تشریح شد، باید در بلوک `try` پیاده‌سازی شود.

```php

public function setCompletionFinancialCharter(Request $request)
{
    // Validate that 'main_id' exists in the request body.
    $validated = $request->validate([
        'main_id' => 'required|integer|exists:charter_main,id',
    ]);

    $mainId = $validated['main_id'];

    try {
        // 1. Check if the charter is already finalized.
        $charter = DB::table('charter_main')->where('id', $mainId)->first();
        if ($charter->finalized_at) {
            return response()->json([
                "message" => "Financial report for this charter has already been finalized."
            ], 409); // 409 Conflict
        }

        // 2. Generate the financial completion report data internally.
        // This would call the same logic as the getCompletionFinancialCharter() method.
        $reportData = $this->generateFinancialData($mainId); // Hypothetical internal method

        // 3. Store the snapshot.
        DB::table('charter_financial_snapshots')->insert([
            'charter_main_id' => $mainId,
            'snapshot_data' => json_encode($reportData),
            'finalized_by' => auth()->id(), // Get user ID from JWT
            'created_at' => now(),
            'updated_at' => now()
        ]);

        // 4. Update the main charter record to mark it as finalized.
        DB::table('charter_main')->where('id', $mainId)->update([
            'finalized_at' => now()
        ]);

        // 5. Return success response.
        return response('', 204);

    } catch (Exception $exception) {
        return response()->json([
            "message" => $exception->getMessage(),
            "trace" => $exception->getTrace()
        ], 400);
    }
}
  
```

# POST /v2/charter/warranty

# Charter: Manage Warranties (Batch Operation)

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fwar"><div class="endpoint-info"><div>**URL:** `/v2/charter/warranty`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@operationWarrantyCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Body Structure

بدنه درخواست از سه بخش اصلی تشکیل شده است: `main_id` برای شناسایی چارتر، آرایه `warranties` برای عملیات ایجاد و به‌روزرسانی، و آرایه `deleted` برای عملیات حذف.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>main\_id</td><td>integer</td><td>**(الزامی)** شناسه اصلی چارتر (`charter_main.id`) که گارانتی‌ها به آن تعلق دارند.</td></tr><tr><td>warranties</td><td>array of objects</td><td>**(الزامی)** آرایه‌ای از اشیاء گارانتی. هر شیء می‌تواند یک گارانتی جدید برای ایجاد یا یک گارانتی موجود برای به‌روزرسانی باشد. ساختار داخلی آن در جدول زیر توضیح داده شده است.</td></tr><tr><td>deleted</td><td>array of integers</td><td>*(اختیاری)* آرایه‌ای از شناسه‌های گارانتی (`charter_warranties.id`) که باید حذف شوند.</td></tr></tbody></table>

</div>### Structure of a Warranty Object (inside `warranties` array)

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer | string</td><td>**شناسه عملیات:**  
- برای **ایجاد (Create)**: این فیلد را ارسال نکنید یا یک شناسه موقت رشته‌ای (مثلاً UUID) ارسال کنید.  
- برای **به‌روزرسانی (Update)**: شناسه عددی گارانتی موجود را ارسال کنید.</td></tr><tr><td>item\_id</td><td>integer</td><td>شناسه آیتم چارتر (کلاس پرواز یا نوع اتاق) که گارانتی به آن تعلق دارد.</td></tr><tr><td>guarantor</td><td>object</td><td>شیء حاوی اطلاعات گارانتی‌کننده (همکار). مثال: `{"id": 12}`</td></tr><tr><td>warranty</td><td>object</td><td>شیء حاوی جزئیات ظرفیت و قیمت گارانتی شده برای هر رده سنی (`adult`, `child`, `infant`). هر رده شامل `number` (تعداد) و `price` است.</td></tr><tr><td>sank\_cost</td><td>numeric</td><td>*(اختیاری)* هزینه سوخت شده (Sunk Cost).</td></tr><tr><td>service\_price</td><td>numeric</td><td>*(اختیاری)* قیمت خدمات اضافی.</td></tr><tr><td>start\_date</td><td>string</td><td>*(اختیاری، برای اقامتگاهی)* تاریخ شروع بازه گارانتی. فرمت: `YYYY-MM-DD`.</td></tr><tr><td>end\_date</td><td>string</td><td>*(اختیاری، برای اقامتگاهی)* تاریخ پایان بازه گارانتی. فرمت: `YYYY-MM-DD`.</td></tr><tr><td>room\_id</td><td>array of objects</td><td>*(اختیاری، برای اقامتگاهی)* آرایه‌ای از اتاق‌های گارانتی شده. هر شیء شامل `room_id` و `number` (شماره اتاق) است. برای عملیات به‌روزرسانی، اتاق‌های جدید در این آرایه قرار می‌گیرند.</td></tr><tr><td>deleted\_room\_id</td><td>array of objects</td><td>*(اختیاری، فقط برای به‌روزرسانی اقامتگاهی)* آرایه‌ای از اتاق‌هایی که باید از گارانتی حذف شوند. ساختار مشابه `room_id`.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت گارانتی (مثلاً 1 برای فعال).</td></tr></tbody></table>

</div>#### Example Request (Create, Update &amp; Delete)

```json
{
  "main_id": 451,
  "warranties": [
    {
      "id": "temp-12345", // CREATE new warranty
      "item_id": 101,
      "guarantor": { "id": 25 },
      "warranty": {
        "adult": { "number": 10, "price": 1200000 },
        "child": { "number": 5, "price": 900000 }
      },
      "status": 1
    },
    {
      "id": 88, // UPDATE existing warranty
      "guarantor": { "id": 26 },
      "warranty": {
        "adult": { "number": 2, "price": 2500000 },
        "child": { "number": 0, "price": 0 }
      },
      "start_date": "2025-12-10",
      "end_date": "2025-12-15",
      "room_id": [ // Add this new room
        { "room_id": 305, "number": "305" }
      ],
      "deleted_room_id": [ // Remove this old room
        { "room_id": 301, "number": "301" }
      ],
      "status": 1
    }
  ],
  "deleted": [92, 95] // DELETE these two warranties
}
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

منطق این اندپوینت به سه بخش اصلی تقسیم می‌شود که به ترتیب اجرا می‌شوند:

### 1. پردازش آرایه `warranties` (ایجاد و به‌روزرسانی)

سیستم روی هر آیتم در آرایه `warranties` حلقه می‌زند و بر اساس وجود یا عدم وجود `id` عددی، تصمیم به ایجاد یا به‌روزرسانی می‌گیرد.

<div class="api-docs" id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%28create">- **عملیات ایجاد (Create):**
    - **شرط:** `id` وجود ندارد یا یک رشته است.
    - **اعتبارسنجی حیاتی (برای اقامتگاهی):** قبل از درج، سیستم بررسی می‌کند که آیا هیچ یک از اتاق‌های مشخص شده (`room_id`) در بازه زمانی (`start_date`, `end_date`) قبلاً در جدول `charter_reservation_accommodation_rooms` **رزرو شده‌اند** یا خیر.
    - **در صورت تداخل:** اگر حتی یک رزرو پیدا شود، عملیات متوقف شده و خطای `409 Conflict` با پیام "اتاق های انتخاب شده دارای رزرو می باشند" بازگردانده می‌شود.
    - **درج داده:** در صورت عدم تداخل، رکورد جدید در `charter_warranties` و رکوردهای مرتبط در `charter_warranty_accommodation_rooms` (در صورت وجود) درج می‌شوند.
- **عملیات به‌روزرسانی (Update):**
    - **شرط:** `id` یک مقدار عددی است.
    - رکورد اصلی در `charter_warranties` با داده‌های جدید به‌روز می‌شود.
    - **مدیریت اتاق‌ها (برای اقامتگاهی):**
        - **افزودن اتاق جدید:** برای هر اتاق در آرایه `room_id`، سیستم ابتدا بررسی می‌کند که آیا این اتاق **هم‌اکنون رزرو شده** یا **قبلاً به این گارانتی لینک شده** است یا خیر. تنها در صورتی که هیچ‌کدام از این دو شرط برقرار نباشد، اتاق جدید به گارانتی اضافه می‌شود.
        - **حذف اتاق:** برای هر اتاق در آرایه `deleted_room_id`، لینک آن از جدول `charter_warranty_accommodation_rooms` حذف می‌شود.

</div>### 2. پردازش آرایه `deleted` (حذف)

<div class="api-docs" id="bkmrk-%D8%B4%D8%B1%D8%B7%3A-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-deleted-%D9%88">- **شرط:** آرایه `deleted` وجود دارد و خالی نیست.
- **اعتبارسنجی حیاتی:** قبل از حذف، سیستم بررسی می‌کند که آیا هیچ یک از شناسه‌های گارانتی موجود در آرایه `deleted`، در جدول رزروها (`charter_reservations`) به عنوان `warranty` ثبت شده‌اند یا خیر.
- **در صورت وجود رزرو:** اگر گارانتی مورد نظر برای ثبت حتی یک رزرو استفاده شده باشد، قابل حذف نیست. عملیات متوقف شده و خطای `409 Conflict` با پیام "آیتم های انتخاب شده دارای رزرو می باشند" بازگردانده می‌شود. این کار از نقض یکپارچگی داده‌ها جلوگیری می‌کند.
- **حذف داده:** در صورت عدم وجود رزرو، رکوردهای گارانتی از جدول `charter_warranties` و تمام لینک‌های اتاق مرتبط با آن از `charter_warranty_accommodation_rooms` حذف می‌شوند.

</div>## Response Structure

### پاسخ موفق

اگر تمام عملیات‌ها (ایجاد، به‌روزرسانی، حذف) بدون خطا انجام شوند، پاسخ `204 No Content` بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-204-no-">- **Status Code:** `204 No Content`

</div>### پاسخ‌های خطا (Conflict)

<div class="api-docs" id="bkmrk-status-code%3A-409-con">- **Status Code:** `409 Conflict`  
    **سناریو ۱:** تلاش برای گارانتی کردن اتاقی که قبلاً رزرو شده است.  
    ```json
    {
      "error": {
        "code": 1000,
        "message": "اتاق های انتخاب شده دارای رزرو می باشند."
      }
    }
    ```
    
    **سناریو ۲:** تلاش برای حذف گارانتی که دارای رزروهای فعال است.  
    ```json
    {
      "error": {
        "code": 1000,
        "message": "آیتم های انتخاب شده دارای رزرو می باشند."
      }
    }
    ```

</div>

# GET /v2/charter/warranty

# Charter: List Warranties

این اندپوینت برای بازیابی لیستی کامل از تمام گارانتی‌های مرتبط با یک چارتر مشخص (`main_id`) استفاده می‌شود. به ازای هر گارانتی، اطلاعات تکمیلی شامل جزئیات گارانتی‌کننده (همکار) و لیست اتاق‌های اختصاص‌داده‌شده (برای چارترهای اقامتگاهی) نیز بازگردانده می‌شود. این اندپوینت از یک مکانیزم کش (Caching) با استفاده از Redis برای بهینه‌سازی و افزایش سرعت دریافت اطلاعات همکاران بهره می‌برد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fwar"><div class="endpoint-info"><div>**URL:** `/v2/charter/warranty`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listWarrantyCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt">- دسترسی معتبر JWT

</div>## Request Parameters (Query String)

این اندپوینت اطلاعات مورد نیاز خود را از طریق Query String دریافت می‌کند.

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه اصلی چارتر (`charter_main.id`) که گارانتی‌های آن باید لیست شوند.</td></tr></tbody></table>

</div>#### Example Request

```http
GET /v2/charter/warranty?id=451
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

فرآیند پردازش درخواست به شرح زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%DA%AF%D8%A7%D8%B1%D8%A7%D9%86%D8%AA%DB%8C%E2%80%8C%D9%87%D8%A7%DB%8C-%D8%A7%D8%B5">1. **واکشی گارانتی‌های اصلی:**
    - ابتدا تمام رکوردها از جدول `charter_warranties` که `main_id` آن‌ها با `id` ارسال شده در درخواست برابر است، واکشی می‌شوند.
    - نتایج بر اساس `id` به صورت نزولی (`DESC`) مرتب می‌شوند تا جدیدترین گارانتی‌ها در ابتدای لیست قرار گیرند.
2. **پردازش و غنی‌سازی هر گارانتی (Mapping):**سیستم بر روی هر رکورد گارانتی واکشی‌شده، یک سری عملیات برای تکمیل اطلاعات انجام می‌دهد:
    
    
    - **واکشی اطلاعات گارانتی‌کننده (Guarantor):**
        - ابتدا تلاش می‌شود اطلاعات همکار (`colleague`) از **کش Redis** با کلیدی مانند `colleagues:{id}` خوانده شود.
        - **در صورت عدم وجود در کش (Cache Miss):** یک کوئری به جدول `colleagues` در پایگاه داده زده می‌شود تا اطلاعات همکار استخراج شود.
        - سپس اطلاعات به دست آمده در Redis ذخیره می‌شود تا در درخواست‌های بعدی با سرعت بیشتری در دسترس باشد (Cache Write).
    - **واکشی اتاق‌های گارانتی‌شده (برای اقامتگاهی):**
        - یک کوئری مجزا به جدول `charter_warranty_accommodation_rooms` زده می‌شود تا تمام اتاق‌هایی که به شناسه گارانتی فعلی (`warranty_id`) متصل هستند، استخراج شوند.
    - **ساختاردهی پاسخ نهایی:**
        - اطلاعات جمع‌آوری شده در یک ساختار JSON استاندارد و قابل فهم برای فرانت‌اند قالب‌بندی می‌شود. فیلدهای پایگاه داده مانند `reserve_adult` و `per_adult` در یک شیء تو در توی `warranty` قرار می‌گیرند.
        - اگر هیچ اتاقی برای گارانتی یافت نشود، فیلد `room_id` مقدار `false` خواهد گرفت.

</div>## Response Structure

### پاسخ موفق (Success)

در صورت موفقیت، پاسخ `200 OK` به همراه یک ساختار JSON استاندارد حاوی لیست گارانتی‌ها بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok">- **Status Code:** `200 OK`

</div>```json
{
  "status": true,
  "time": 1733271000, // Unix Timestamp
  "data": [
    {
      "id": 99,
      "item_id": 102, // شناسه آیتم (مثلا نوع اتاق)
      "guarantor": { // اطلاعات کامل همکار از کش یا دیتابیس
        "id": 25,
        "title_fa": "آژانس مسافرتی بهار",
        "title_en": "Bahar Agency",
        "first_name": "علی",
        "last_name": "رضایی",
        "credit_amount": 50000000,
        "status": 1
      },
      "warranty": {
        "adult": {
          "number": 2,
          "price": 2500000
        },
        "child": {
          "number": 1,
          "price": 1800000
        },
        "infant": {
          "number": 0,
          "price": 0
        }
      },
      "room_id": [ // لیست اتاق های این گارانتی
        {
          "id": 210,
          "warranty_id": 99,
          "room_id": 305
        },
        {
          "id": 211,
          "warranty_id": 99,
          "room_id": 306
        }
      ],
      "sank_cost": 0,
      "service_price": 150000,
      "start_date": "2025-12-10",
      "end_date": "2025-12-15",
      "status": 1
    },
    {
      "id": 88,
      "item_id": 101, // شناسه آیتم (مثلا کلاس پروازی)
      "guarantor": { ... },
      "warranty": { ... },
      "room_id": false, // این گارانتی اقامتگاهی نیست یا اتاقی ندارد
      "sank_cost": 500000,
      "service_price": 0,
      "start_date": null,
      "end_date": null,
      "status": 1
    }
  ]
}
```

### پاسخ خطا (Error)

در صورت بروز هرگونه استثناء (Exception) در سرور، یک پاسخ با ساختار زیر و کد وضعیت (معمولا `200 OK` به دلیل مدیریت خطا در کد) بازگردانده می‌شود.   
**توجه:** نمایش `trace` خطا در محیط پروداکشن توصیه نمی‌شود.

```json
{
    "status": false,
    "time": 1733271000,
    "message": "Error message from the exception.",
    "trace": [ ... ] // Stack trace of the error
}
```

# GET /v2/charter/services/list

# Charter: List Service Categories &amp; Services

این اندپوینت برای دریافت لیست دسته‌بندی‌های خدمات چارتر به‌همراه سرویس‌های زیرمجموعه هر دسته استفاده می‌شود. داده‌ها از جداول `charter_service_categories` و `charter_services` بارگذاری شده و فقط مواردی که `status = 1` دارند بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fser"><div class="endpoint-info"><div>**URL:** `/v2/charter/services/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listServicesCharter</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-jwt-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-%D9%84%D8%A7%D8%B2%D9%85-%D8%A7%D8%B3%D8%AA-%D9%87">- JWT معتبر لازم است
- هیچ محدودیت branch یا core middleware وجود ندارد
- اطلاعات اپراتور از طریق JWT تزریق می‌شود: <div class="code-inline">$request-&gt;get('operator')</div>

</div>## Query Parameters

این اندپوینت هیچ پارامتر ورودی (Query / Body) ندارد.

<div class="api-docs" id="bkmrk--1"></div>## Response Structure

خروجی موفق شامل کلیدهای زیر است:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>status</td><td>boolean</td><td>وضعیت موفقیت درخواست</td></tr><tr><td>time</td><td>integer</td><td>Unix timestamp زمان تولید پاسخ</td></tr><tr><td>data</td><td>array</td><td>آرایه‌ای از دسته‌بندی‌ها به همراه سرویس‌های هر دسته</td></tr></tbody></table>

</div>## Category Object Schema

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه دسته‌بندی</td></tr><tr><td>title.fa</td><td>string</td><td>عنوان فارسی</td></tr><tr><td>title.en</td><td>string</td><td>عنوان انگلیسی</td></tr><tr><td>services</td><td>array</td><td>لیست سرویس‌های زیرمجموعه (در صورت نبود، آرایه خالی)</td></tr></tbody></table>

</div>## Service Object Schema

<div class="api-docs" id="bkmrk-field-type-descripti-2"><table class="schema-table"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه سرویس</td></tr><tr><td>title</td><td>string</td><td>عنوان سرویس</td></tr><tr><td>type</td><td>string</td><td>نوع سرویس</td></tr><tr><td>description</td><td>string|null</td><td>توضیحات سرویس (می‌تواند null باشد)</td></tr></tbody></table>

</div>## Core Processing Logic

```
$categories = DB::table('charter_service_categories')
    ->select('id','title_fa','title_en')
    ->where('status', 1)
    ->get();

foreach ($categories as $category) {
    $services = DB::table('charter_services')
        ->select('id','title','type','description')
        ->where('category', $category->id)
        ->where('status', 1)
        ->get();
}
  
```

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%AF%D8%B3%D8%AA%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A8%D8%A7-sta">- ابتدا دسته‌ها با `status=1` واکشی می‌شوند
- برای هر دسته، سرویس‌های فعال لود می‌شوند
- اگر سرویس وجود نداشته باشد → آرایه خالی برگردانده می‌شود
- خروجی نهایی شامل تمام دسته‌ها + سرویس‌هایشان است

</div>## Response (Success)

```
{
  "status": true,
  "time": 1710000000,
  "data": [
    {
      "id": 1,
      "title": {
        "fa": "عنوان فارسی",
        "en": "English Title"
      },
      "services": [
        {
          "id": 100,
          "title": "VIP Lounge",
          "type": "lounge",
          "description": "Access to VIP lounge"
        }
      ]
    }
  ]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Error)

در صورت Exception (به دلیل وجود try/catch):

```
{
  "status": false,
  "time": 1710000000,
  "message": "Exception message here",
  "trace": [ ... ]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-load-"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Load Active Categories</div><div class="flow-arrow">↓</div><div class="flow-item">For Each Category → Load Services</div><div class="flow-arrow">↓</div><div class="flow-item">Assemble Response Array</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON (status=true)</div></div></div>

# GET /v2/charter/flight-rate

# Charter: Get Approved Flight Rate

این اندپوینت نرخ مصوب (Approved Flight Rate) میان دو فرودگاه را بر اساس جدول `approved_flight_rate` بازیابی می‌کند. سیستم ابتدا دقیقاً مسیر **origin → destination** را جستجو می‌کند و در صورت نبود، مسیر **destination → origin** را نیز بررسی می‌کند. در صورت نیافتن رکورد، خروجی با مقدارهای پیش‌فرض (0) برگردانده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Ffli"><div class="endpoint-info"><div>**URL:** `/v2/charter/flight-rate`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@getApprovedFlightRate</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%81%D9%82%D8%B7-%D8%A8%D8%A7-jwt-%D9%85%D8%B9">- دسترسی فقط با JWT معتبر
- هیچ شرط branch، نقش یا core middleware وجود ندارد
- ورودی‌ها مستقیماً از Query Params خوانده می‌شوند

</div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>origin</td><td>integer</td><td>yes</td><td>ID فرودگاه مبدا</td></tr><tr><td>destination</td><td>integer</td><td>yes</td><td>ID فرودگاه مقصد</td></tr></tbody></table>

</div>## Database Table Reference

اطلاعات نرخ‌های مصوب از جدول زیر خوانده می‌شود:

<div class="api-docs" id="bkmrk-column-type-descript"><table class="schema-table"><thead><tr><th>Column</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>Primary Key</td></tr><tr><td>origin</td><td>integer</td><td>ID فرودگاه مبدا</td></tr><tr><td>destination</td><td>integer</td><td>ID فرودگاه مقصد</td></tr><tr><td>least</td><td>integer</td><td>کمترین نرخ مصوب</td></tr><tr><td>most</td><td>integer</td><td>بیشترین نرخ مصوب</td></tr><tr><td>updated\_at</td><td>datetime</td><td>زمان آخرین بروزرسانی نرخ</td></tr></tbody></table>

</div>## Core Processing Logic

```
$result = DB::table('approved_flight_rate')
    ->where(function ($q) use ($request) {
        $q->where('origin', $request->get('origin'))
          ->where('destination', $request->get('destination'));
    })
    ->orWhere(function ($q) use ($request) {
        $q->where('origin', $request->get('destination'))
          ->where('destination', $request->get('origin'));
    })
    ->first();
  
```

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-exact-ma">- ابتدا رکورد exact-match برای origin → destination جستجو می‌شود
- در صورت عدم وجود، مسیر معکوس destination → origin بررسی می‌شود
- اگر رکورد یافت شود: مقادیر واقعی least/most برگشت داده می‌شود
- اگر یافت نشود: least = 0 و most = 0 و updated\_at = now()

</div>## Response (Success — Found)

در صورتی که رکورد واقعی در جدول موجود باشد:

```
{
  "status": true,
  "time": 1710000000,
  "data": {
    "origin": 1,
    "destination": 2,
    "least": 850000,
    "most": 1350000,
    "updated_at": "2024-09-12 16:40:55"
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Success — Not Found)

اگر ریتی برای این مسیر ثبت نشده باشد:

```
{
  "status": true,
  "time": 1710000000,
  "data": {
    "origin": 1,
    "destination": 2,
    "least": 0,
    "most": 0,
    "updated_at": "2025-02-12 14:10:21"
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Response (Error)

در صورت بروز Exception:

```
{
  "status": false,
  "time": 1710000000,
  "message": "SQLSTATE[HY000]: ...",
  "trace": [ ... ]
}
  
```

<div class="api-docs" id="bkmrk--3"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-read-"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Read Query Params: origin, destination</div><div class="flow-arrow">↓</div><div class="flow-item">DB Query: origin → destination</div><div class="flow-arrow">↓</div><div class="flow-item">If not found → DB Query: destination → origin</div><div class="flow-arrow">↓</div><div class="flow-item">Record Found?</div><div class="flow-arrow">↓</div><div class="flow-branch"><div class="flow-item">Yes → Return real least/most</div><div class="flow-item">No → Return least=0, most=0, now()</div></div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON (status=true)</div></div></div>

# GET /v2/charter/pledger

# Charter: List Branch Pledgers

این اندپوینت لیست ضامنین (Pledgers) یک شعبه را از جدول `charter_pledgers` به همراه اطلاعات همکار (colleague) بازیابی می‌کند. داده‌ها با join روی جدول `colleagues` enrich شده و خروجی نهایی یک آرایه items با ساختار title.fa و title.en بازمی‌گرداند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fple"><div class="endpoint-info"><div>**URL:** `/v2/charter/pledger`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CharterController@listPledgerCharter</div><div>**Middleware:** authWithJwt</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>yes</td><td>شناسه شعبه‌ای که باید لیست ضامنین آن واکشی شود</td></tr></tbody></table>

</div>## Database Tables

### Table: charter\_pledgers

<div class="api-docs" id="bkmrk-column-type-descript"><table class="schema-table"><thead><tr><th>Column</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه ضامن</td></tr><tr><td>colleague\_id</td><td>integer</td><td>شناسه همکار ضامن</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه</td></tr><tr><td>status</td><td>tinyint</td><td>1=فعال، 0=غیرفعال</td></tr></tbody></table>

</div>### Table: colleagues

<div class="api-docs" id="bkmrk-column-type-descript-1"><table class="schema-table"><thead><tr><th>Column</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه همکار</td></tr><tr><td>first\_name</td><td>string</td><td>نام</td></tr><tr><td>last\_name</td><td>string</td><td>نام خانوادگی</td></tr><tr><td>office</td><td>string</td><td>نام دفتر به فارسی</td></tr><tr><td>office\_en</td><td>string</td><td>نام دفتر به انگلیسی</td></tr><tr><td>status</td><td>tinyint</td><td>باید 1 باشد تا همکار معتبر محسوب شود</td></tr></tbody></table>

</div>## Core Processing Logic

```
$pledgers = DB::table('charter_pledgers')
    ->select(
        'charter_pledgers.id',
        'colleagues.id as colleague_id',
        'colleagues.first_name',
        'colleagues.last_name',
        'colleagues.office',
        'colleagues.office_en',
        'colleagues.status'
    )
    ->where('charter_pledgers.branch', $request->get('branch'))
    ->where('charter_pledgers.status', 1)
    ->where('colleagues.status', 1)
    ->leftJoin('colleagues', 'charter_pledgers.colleague_id', 'colleagues.id')
    ->orderBy('charter_pledgers.id', 'DESC')
    ->get();
  
```

<div class="api-docs" id="bkmrk-%D9%81%D9%82%D8%B7-%D8%B6%D8%A7%D9%85%D9%86%DB%8C%D9%86-%D9%81%D8%B9%D8%A7%D9%84-bran">- فقط ضامنین فعال branch مدنظر انتخاب می‌شوند
- leftJoin تضمین می‌کند اگر colleague حذف شده باشد، رکورد pledger حذف نشود (اما فیلتر status=1 جلوی آن را می‌گیرد)
- مرتب‌سازی DESC بر اساس آخرین ضامن‌های ثبت‌شده انجام می‌شود

</div>## Output Mapping Logic

```
$return = $pledgers->map(function ($pledger) {
    return [
        'id' => $pledger->id,
        'colleague_id' => $pledger->colleague_id,
        'title' => [
            'fa' => $pledger->office .
                   ($pledger->last_name != '' 
                     ? ' ' . $pledger->first_name . ' ' . $pledger->last_name 
                     : ""),
            'en' => $pledger->office_en
        ]
    ];
});
  
```

ساختار title.fa مطابق الگوی زیر ساخته می‌شود:

<div class="api-docs" id="bkmrk-field-example-descri"><table class="schema-table"><thead><tr><th>Field</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td>office</td><td>آژانس هرمس</td><td>نام دفتر</td></tr><tr><td>first\_name</td><td>علی</td><td>نام همکار (در صورت وجود)</td></tr><tr><td>last\_name</td><td>کاظمی</td><td>نام خانوادگی همکار (در صورت وجود)</td></tr><tr><td colspan="3">**Final Output Example →** آژانس هرمس علی کاظمی</td></tr></tbody></table>

</div>## Response (Success)

```
{
  "items": [
    {
      "id": 18,
      "colleague_id": 92,
      "title": {
        "fa": "آژانس هرمس علی کاظمی",
        "en": "Hermes Agency"
      }
    }
  ],
  "meta": { "timestamp": 1710000000 }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response (Error)

```
{
  "message": "SQLSTATE[HY000]: ...",
  "trace": [...]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-read-"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Read Query Param: branch</div><div class="flow-arrow">↓</div><div class="flow-item">DB Query charter_pledgers WHERE branch, status=1</div><div class="flow-arrow">↓</div><div class="flow-item">LEFT JOIN colleagues ON colleague_id</div><div class="flow-arrow">↓</div><div class="flow-item">Filter colleagues.status=1</div><div class="flow-arrow">↓</div><div class="flow-item">ORDER BY id DESC</div><div class="flow-arrow">↓</div><div class="flow-item">Map → Build title.fa / title.en</div><div class="flow-arrow">↓</div><div class="flow-item">Return JSON: items[], meta.timestamp</div></div></div>

# POST /v2/charter/pledger

# pledger: storePledgerCharter

این اندپوینت یک ضامن جدید (Pledger) را برای یک شعبه در جدول `charter_pledgers` ثبت می‌کند. در صورت موفقیت مقدار خاصی برنمی‌گرداند و تنها وضعیت **201 Created** داده می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Fple"><div class="endpoint-info"><div>**URL:** `/v2/charter/pledger`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@storePledgerCharter</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Request Body

<div class="api-docs" id="bkmrk-field-type-required-"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>yes</td><td>شناسه شعبه‌ای که ضامن برای آن ثبت می‌شود</td></tr><tr><td>colleague\_id</td><td>integer</td><td>yes</td><td>شناسه همکار ضامن</td></tr></tbody></table>

</div></div>## Controller Logic

```
public function storePledgerCharter(Request $request)
{
    try {
        $insert = [
            'branch' => $request->get('branch'),
            'colleague_id' => $request->colleague_id,
        ];

        DB::table('charter_pledgers')->insert($insert);

        return response('', 201);

    } catch (Exception $exception) {
        return response()->json([
            "message" => $exception->getMessage(),
            "trace" => $exception->getTrace()
        ], 400);
    }
}
  
```

## Table: charter\_pledgers

<div class="api-docs" id="bkmrk-column-type-descript"><div class="table-wrapper"><table><thead><tr><th>Column</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>Primary Key (auto-increment)</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه</td></tr><tr><td>colleague\_id</td><td>integer</td><td>شناسه همکار ضامن</td></tr><tr><td>status</td><td>tinyint</td><td>۱ = فعال، ۰ = غیرفعال</td></tr></tbody></table>

</div></div>## Success Response

```
Status: 201 Created
Body:   (empty)
  
```

## Error Response

```
Status: 400 Bad Request

{
  "message": "SQLSTATE[23000]: ...",
  "trace": [...]
}
  
```

## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-read%3A"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Read: branch, colleague_id</div><div class="flow-arrow">↓</div><div class="flow-item">DB Insert → charter_pledgers</div><div class="flow-arrow">↓</div><div class="flow-item">Insert OK?</div><div class="flow-arrow">↓</div><div class="flow-item">Yes → return 201</div><div class="flow-arrow">↓</div><div class="flow-item">No → return 400 with message + trace</div></div></div>

# POST /v2/charter/accommodation/rooms

# rooms: getCharterAccommodationRooms

این اندپوینت برای دریافت لیست اتاق‌های اقامتگاه (Accommodation Rooms) مرتبط با یک محاسبه `calculation_id` استفاده می‌شود. مقدار `calculation_id` ابتدا اصلاح شده و از جدول مرتبط واکشی می‌گردد. فقط زمانی پاسخ معتبر برگردانده می‌شود که چارتر از نوع **accommodation** باشد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Facc"><div class="endpoint-info"><div>**URL:** `/v2/charter/accommodation/rooms`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@getCharterAccommodationRooms</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Request Body

<div class="api-docs" id="bkmrk-field-type-required-"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>calculation\_id</td><td>integer</td><td>yes</td><td>شناسه محاسبه (calculation) که باید حداقل بزرگ‌تر از 10000 باشد. مقدار واقعی با کم کردن 10000 استخراج می‌شود.</td></tr></tbody></table>

</div></div>## Controller Logic

```
static function getCharterAccommodationRooms(Request $request)
{
    try {
        if (isset($request->calculation_id) && $request->calculation_id) {

            $calculationId = $request->calculation_id - 10000;

            $calculation = DB::table('charter_calculations_accommodation')
                ->select('main_id')
                ->find($calculationId);

            if ($calculation) {

                $charter = DB::table('charters')
                    ->select('type')
                    ->find($calculation->main_id);

                if ($charter && $charter->type == 'accommodation') {

                    $accommodationRooms = DB::table('charter_accommodation_rooms')
                        ->where('calc_id', $calculationId)
                        ->get();

                    return response()->json([
                        'items' => $accommodationRooms,
                        'meta' => ['timestamp' => time()]
                    ]);

                } else {
                    return response()->json([
                        'error' => ['message' => 'charter not found.'],
                        'meta' => ['timestamp' => time()]
                    ], 400);
                }

            } else {
                return response()->json([
                    'error' => ['message' => 'calculation not found.'],
                    'meta' => ['timestamp' => time()]
                ], 400);
            }

        } else {
            return response()->json([
                'error' => ['message' => 'The calculation_id field is required.'],
                'meta' => ['timestamp' => time()]
            ], 400);
        }

    } catch (Exception $exception) {
        return response()->json([
            'error' => [
                'code' => $exception->getCode(),
                'message' => $exception->getMessage(),
                'trace' => $exception->getTrace()
            ]
        ], 400);
    }
}
  
```

<div class="api-docs" id="bkmrk-"></div>## Possible Errors

<div class="api-docs" id="bkmrk-error-http-code-desc"><div class="table-wrapper"><table><thead><tr><th>Error</th><th>HTTP Code</th><th>Description</th></tr></thead><tbody><tr><td>The calculation\_id field is required.</td><td>400</td><td>اگر calculation\_id ارسال نشده باشد</td></tr><tr><td>calculation not found.</td><td>400</td><td>شناسه محاسبه معتبر نیست یا یافت نشد</td></tr><tr><td>charter not found.</td><td>400</td><td>محاسبه یافت شد اما چارتر وابسته از نوع accommodation نیست</td></tr><tr><td>Exception</td><td>400</td><td>بروز خطای داخلی همراه با message، code و trace</td></tr></tbody></table>

</div></div>## Success Response

```
Status: 200 OK

{
  "items": [
    {
      "id": 12,
      "calc_id": 1033,
      "room_title": "...",
      "capacity": 3,
      "price": 1250000,
      ...
    },
    ...
  ],
  "meta": {
    "timestamp": 1730000000
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Error Response

```
Status: 400 Bad Request

{
  "error": {
    "message": "calculation not found."
  },
  "meta": {
    "timestamp": 1730000000
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-check"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Check calculation_id exists?</div><div class="flow-arrow">↓</div><div class="flow-item">calculation_id - 10000</div><div class="flow-arrow">↓</div><div class="flow-item">Find calculation in charter_calculations_accommodation</div><div class="flow-arrow">↓</div><div class="flow-item">Find charter(type)</div><div class="flow-arrow">↓</div><div class="flow-item">type == accommodation?</div><div class="flow-arrow">↓</div><div class="flow-item">Yes → fetch rooms from charter_accommodation_rooms</div><div class="flow-arrow">↓</div><div class="flow-item">Return items + timestamp</div></div></div>

# POST /v2/charter/accommodation/rooms/access

# Charter: Access Accommodation Rooms

این اندپوینت برای «از دسترس خارج کردن» یا «بازگرداندن به دسترس» یک اتاق اقامتگاهی استفاده می‌شود. رفتار اندپوینت بر اساس ارسال یا عدم ارسال `start_date` و `end_date` دو حالت مختلف دارد: در حالت بازه‌دار روی تاریخ‌ها عمل می‌کند و در حالت بدون بازه روی وضعیت عمومی اتاق.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcharter%2Facc"><div class="endpoint-info"><div>**URL:** `/v2/charter/accommodation/rooms/access`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CharterController@accessCharterAccommodationRooms</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Request Body

<div class="api-docs" id="bkmrk-field-type-required-"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>room\_id</td><td>integer</td><td>yes</td><td>شناسه اتاق (باید مقدار واقعی با `room_id - 10000` به‌دست آید)</td></tr><tr><td>delete</td><td>boolean</td><td>no</td><td>اگر true باشد: عملیات «حذف» (بازگشت از حالت disable) اگر false یا ارسال نشود: اعمال disable</td></tr><tr><td>start\_date</td><td>string (Y-m-d)</td><td>conditional</td><td>تاریخ شروع بازه؛ اگر همراه end\_date ارسال شود عملیات روی بازه انجام می‌شود</td></tr><tr><td>end\_date</td><td>string (Y-m-d)</td><td>conditional</td><td>تاریخ پایان بازه؛ یک روز از آن کم می‌شود (`subDay()`)</td></tr></tbody></table>

</div></div>## Controller Logic

```
static function accessCharterAccommodationRooms(Request $request)
{
    try {
        $roomId = $request->room_id - 10000;
        $delete = false;

        if (isset($request->delete) && $request->delete) {
            $delete = filter_var($request->delete, FILTER_VALIDATE_BOOLEAN);
        }

        if ((isset($request->start_date) && $request->start_date) && isset($request->end_date) && $request->end_date) {

            $startDate = Carbon::createFromFormat('Y-m-d', $request->start_date);
            $endDate = Carbon::createFromFormat('Y-m-d', $request->end_date)->subDay();
            $period = CarbonPeriod::create($startDate, $endDate);

            if ($delete) {

                foreach ($period as $date) {
                    $dateString = $date->toDateString();

                    DB::table('charter_reservation_accommodation_rooms')
                        ->where([
                            ['type', '=', 'disable'],
                            ['room_id', '=', $roomId],
                            ['date', '=', $dateString],
                        ])
                        ->delete();
                }

            } else {

                $allReservations = DB::table('charter_reservation_accommodation_rooms')
                    ->where('date', '>=', $startDate->toDateString())
                    ->where('date', '<=', $endDate->toDateString())
                    ->where('room_id', $roomId)
                    ->exists();

                if (!$allReservations) {

                    foreach ($period as $date) {
                        $dateString = $date->toDateString();

                        DB::table('charter_reservation_accommodation_rooms')->insertGetId([
                            'type' => 'disable',
                            'room_id' => $roomId,
                            'date' => $dateString,
                        ]);
                    }

                } else {

                    return response()->json([
                        'error' => [
                            'message' =>
                                'این اتاق در بازه درخواستی شما دارای رزرو میباشد و از دسترس خارج کردن اتاق امکان پذیر نمی باشد.',
                        ],
                        'meta' => ['timestamp' => time()]
                    ], 400);

                }
            }

        } else {

            if ($delete) {

                DB::table('charter_accommodation_rooms')
                    ->where('id', $roomId)
                    ->update(['status' => 1]);

            } else {

                $allReservations = DB::table('charter_reservation_accommodation_rooms')
                    ->where('date', '>', Carbon::today())
                    ->where('room_id', $roomId)
                    ->exists();

                if (!$allReservations) {

                    DB::table('charter_accommodation_rooms')
                        ->where('id', $roomId)
                        ->update(['status' => 2]);

                } else {

                    return response()->json([
                        'error' => [
                            'message' =>
                                'این اتاق در بازه درخواستی شما دارای رزرو می باشد و از دسترس خارج کردن اتاق امکان پذیر نمی باشد.',
                        ],
                        'meta' => ['timestamp' => time()]
                    ], 400);

                }
            }
        }

        return response()->json([
            "payload" => true,
            'meta' => ['timestamp' => time()]
        ]);

    } catch (Exception $exception) {
        return response()->json([
            'error' => [
                'code' => $exception->getCode(),
                'message' => $exception->getMessage(),
                'trace' => $exception->getTrace()
            ]
        ], 400);
    }
}
  
```

<div class="api-docs" id="bkmrk-"></div>## Possible Errors

<div class="api-docs" id="bkmrk-error-message-http-c"><div class="table-wrapper"><table><thead><tr><th>Error Message</th><th>HTTP Code</th><th>Description</th></tr></thead><tbody><tr><td>این اتاق در بازه درخواستی شما دارای رزرو میباشد و از دسترس خارج کردن اتاق امکان پذیر نمی باشد.</td><td>400</td><td>وقتی برای بازهٔ ارسالی قبلاً رزرو وجود دارد</td></tr><tr><td>این اتاق در بازه درخواستی شما دارای رزرو می باشد و از دسترس خارج کردن اتاق امکان پذیر نمی باشد.</td><td>400</td><td>وقتی بدون بازه، رزرو آینده وجود دارد (نسخه دوم پیام با فاصله متفاوت)</td></tr></tbody></table>

</div></div>## Success Response

```
Status: 200 OK

{
  "payload": true,
  "meta": {
    "timestamp": 1730000000
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-room_"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">room_id - 10000</div><div class="flow-arrow">↓</div><div class="flow-item">delete flag?</div><div class="flow-arrow">↓</div><div class="flow-item">start_date &amp; end_date provided?</div><div class="flow-arrow">↓</div><div class="flow-item">Yes → Build date period</div><div class="flow-arrow">↓</div><div class="flow-item">Check reservations in range</div><div class="flow-arrow">↓</div><div class="flow-item">If none → insert disable rows</div><div class="flow-arrow">↓</div><div class="flow-item">Else → return error</div><div class="flow-arrow">↓</div><div class="flow-item">No (no dates)</div><div class="flow-arrow">↓</div><div class="flow-item">Check future reservations</div><div class="flow-arrow">↓</div><div class="flow-item">If none → update status</div><div class="flow-arrow">↓</div><div class="flow-item">Else → return error</div></div></div>

# GET /v2/academy/categories/view

# Academy: View Categories

این اندپوینت برای دریافت لیست دسته‌بندی‌های فعال آکادمی استفاده می‌شود. تنها رکوردهایی که `status = 1` دارند واکشی می‌شوند و داده‌ها در ساختار استاندارد شامل فیلدهای id، slug، flag، title، description و status بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Facademy%2Fcat"><div class="endpoint-info"><div>**URL:** `/v2/academy/categories/view`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** AcademyController@viewCategories</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Request

این اندپوینت هیچ پارامتر اجباری یا اختیاری دریافت نمی‌کند.

<div class="api-docs" id="bkmrk-"></div>## Controller Logic

```
public function viewCategories(Request $request)
{
    $resultCategories = DB::table('academy_categories')->where("status", 1)->get();
    $categories = [];

    foreach ($resultCategories as $category) {
        $categories[] = [
            "id" => $category->id,
            "slug" => $category->slug,
            "flag" => $category->flag,
            "title" => [
                "fa" => $category->title_fa,
                "en" => $category->title_en
            ],
            "description" => $category->description,
            "status" => $category->status
        ];
    }

    return [
        "status" => true,
        "time" => time(),
        "data" => $categories
    ];
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Response Structure

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>status</td><td>boolean</td><td>همیشه true در صورت اجرای موفق</td></tr><tr><td>time</td><td>integer (timestamp)</td><td>زمان پاسخ</td></tr><tr><td>data</td><td>array</td><td>لیست دسته‌بندی‌ها</td></tr></tbody></table>

</div></div>## Category Object

<div class="api-docs" id="bkmrk-field-type-descripti-1"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه دسته‌بندی</td></tr><tr><td>slug</td><td>string</td><td>اسلاگ یکتا</td></tr><tr><td>flag</td><td>string</td><td>فلگ/برچسب دسته‌بندی</td></tr><tr><td>title.fa</td><td>string</td><td>عنوان فارسی</td></tr><tr><td>title.en</td><td>string</td><td>عنوان انگلیسی</td></tr><tr><td>description</td><td>string</td><td>توضیحات دسته‌بندی</td></tr><tr><td>status</td><td>integer</td><td>وضعیت دسته‌بندی (فقط 1 بازگردانده می‌شود)</td></tr></tbody></table>

</div></div>## Example Response

```
Status: 200 OK

{
  "status": true,
  "time": 1730000000,
  "data": [
    {
      "id": 4,
      "slug": "frontend",
      "flag": "fe",
      "title": {
        "fa": "فرانت‌اند",
        "en": "Frontend"
      },
      "description": "دسته‌بندی آموزش‌های تخصصی فرانت‌اند",
      "status": 1
    },
    ...
  ]
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-fetch"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch categories where status = 1</div><div class="flow-arrow">↓</div><div class="flow-item">Transform DB rows → array</div><div class="flow-arrow">↓</div><div class="flow-item">Return status=true + data + time()</div></div></div>

# GET /v2/academy/courses/view

# Academy: View Courses

این اندپوینت لیست دوره‌های آموزشی آکادمی را با امکان فیلتر بر اساس دسته‌بندی بازمی‌گرداند. داده‌ها شامل اطلاعات کامل دوره، اطلاعات دسته‌بندی مرتبط، وضعیت کاربر در دوره (progress)، تعداد کل مراحل و وضعیت نمایش (featured) است. تنها دوره‌هایی که وضعیت آن‌ها 1 یا 3 باشد در خروجی قرار می‌گیرند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Facademy%2Fcou"><div class="endpoint-info"><div>**URL:** `/v2/academy/courses/view`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** AcademyController@viewCourses</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div><div>**Filter:** `?category_id=` (optional)</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><div class="table-wrapper"><table><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>category\_id</td><td>integer</td><td>no</td><td>اگر ارسال شود فقط دوره‌های مربوط به آن دسته‌بندی نمایش داده می‌شوند</td></tr></tbody></table>

</div></div>## Controller Logic

```
// Filter: category_id (optional)
// Only show courses with status IN (1,3)
// Join with academy_categories for category details
// For each course: find progress, total steps, progress completed

$resultCourses = DB::table('academy_courses')
    ->select([...])
    ->where(function ($q) use ($request) {
        if ($request->has('category_id') && $request->get('category_id')) {
            $q->where('academy_courses.category', $request->get('category_id'));
        }
    })
    ->whereIn("academy_courses.status", [1, 3])
    ->leftJoin("academy_categories", "academy_categories.id", "academy_courses.category")
    ->get();

foreach ($resultCourses as $course) {

    $resultCompleted = DB::table('academy_course_completed')
        ->select('completed', 'current_step')
        ->where('course', $course->id)
        ->where('student', $request->get('operator')->id)
        ->first();

    if (!$resultCompleted) {
        $stepCompleted = 0;
        $currentStep = 1;
    } else {
        $stepCompleted = $resultCompleted->completed;
        $currentStep = $resultCompleted->current_step;
    }

    $courseCount = DB::table('academy_course_steps')
        ->where('course', $course->id)
        ->count();

    $courses[] = [
        "id" => $course->id,
        "slug" => $course->slug,
        "branch" => !is_null($course->branch) ? $course->branch : false,
        "price" => !is_null($course->price) ? $course->price : 'free',
        "necessary" => !is_null($course->necessary) ? json_decode($course->necessary) : false,
        "title" => [...],
        "category" => [...],
        "progress" => [
            "current_step" => (int)$currentStep,
            "completed" => (int)$stepCompleted
        ],
        "duration" => $course->duration,
        "total_steps" => $courseCount,
        "featured" => $course->featured,
        "description" => $course->description,
        "status" => $course->status,
        "created_at" => $course->created_at,
        "updated_at" => $course->updated_at
    ];
}

return [
    "status" => true,
    "time" => time(),
    "data" => $courses
];
  
```

<div class="api-docs" id="bkmrk-"></div>## Course Object Structure

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه دوره</td></tr><tr><td>slug</td><td>string</td><td>اسلاگ یکتا</td></tr><tr><td>branch</td><td>integer | false</td><td>شناسه شعبه یا false اگر مقدار نداشته باشد</td></tr><tr><td>price</td><td>integer | "free"</td><td>قیمت دوره یا "free" اگر مقدار نداشته باشد</td></tr><tr><td>necessary</td><td>array | false</td><td>پیش‌نیازهای دوره (json\_decode) یا false</td></tr><tr><td>title.fa</td><td>string</td><td>عنوان فارسی دوره</td></tr><tr><td>title.en</td><td>string</td><td>عنوان انگلیسی دوره</td></tr><tr><td>category.\*</td><td>object</td><td>اطلاعات کامل دسته‌بندی مرتبط</td></tr><tr><td>progress.current\_step</td><td>integer</td><td>مرحله فعلی کاربر در دوره</td></tr><tr><td>progress.completed</td><td>integer</td><td>تعداد مراحل تکمیل‌شده توسط کاربر</td></tr><tr><td>duration</td><td>integer</td><td>مدت زمان دوره (به واحد دقیقه)</td></tr><tr><td>total\_steps</td><td>integer</td><td>تعداد کل مراحل دوره</td></tr><tr><td>featured</td><td>integer (0/1)</td><td>وضعیت برجسته بودن دوره</td></tr><tr><td>description</td><td>string</td><td>توضیحات دوره</td></tr><tr><td>status</td><td>integer</td><td>1 یا 3 (فعال / نمایشی)</td></tr><tr><td>created\_at</td><td>datetime</td><td>تاریخ ایجاد</td></tr><tr><td>updated\_at</td><td>datetime</td><td>تاریخ به‌روزرسانی</td></tr></tbody></table>

</div></div>## Example Response

```
Status: 200 OK

{
  "status": true,
  "time": 1730000000,
  "data": [
    {
      "id": 21,
      "slug": "advanced-react",
      "branch": false,
      "price": "free",
      "necessary": false,
      "title": {
        "fa": "ری‌اکت پیشرفته",
        "en": "Advanced React"
      },
      "category": { ... },
      "progress": { "current_step": 1, "completed": 0 },
      "duration": 180,
      "total_steps": 12,
      "featured": 1,
      "description": "دوره کامل React برای توسعه‌دهندگان پیشرفته",
      "status": 1,
      "created_at": "2024-04-02 11:30:00",
      "updated_at": "2024-04-20 19:12:00"
    }
  ]
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%28operat"><div class="flowchart"><div class="flow-item">Validate JWT (operator loaded)</div><div class="flow-arrow">↓</div><div class="flow-item">Check optional category_id</div><div class="flow-arrow">↓</div><div class="flow-item">Query academy_courses WHERE status IN (1,3)</div><div class="flow-arrow">↓</div><div class="flow-item">LEFT JOIN academy_categories</div><div class="flow-arrow">↓</div><div class="flow-item">Loop → Fetch course progress (completed/current_step)</div><div class="flow-arrow">↓</div><div class="flow-item">Count total_steps from academy_course_steps</div><div class="flow-arrow">↓</div><div class="flow-item">Build final structured course object</div><div class="flow-arrow">↓</div><div class="flow-item">Return status=true + time + data[]</div></div></div>

# GET /v2/academy/course/steps/view

# Academy: View Course Steps

این اندپوینت اطلاعات کامل یک دوره آموزشی به‌همراه تمام مراحل (steps) آن را برمی‌گرداند. همچنین وضعیت پیشرفت کاربر (current\_step و completed) را به‌روزرسانی و بازگردانی می‌کند. منطق این API بر اساس آخرین مرحله (آخرین رکورد step) داده دریافت شده بنا شده است و اطلاعات دوره نیز عمداً از همان آخرین step استخراج می‌شود (طبق رفتار فعلی سیستم).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Facademy%2Fcou"><div class="endpoint-info"><div>**URL:** `/v2/academy/course/steps/view`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** AcademyController@viewStepsCourse</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><div class="table-wrapper"><table><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>course\_id</td><td>integer</td><td>yes</td><td>شناسه دوره‌ای که می‌خواهید مراحل آن را مشاهده کنید</td></tr><tr><td>step\_id</td><td>integer</td><td>no</td><td>اگر ارسال شود فقط یک مرحله خاص فیلتر می‌شود</td></tr><tr><td>current\_step</td><td>integer</td><td>no</td><td>مرحله جدیدی که کاربر به آن رسیده؛ اگر برابر total\_steps باشد → مقدار completed یک افزایش می‌یابد</td></tr></tbody></table>

</div></div>## Controller Logic

```
// 1. Fetch all steps of this course (optional filtering by step_id)
// 2. LEFT JOIN course + category info inside each step row
// 3. Load or create progress record in academy_course_completed
// 4. Update progress (completed / current_step)
// 5. Build steps[]
// 6. Build final course object based on the LAST step (intentional behavior)

$resultCourseSteps = DB::table('academy_course_steps')
    ->select([... course + step + category fields ...])
    ->where('academy_course_steps.course', $request->get('course_id'))
    ->where(function ($q) {
        if ($request->has('step_id')) $q->where('academy_course_steps.id', $request->get('step_id'));
    })
    ->leftJoin("academy_courses", ...)
    ->leftJoin("academy_categories", ...)
    ->orderBy('order')->orderBy('id')
    ->get();

$resultStep = DB::table('academy_course_completed')
    ->where('course', $request->get('course_id'))
    ->where('student', $request->get('operator')->id)
    ->first();

$courseCount = total number of steps in academy_course_steps;

// Progress Update Logic
if (!$resultStep) create new record with defaults;
else update based on current_step rules;

// Build steps[]
foreach (... each step ...) { ... }

// IMPORTANT: course info is taken from LAST step (as user confirmed)
$return = [
   "id" => $step->course_id,
   ...
   "steps" => $steps
];

return [...];
  
```

<div class="api-docs" id="bkmrk-"></div>## Step Object Structure

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه مرحله</td></tr><tr><td>title.fa</td><td>string</td><td>عنوان فارسی مرحله</td></tr><tr><td>title.en</td><td>string</td><td>عنوان انگلیسی مرحله</td></tr><tr><td>subtitle.fa</td><td>string</td><td>زیرعنوان فارسی</td></tr><tr><td>subtitle.en</td><td>string</td><td>زیرعنوان انگلیسی</td></tr><tr><td>content</td><td>string</td><td>محتوای مرحله (HTML / Markdown)</td></tr><tr><td>order</td><td>integer</td><td>ترتیب نمایش مرحله</td></tr><tr><td>status</td><td>integer</td><td>وضعیت فعال/غیرفعال</td></tr><tr><td>created\_at</td><td>datetime</td><td>تاریخ ساخت</td></tr><tr><td>updated\_at</td><td>datetime</td><td>آخرین به‌روزرسانی</td></tr></tbody></table>

</div></div>## Course Object Structure (Extracted From Last Step)

<div class="api-docs" id="bkmrk-field-type-descripti-1"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه دوره (از آخرین step)</td></tr><tr><td>slug</td><td>string</td><td>اسلاگ دوره</td></tr><tr><td>branch</td><td>integer | false</td><td>شناسه شعبه یا false</td></tr><tr><td>price</td><td>integer | "free"</td><td>قیمت دوره</td></tr><tr><td>necessary</td><td>array | false</td><td>پیش‌نیازها</td></tr><tr><td>title.fa</td><td>string</td><td>عنوان فارسی دوره</td></tr><tr><td>title.en</td><td>string</td><td>عنوان انگلیسی دوره</td></tr><tr><td>category.\*</td><td>object</td><td>اطلاعات دسته‌بندی</td></tr><tr><td>progress.current\_step</td><td>integer</td><td>مرحله فعلی</td></tr><tr><td>progress.completed</td><td>integer</td><td>تعداد مراحل تکمیل‌شده</td></tr><tr><td>duration</td><td>integer</td><td>مدت زمان دوره</td></tr><tr><td>total\_steps</td><td>integer</td><td>تعداد کل مراحل</td></tr><tr><td>featured</td><td>integer</td><td>1 اگر دوره ویژه است</td></tr><tr><td>created\_at</td><td>datetime</td><td>تاریخ ساخت</td></tr><tr><td>updated\_at</td><td>datetime</td><td>آخرین تغییر</td></tr><tr><td>steps</td><td>array</td><td>آرایه‌ی تمام مراحل</td></tr></tbody></table>

</div></div>## Important Note

اطلاعات دوره از \*\*آخرین step\*\* استخراج می‌شود. این رفتار در کد وجود دارد و توسط کاربر (علیرضا) تأیید شده‌است. اگر steps خالی باشد، `$step` undefined شده و سیستم خطا خواهد داد.

<div class="api-docs" id="bkmrk--1"></div>## Example Response

```
Status: 200 OK

{
  "status": true,
  "time": 1730000000,
  "data": {
    "id": 21,
    "slug": "advanced-react",
    "branch": false,
    "price": "free",
    "necessary": false,
    "title": { "fa": "ری‌اکت پیشرفته", "en": "Advanced React" },
    "category": {
      "id": 3,
      "flag": "frontend",
      "title": { "fa": "فرانت‌اند", "en": "Frontend" }
    },
    "progress": { "current_step": 2, "completed": 1 },
    "total_steps": 12,
    "duration": 180,
    "featured": 1,
    "created_at": "2024-04-02 11:30:00",
    "updated_at": "2024-04-20 19:12:00",
    "status": 1,
    "steps": [
      {
        "id": 10,
        "title": { "fa": "آشنایی", "en": "Introduction" },
        "subtitle": { "fa": "مقدمه", "en": "Intro" },
        "content": "...",
        "order": 1,
        "status": 1,
        "created_at": "2024-04-02 11:30:00",
        "updated_at": "2024-04-02 11:30:00"
      },
      ...
    ]
  }
}
  
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%28operat"><div class="flowchart"><div class="flow-item">Validate JWT (operator)</div><div class="flow-arrow">↓</div><div class="flow-item">Fetch steps (JOIN course + category)</div><div class="flow-arrow">↓</div><div class="flow-item">Load or create progress record</div><div class="flow-arrow">↓</div><div class="flow-item">Update current_step / completed</div><div class="flow-arrow">↓</div><div class="flow-item">Build steps[]</div><div class="flow-arrow">↓</div><div class="flow-item">Extract course info from LAST step</div><div class="flow-arrow">↓</div><div class="flow-item">Return final structured course object</div></div></div>

# POST /v2/academy/course/steps/update

# Academy: Update / Store / Delete Course Steps

این اندپوینت سه عملیات روی مراحل دوره (Course Steps) انجام می‌دهد: ایجاد (store)، ویرایش (update) و حذف (delete). نوع عملیات از طریق پارامتر `action` مشخص می‌شود. همه عملیات‌ها روی جدول `academy_course_steps` انجام می‌شود و در صورت خطا، پاسخ استاندارد شامل پیام خطا بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Facademy%2Fcou"><div class="endpoint-info"><div>**URL:** `/v2/academy/course/steps/update`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** AcademyController@updateStepsCourse</div><div>**Middleware:** authWithJwt</div><div>**Auth:** JWT Required</div></div></div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-required-"><div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>action</td><td>string</td><td>yes</td><td>مشخص‌کننده عملیات. یکی از مقادیر: **update**، **store**، **delete**.</td></tr><tr><td>data.course</td><td>integer</td><td>yes (store/update)</td><td>شناسه دوره</td></tr><tr><td>data.step\_id</td><td>integer</td><td>yes (update/delete)</td><td>شناسه مرحله</td></tr><tr><td>data.title\_fa</td><td>string</td><td>yes (store/update)</td><td>عنوان فارسی مرحله</td></tr><tr><td>data.title\_en</td><td>string | null</td><td>no</td><td>اگر رشته خالی ارسال شود → مقدار `null` ذخیره می‌شود (دقیقاً مطابق کد).</td></tr><tr><td>data.subtitle\_fa</td><td>string</td><td>yes</td><td>زیرعنوان فارسی</td></tr><tr><td>data.subtitle\_en</td><td>string | null</td><td>no</td><td>اگر رشته خالی باشد تبدیل به null می‌شود.</td></tr><tr><td>data.content</td><td>string</td><td>yes</td><td>محتوای مرحله (HTML یا Markdown)</td></tr><tr><td>data.order</td><td>integer</td><td>yes</td><td>ترتیب مرحله در دوره</td></tr></tbody></table>

</div></div>## Controller Logic

```
action = $request->get('action');
data = $request->data;

try {

  // ---------- UPDATE ----------
  if (action == 'update') {
      update academy_course_steps where id = data['step_id']
      set:
        course = data['course']
        title_fa = data['title_fa']
        title_en = data['title_en'] == '' ? null : data['title_en']
        subtitle_fa = data['subtitle_fa']
        subtitle_en = data['subtitle_en'] == '' ? null : data['subtitle_en']
        content = data['content']
        order = data['order']
        updated_at = now()

      return status=true, time

  // ---------- STORE ----------
  } else if (action == 'store') {
      insert into academy_course_steps:
        course = data['course']
        title_fa = data['title_fa']
        title_en = data['title_en'] == '' ? null : data['title_en']
        subtitle_fa = data['subtitle_fa']
        subtitle_en = data['subtitle_en'] == '' ? null : data['subtitle_en']
        content = data['content']
        order = data['order']
        created_at = now()
        updated_at = now()

      return status=true, time

  // ---------- DELETE ----------
  } else if (action == 'delete') {
      delete from academy_course_steps where id = data['step_id']
      return status=true, time
  }

} catch (Exception $e) {
    return status=false, time, message=$e->getMessage();
}
  
```

<div class="api-docs" id="bkmrk-"></div>## Important Notes

<div class="api-docs" id="bkmrk-%D9%87%D8%B1-%D8%B3%D9%87-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-%DB%8C">- هر سه عملیات خروجی یکسان دارند: `{"status": true, "time": ...}`
- رشته‌های خالی `""` در فیلدهای `title_en` و `subtitle_en` به `null` تبدیل می‌شوند.
- عملیات حذف، Soft Delete نیست — رکورد کاملاً حذف می‌شود.
- در صورت بروز Exception، پیام خطا همان مقدار واقعی Exception بازگردانی می‌شود.

</div>## Example Requests

### 1. Update Step

```
POST /v2/academy/course/steps/update
{
  "action": "update",
  "data": {
    "step_id": 12,
    "course": 4,
    "title_fa": "ویرایش شده",
    "title_en": "",
    "subtitle_fa": "ساب جدید",
    "subtitle_en": "",
    "content": "
```

New HTML

```
",
    "order": 3
  }
}
  
```

### 2. Store New Step

```
POST /v2/academy/course/steps/update
{
  "action": "store",
  "data": {
    "course": 4,
    "title_fa": "مرحله جدید",
    "title_en": "New Step",
    "subtitle_fa": "ساب",
    "subtitle_en": "",
    "content": "
```

Hello

```
",
    "order": 5
  }
}
  
```

### 3. Delete Step

```
POST /v2/academy/course/steps/update
{
  "action": "delete",
  "data": {
    "step_id": 12
  }
}
  
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-validate-jwt-%E2%86%93-read-"><div class="flowchart"><div class="flow-item">Validate JWT</div><div class="flow-arrow">↓</div><div class="flow-item">Read action (update / store / delete)</div><div class="flow-arrow">↓</div><div class="flow-item">Run respective DB query</div><div class="flow-arrow">↓</div><div class="flow-item">Return status=true | handle exception</div></div></div>

# GET /v2/mail/servers/list

# Mail: Get Server List

این اندپوینت لیستی از سرورهای ایمیل **فعال** (Status = 1) را برای یک شعبه (Branch) خاص بازیابی می‌کند.   
خروجی شامل اطلاعات پیکربندی سرویس و محدودیت‌های ماهانه هر سرور است که برای مدیریت ارسال ایمیل‌ها استفاده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Fserver"><div class="endpoint-info"><div>**URL:** `/v2/mail/servers/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailServer</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%28%D8%A7%D8%AD">- دسترسی معتبر JWT (احراز هویت شده)

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>mixed</td><td>**(فیلتر)** شناسه شعبه مورد نظر برای فیلتر کردن سرورهای ایمیل.</td></tr></tbody></table>

</div>#### Example Request

```bash
GET /v2/mail/servers/list?branch=10
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

فرآیند پردازش در متد `mailServer` به صورت زیر است:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%3A-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA">1. **دریافت ورودی:** پارامتر `branch` از درخواست دریافت می‌شود.
2. **کوئری دیتابیس:** به جدول `mail_servers` کوئری زده می‌شود با دو شرط: 
    - `branch` برابر با پارامتر ورودی باشد.
    - `status` برابر با `1` باشد (فقط سرورهای فعال).
3. **تغییر ساختار (Transformation):** روی نتایج حلقه زده می‌شود تا ساختار خروجی استاندارد شود: 
    - یک شیء تو در تو به نام `service` ایجاد می‌شود که شامل نام سرویس (از فیلد `service`) و شناسه سرویس (از فیلد `service_id`) است.
    - سایر فیلدها مثل `monthly_limit` و تاریخ‌ها مستقیماً منتقل می‌شوند.
4. **ارسال پاسخ:** لیست نهایی در قالب استاندارد با وضعیت `true` بازگردانده می‌شود.

</div>## Response Structure

### پاسخ موفق

در صورت موفقیت، لیست سرورها در آرایه `data` قرار می‌گیرد.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670154800,
      "data": [
        {
          "id": 5,
          "service": {
            "title": "SendGrid",
            "id": 101
          },
          "monthly_limit": 5000,
          "created_at": "2023-01-10 12:00:00",
          "updated_at": "2023-05-15 14:30:00"
        },
        {
          "id": 8,
          "service": {
            "title": "SMTP Internal",
            "id": 202
          },
          "monthly_limit": 10000,
          "created_at": "2023-02-20 09:00:00",
          "updated_at": "2023-02-20 09:00:00"
        }
      ]
    }
    ```

</div>### پاسخ خطا

در صورت بروز هرگونه خطا (Exception) در طول پردازش، کد وضعیت 400 بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "status": false,
      "error": "Error message description..."
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fmail%2Fser"><div class="flowchart"><div class="flow-item">Start (GET /mail/servers/list)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Database Query**  
Table: `mail_servers`  
Where `branch` == Request input  
AND `status` == 1</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7; border: 1px solid #fbc02d;">**Data Transformation Loop**  
For each server:  
Format `service` object {title, id}</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK (JSON List)</div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Exception</div><div class="flow-item-error">Return 400 Bad Request (Error Message)</div></div></div></div>

# GET /v2/mail/address/list

# Mail: Get Address List

این اندپوینت لیستی از آدرس‌های ایمیل تعریف شده روی یک **سرور خاص** را برمی‌گرداند.   
نکته کلیدی در این اندپوینت، سیستم دسترسی است: تنها آدرس‌هایی بازگردانده می‌شوند که شناسه اپراتور درخواست‌دهنده در لیست کاربران مجاز آن آدرس (`users` JSON column) وجود داشته باشد. همچنین آدرس نهایی با ترکیب نام کاربری و دامنه سرور ساخته می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Faddres"><div class="endpoint-info"><div>**URL:** `/v2/mail/address/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailAddress</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%28%D8%B4%D9%86">- دسترسی معتبر JWT (شناسه اپراتور از توکن/درخواست استخراج می‌شود).

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>server</td><td>integer</td><td>**(الزامی)** شناسه سرور ایمیل (`mail\_servers.id`) که آدرس‌ها باید از آن خوانده شوند.</td></tr><tr><td>operator</td><td>object/id</td><td>**(ضمنی)** آبجکت اپراتور که معمولاً توسط میدل‌ور احراز هویت به درخواست تزریق می‌شود (برای بررسی دسترسی).</td></tr></tbody></table>

</div>#### Example Request

```bash
GET /v2/mail/address/list?server=5
```

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

مراحل پردازش در متد `mailAddress` به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D9%BE%D8%A7%DB%8C%D9%87%3A">1. **دریافت اطلاعات پایه:**
    - شناسه اپراتور (`operator-&gt;id`) از درخواست استخراج می‌شود.
    - اطلاعات سرور ایمیل بر اساس پارامتر `server` از جدول `mail\_servers` واکشی می‌شود (جهت دسترسی به دامنه سرور).
2. **جستجو در دیتابیس (فیلترینگ امنیتی):**   
    کوئری به جدول `mail\_address` زده می‌شود با شروط زیر: 
    - `server`: منطبق با شناسه سرور درخواستی باشد.
    - `status`: برابر با `1` (فعال) باشد.
    - `users`: این فیلد یک آرایه JSON است. سیستم با استفاده از دستور `JSON_CONTAINS` بررسی می‌کند که آیا شناسه اپراتور جاری در لیست کاربران مجاز این ایمیل وجود دارد یا خیر.
3. **آماده‌سازی خروجی (Data Mutation):**
    - فیلدهای داخلی و حساس (`users` و `status`) از شیء نتیجه حذف می‌شوند.
    - آدرس کامل ایمیل ساخته می‌شود: مقدار فیلد `address` (نام کاربری) با کاراکتر `@` و `domain` (از جدول سرور) ترکیب می‌شود.   
        *مثال:* `info` + `@` + `example.com` = `info@example.com`

</div>## Response Structure

### پاسخ موفق

لیست آدرس‌های مجاز به همراه آدرس کامل ساخته شده بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670154900,
      "data": [
        {
          "id": 12,
          "server": 5,
          "address": "support@mydomain.com", // <-- Full address constructed dynamically
          "name": "Support Team", // Other fields from DB
          "created_at": "2023-01-01 10:00:00",
          "updated_at": "2023-01-01 10:00:00"
        },
        {
          "id": 14,
          "server": 5,
          "address": "sales@mydomain.com",
          "name": "Sales Department",
          "created_at": "2023-02-01 11:00:00",
          "updated_at": "2023-02-01 11:00:00"
        }
      ]
    }
    ```

</div>### پاسخ خطا

در صورت بروز خطا (مثلاً سرور یافت نشود یا خطای دیتابیس)، پاسخ 400 بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "status": false,
      "error": "Trying to get property 'id' of non-object", // Example if server not found
      "trace": [...]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fmail%2Fadd"><div class="flowchart"><div class="flow-item">Start (GET /mail/address/list)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Fetch Server Info**  
Get `mail_servers` row by ID.  
(Retrieve Domain)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Query Addresses (Security Check)**  
From `mail_address` WHERE:  
1. `server` matches  
2. `status` = 1  
3. `JSON_CONTAINS(users, operator_id)`</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7; border: 1px solid #fbc02d;">**Format Loop**  
1. Remove `users`, `status`.  
2. `address` = `address` + '@' + `server.domain`.</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK (List)</div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Exception</div><div class="flow-item-error">Return 400 Bad Request</div></div></div></div>

# POST /v2/mail/address/store

# Mail: Create Address

این اندپوینت برای ایجاد یک حساب ایمیل جدید استفاده می‌شود.   
این متد یک فرآیند دو مرحله‌ای دارد: ابتدا درخواست ساخت اکانت را به سرویس‌دهنده خارجی (External API) ارسال می‌کند و در صورت موفقیت، اطلاعات آن را در دیتابیس محلی ذخیره کرده و به اپراتور درخواست‌دهنده تخصیص می‌دهد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Faddres"><div class="endpoint-info"><div>**URL:** `/v2/mail/address/store`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MailController@mailAddressStore</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt-%28%D8%A7%D9%84">- دسترسی معتبر JWT (الزامی برای شناسایی اپراتور سازنده).

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>service\_id</td><td>string/int</td><td>**(الزامی)** شناسه سرویس در سیستم خارجی (Third-party) که برای فراخوانی API آن استفاده می‌شود.</td></tr><tr><td>server</td><td>integer</td><td>**(الزامی)** شناسه رکورد سرور در دیتابیس داخلی (`mail\_servers.id`) برای اتصال ایمیل به سرور.</td></tr><tr><td>address</td><td>string</td><td>**(اختیاری)** نام کاربری ایمیل (بدون @domain).   
اگر ارسال نشود، سیستم به طور خودکار بر اساس نام لاتین اپراتور (`f.lastname`) آن را تولید می‌کند.</td></tr></tbody></table>

</div>## Logic Details

فرآیند متد `mailAddressStore` به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AA%D8%B9%DB%8C%DB%8C%D9%86-%D9%86%D8%A7%D9%85-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%DB%8C-%28ad">1. **تعیین نام کاربری (Address Generation):**
    - اگر پارامتر `address` ارسال شده باشد، همان استفاده می‌شود.
    - اگر ارسال نشده باشد، سیستم حرف اول `first_name_en` و کل `last_name_en` اپراتور را با نقطه ترکیب کرده و کوچک (lowercase) می‌کند. (مثال: `a.rezayi`).
2. **فراخوانی سرویس خارجی:**
    - یک درخواست `POST` به آدرس `baseUrl/{service_id}/accounts` ارسال می‌شود.
    - پارامتر ارسالی: `{ "name": mailAddress }`.
    - هدر: توکن Bearer داخلی سیستم (`$this-&gt;token`).
3. **بررسی پاسخ و ذخیره‌سازی:**
    - اگر پاسخ سرویس خارجی موفق (`status == 'success'`) باشد:   
        یک رکورد در جدول `mail_address` درج می‌شود شامل: 
        - `server`: شناسه سرور داخلی.
        - `address`: نام کاربری ایمیل.
        - `title`: نام کامل فارسی اپراتور.
        - `users`: آرایه JSON حاوی شناسه اپراتور سازنده (به عنوان مالک).
        - `avatar` و `service_id`.
    - اگر پاسخ ناموفق باشد، پیام خطای سرویس خارجی بازگردانده می‌شود.

</div>## Response Structure

### پاسخ موفق (Created)

حساب کاربری هم در سرویس خارجی و هم در دیتابیس داخلی ایجاد شده است.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670155000,
      "data": "a.username" // The created mail address
    }
    ```

</div>### پاسخ ناموفق (API Reject)

سرویس خارجی درخواست ساخت اکانت را رد کرده است (مثلاً نام کاربری تکراری است).

<div class="api-docs" id="bkmrk-status-code%3A-200-ok--1">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": false,
      "time": 1670155005,
      "message": "User already exists or invalid name." // Error from external API
    }
    ```

</div>### پاسخ خطا (Exception)

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "status": false,
      "error": "Http request failed...",
      "trace": [...]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Fmail%2Fad"><div class="flowchart"><div class="flow-item">Start (POST /mail/address/store)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has `address` Input?</div><div class="flow-arrow-label-left">No</div><div class="flow-arrow-label-right">Yes</div><div style="display: flex; justify-content: center; gap: 40px; margin: 10px 0;"><div class="flow-item-process" style="width: 180px;">Generate: lower(f.lastname)</div><div class="flow-item-process" style="width: 180px;">Use Input Address</div></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #ffe0b2;">**External API Call**  
POST /accounts  
(Payload: name)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">API Result Success?</div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #c8e6c9;">**DB Insert (mail\_address)**  
Set `users` = [operator_id]  
Set `title`, `avatar` from operator</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Status True + Address</div><div style="position: relative; top: -140px; left: 180px; width: 200px;"><div class="flow-arrow-error" style="transform: rotate(0deg);">→ (No)</div><div class="flow-item-error" style="margin-top: 5px;">Return Status False + API Message</div></div><div class="flow-error-path" style="margin-top: 20px;"><div class="flow-arrow-error">→ On Exception</div><div class="flow-item-error">Return 400 Bad Request</div></div></div></div>

# GET /v2/mail/{type}/list

# Mail: Get Mailbox List

این اندپوینت چندمنظوره برای دریافت لیست ایمیل‌ها بر اساس دسته‌بندی‌های مختلف استفاده می‌شود.   
بسته به پارامتر `type` در آدرس، سیستم می‌تواند صندوق ورودی، صندوق ارسال، پیش‌نویس‌ها، یا ایمیل‌های فیلتر شده بر اساس تگ و لیبل را بازگرداند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2F%7Btype%7D"><div class="endpoint-info"><div>**URL:** `/v2/mail/{type}/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailBox</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-description-ty"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>نوع صندوق یا فیلتر مورد نظر. مقادیر مجاز: - `inbox` (صندوق ورودی)
- `sent` (صندوق ارسال)
- **Types:** `draft`, `trash`, `spam`, `outbox`
- **Tags:** `starred`, `important`
- **Labels:** `tickets`, `personal`, `work`, `finance`, ...

</td></tr></tbody></table>

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>address\_id</td><td>integer</td><td>**(الزامی)** شناسه آدرس ایمیل در دیتابیس (`mail\_address.id`) که قصد دریافت ایمیل‌های آن را دارید.</td></tr><tr><td>service\_id</td><td>integer</td><td>**(فقط برای Inbox)** جهت سینک کردن ایمیل‌ها و ارسال به جاب صف (SnailJob).</td></tr></tbody></table>

</div>## Logic Details

منطق این متد بر اساس `type` به سه شاخه اصلی تقسیم می‌شود:

<div class="api-docs" id="bkmrk-%D8%AD%D8%A7%D9%84%D8%AA-inbox-%28%D8%B5%D9%86%D8%AF%D9%88%D9%82-%D9%88%D8%B1">1. **حالت Inbox (صندوق ورودی):**
    - ابتدا یک جاب (Job) برای همگام‌سازی ایمیل‌ها با سرویس‌دهنده خارجی (Liara Mail) در صف `snailJob` دیسپچ می‌شود (با تاخیر ۱۰ دقیقه).
    - از جدول `mail_inbox` کوئری گرفته می‌شود (شرط: `recipient` و `status=1`).
    - تعداد پیوست‌ها (Attachments) محاسبه شده و خروجی فرمت‌دهی می‌شود.
2. **حالت Sent (صندوق ارسال):**
    - از جدول `mail_sent` کوئری گرفته می‌شود (شرط: `from` و `status=1`).
    - لیست گیرندگان (`recipients`) در خروجی قرار می‌گیرد.
3. **حالت‌های فیلتر (Type/Tag/Label):**
    - سیستم ابتدا تشخیص می‌دهد که `type` درخواستی جزو کدام دسته است (Type, Tag یا Label).
    - سپس **هر دو جدول** `mail_inbox` و `mail_sent` را جستجو می‌کند.
    - این یعنی اگر شما دنبال تگ `important` باشید، هم ایمیل‌های دریافتی و هم ایمیل‌های ارسالی که این تگ را دارند بازگردانده می‌شوند.

</div>## Response Structure

### پاسخ موفق

خروجی آرایه‌ای از اشیاء ایمیل است. بسته به نوع (دریافتی/ارسالی) فیلد `sender` یا `recipients` وجود خواهد داشت.

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670156000,
      "data": [
        {
          "id": 105,
          "direction": "inbox",
          "sender": "client@gmail.com",
          "sender_name": "John Doe",
          "subject": "Project Update",
          "avatar": false,
          "attachment": 2, // Count of attachments
          "type": false,
          "tag": "starred",
          "label": "work",
          "created_at": "2023-05-20 10:00:00",
          "read_at": null
        },
        {
          "id": 88,
          "direction": "sent",
          "recipients": "boss@company.com", // Specific to sent items
          "subject": "Weekly Report",
          "attachment": 0,
          "type": false,
          "tag": false,
          "label": "finance",
          "created_at": "2023-05-19 14:00:00"
        }
      ]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fmail%2F%7Bty"><div class="flowchart"><div class="flow-item">Start (GET /mail/{type}/list)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Check {type}</div><div style="position: relative; left: -200px; top: 20px;"><div class="flow-arrow-label-left" style="top: -30px; left: 80px;">Inbox</div><div class="flow-item-process" style="width: 160px; background-color: #ffe0b2;">**Dispatch Job**  
Sync Mail (10m delay)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 160px;">Query `mail_inbox`  
Where recipient = ID</div></div><div style="position: relative; left: 0px; top: -140px;"><div class="flow-arrow-label-right" style="top: 80px; left: -10px;">Sent</div><div class="flow-item-process" style="width: 160px; margin-top: 110px;">Query `mail_sent`  
Where from = ID</div></div><div style="position: relative; left: 200px; top: -330px;"><div class="flow-arrow-label-right" style="top: 80px; left: -40px;">Other (Tag/Label)</div><div class="flow-item-process" style="width: 180px; margin-top: 110px; background-color: #e3f2fd;">**Dual Query**  
Query `mail_inbox` AND `mail_sent`  
Where key = {type}</div></div><div class="flow-arrow" style="margin-top: -100px;">↓</div><div class="flow-item-process" style="background-color: #fffde7; border: 1px solid #fbc02d;">**Format &amp; Merge**  
Calculate Attachments Count  
Standardize Fields</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Data</div></div></div>

# POST /v2/mail/{type}/update

# Mail: Update Attributes

این اندپوینت یک متد عمومی و داینامیک برای ویرایش ویژگی‌های یک ایمیل است.   
با استفاده از این متد می‌توان وضعیت‌هایی مانند "خوانده شده" (`read\_at`)، برچسب‌ها (`label`)، تگ‌ها (`tag`) یا هر ستون دیگری از ایمیل را در صندوق ورودی یا ارسالی تغییر داد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2F%7Btype%7D"><div class="endpoint-info"><div>**URL:** `/v2/mail/{type}/update`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MailController@mailUpdate</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-description-ty"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>نوع صندوق پستی که ایمیل در آن قرار دارد (نام جدول را تعیین می‌کند). - `inbox`: عملیات روی جدول `mail_inbox`
- `sent`: عملیات روی جدول `mail_sent`

</td></tr></tbody></table>

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه منحصر‌به‌فرد ایمیل (`id`) که قصد ویرایش آن را دارید.</td></tr><tr><td>key</td><td>string</td><td>**(الزامی)** نام ستون (Column) در دیتابیس که باید تغییر کند.   
مثال: `read\_at`, `tag`, `label`, `type`.</td></tr><tr><td>value</td><td>mixed</td><td>**(الزامی)** مقدار جدیدی که باید در ستون انتخاب شده قرار گیرد.</td></tr></tbody></table>

</div>## Logic Details

منطق این متد بسیار مستقیم است:

<div class="api-docs" id="bkmrk-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-%D8%AC%D8%AF%D9%88%D9%84-%D9%87%D8%AF%D9%81%3A-%D8%B3%DB%8C%D8%B3%D8%AA">1. **تشخیص جدول هدف:** سیستم نام جدول را با الحاق پیشوند `mail_` به پارامتر `type` آدرس می‌سازد (مثلاً `mail\_inbox`).
2. **اجرای آپدیت:** یک کوئری Update روی جدول انتخاب شده اجرا می‌شود که در آن رکوردی با شناسه `mail_id` پیدا شده و مقدار ستون `key` با `value` جایگزین می‌شود.
3. **موارد استفاده معمول:**
    - **تغییر وضعیت خواندن:** ارسال `key: read\_at` و `value: (timestamp)`.
    - **ستاره‌دار کردن:** ارسال `key: tag` و `value: starred`.
    - **تغییر لیبل:** ارسال `key: label` و `value: work`.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670157000
    }
    ```

</div>### پاسخ خطا

در صورت بروز خطا (مثلاً نام ستون اشتباه باشد):

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- **Body:**```json
    {
      "status": false,
      "error": "SQLSTATE[42S22]: Column not found...",
      "trace": [...]
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Fmail%2F%7Bt"><div class="flowchart"><div class="flow-item">Start (POST /mail/{type}/update)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Determine Table**  
Table = "mail_" + {type}</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 200px;">**Construct Query**  
UPDATE [Table]  
SET [key] = [value]  
WHERE id = [mail_id]</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Update Successful?</div><div class="flow-arrow-label-left">No (Exception)</div><div class="flow-arrow-label-right">Yes</div><div style="display: flex; justify-content: center; gap: 40px; margin: 10px 0;"><div class="flow-item-error">Return 400 + Error Trace</div><div class="flow-item-success">Return Status True</div></div></div></div>

# POST /v2/mail/attachments/download

# Mail: Download Attachment

این اندپوینت وظیفه دریافت لینک دانلود فایل‌های پیوست ایمیل را بر عهده دارد.   
برای ایمیل‌های ورودی (Inbox)، از استراتژی **Lazy Download** استفاده می‌شود: فایل ابتدا روی سرور ایمیل خارجی است؛ اگر اولین بار باشد که درخواست می‌شود، فایل دانلود شده، در S3 ذخیره می‌شود و لینک آن در دیتابیس ثبت می‌گردد. در درخواست‌های بعدی، لینک مستقیم S3 بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Fattach"><div class="endpoint-info"><div>**URL:** `/v2/mail/attachments/download`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MailController@mailAttachmentsDownload</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه رکورد پیوست در جدول لوکال (`mail\_attachment.id`).</td></tr><tr><td>service\_id</td><td>integer</td><td>**(الزامی برای Inbox)** شناسه سرویس ایمیل جهت ارتباط با API خارجی.</td></tr><tr><td>mail\_id</td><td>string</td><td>**(الزامی برای Inbox)** شناسه پیام در سرور خارجی (Message ID).</td></tr><tr><td>attach\_id</td><td>string</td><td>**(الزامی برای Inbox)** شناسه پیوست در سرور خارجی.</td></tr><tr><td>branch</td><td>string</td><td>**(الزامی)** نام یا شناسه برنچ جهت ساختن مسیر ذخیره‌سازی فایل در S3 (مثلاً: `uploads/{branch}/mailbox/...`).</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A8%D8%A7%D8%B2%DB%8C%D8%A7%D8%A8%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%3A-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7">1. **بازیابی رکورد:** ابتدا رکورد پیوست از جدول `mail_attachment` بر اساس `id` خوانده می‌شود.
2. **بررسی جهت (Direction):**
    - **اگر Sent (ارسالی) باشد:** فایل قبلاً هنگام ارسال آپلود شده است. لینک موجود در ستون `storage` مستقیماً بازگردانده می‌شود.
    - **اگر Inbox (دریافتی) باشد:**
        - **بررسی کش (Cache Check):** سیستم چک می‌کند آیا فیلد `storage` مقدار دارد (Not Null)؟ 
            - **بله:** فایل قبلاً دانلود شده است. لینک S3 بازگردانده می‌شود.
            - **خیر (دانلود اولیه):**
                1. یک درخواست به API سرویس‌دهنده ایمیل ارسال می‌شود (با استفاده از `service\_id`, `mail\_id`, `attach\_id`).
                2. محتوای فایل (Base64) دریافت می‌شود.
                3. فایل در S3 (فضای ابری) آپلود می‌شود.
                4. لینک فایل جدید در ستون `storage` رکورد دیتابیس آپدیت می‌شود تا در دفعات بعدی دانلود نشود.
                5. لینک نهایی به کاربر ارسال می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK` (یا بازگشت آرایه موفق)
- **Body:**```json
    {
      "status": true,
      "time": 1670158000,
      "link": "https://storage.bucket.com/uploads/main/mailbox/2023/05/file.pdf"
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Fattachm"><div class="flowchart"><div class="flow-item">Start (POST /attachments/download)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch `mail_attachment` by ID</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Direction?</div><div style="position: relative; left: -180px; top: 20px;"><div class="flow-arrow-label-left" style="top: -30px; left: 80px;">Sent</div><div class="flow-item-success" style="width: 140px; margin-top: 20px;">Return `storage`  
(Existing Link)</div></div><div style="position: relative; left: 180px; top: -100px;"><div class="flow-arrow-label-right" style="top: 80px; left: -30px;">Inbox</div><div class="flow-item-decision" style="margin-top: 110px;">Storage Exists?</div><div style="position: relative; left: -140px; top: 10px;"><div class="flow-arrow-label-left" style="top: -25px; left: 60px;">Yes</div><div class="flow-item-success" style="width: 120px;">Return Link</div></div><div style="position: relative; left: 10px; top: 30px;"><div class="flow-arrow-label-right" style="top: -55px; left: 10px;">No (Null)</div><div class="flow-item-process" style="width: 180px; background-color: #ffe0b2;">**Fetch External API**  
Get Base64 Content</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 180px; background-color: #c8e6c9;">**Upload to S3**  
Update DB `storage`</div><div class="flow-arrow">↓</div><div class="flow-item-success" style="width: 140px;">Return New Link</div></div></div></div></div>

# GET /v2/mail/sent/get

# Mail: Get Sent Details

این اندپوینت برای دریافت جزئیات کامل یک ایمیل **ارسال شده** استفاده می‌شود.   
برخلاف لیست‌ها که اطلاعات خلاصه‌ای ارائه می‌دهند، این متد متن کامل بدنه ایمیل (`content`)، لیست کامل گیرندگان (CC/BCC) و آرایه کامل فایل‌های پیوست را باز می‌گرداند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Fsent%2Fg"><div class="endpoint-info"><div>**URL:** `/v2/mail/sent/get`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailSentGet</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه منحصر‌به‌فرد ایمیل در جدول `mail\_sent`.</td></tr></tbody></table>

</div>## Logic Details

فرآیند واکشی اطلاعات به صورت زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%A7%DB%8C%D9%85%DB%8C%D9%84-%D8%A7%D8%B5%D9%84%DB%8C%3A-%D8%B3%DB%8C">1. **واکشی ایمیل اصلی:** سیستم در جدول `mail_sent` به دنبال رکوردی می‌گردد که `id` آن برابر ورودی باشد و `status=1` (فعال) باشد.
2. **واکشی پیوست‌ها:** همزمان، تمام رکوردهای جدول `mail_attachment` که `mail_id` آن‌ها منطبق است و جهت (`direction`) آن‌ها برابر با `sent` است، استخراج می‌شوند.
3. **فرمت‌دهی داده‌ها:**
    - فیلد `tag` به صورت بولین برگردانده می‌شود (اگر تگ داشته باشد `true`، در غیر این صورت `false`).
    - فیلد `label` اگر موجود باشد مقدار رشته‌ای آن (مثلا "work") و اگر نباشد `false` برگردانده می‌شود.
    - آرایه `attachment` حاوی آبجکت‌های کامل فایل‌های پیوست است.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670160000,
      "data": {
        "id": 88,
        "direction": "sent",
        "from": "support@mydomain.com",
        "recipients": "client@gmail.com",
        "cc": "manager@mydomain.com",
        "bcc": null,
        "avatar": false,
        "attachment": [
            {
                "id": 10,
                "name": "invoice.pdf",
                "size": "1024kb",
                "storage": "https://s3.bucket..."
            }
        ],
        "subject": "Invoice #12345",
        "content": "Please find attached...",
        "service_id": 1,
        "type": false,
        "tag": true,        // Boolean: has tag or not
        "label": "finance", // String or false
        "created_at": "2023-06-15 09:30:00"
      }
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Fmail%2Fsen"><div class="flowchart"><div class="flow-item">Start (GET /mail/sent/get)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Query `mail\_sent`**  
Where ID = request.id  
AND Status = 1</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Query `mail\_attachment`**  
Where mail_id = request.id  
AND direction = 'sent'</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 200px; border: 1px solid #ff9800; background-color: #fff3e0;">**Format Response**  
Convert Tag to Bool  
Handle Label Nulls  
Attach Attachments Array</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Data</div></div></div>

# POST /v2/mail/sent/store

# Mail: Store &amp; Send Email

این اندپوینت وظیفه ذخیره‌سازی و ارسال ایمیل را بر عهده دارد.   
عملکرد این متد وابسته به پارامتر `type` است: اگر `outbox` باشد، ایمیل علاوه بر ذخیره شدن در دیتابیس، با استفاده از تنظیمات SMTP پویا (Dynamic SMTP) بلافاصله ارسال می‌شود. در غیر این صورت (مثلاً پیش‌نویس)، صرفاً ذخیره می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Fsent%2Fs"><div class="endpoint-info"><div>**URL:** `/v2/mail/sent/store`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MailController@mailSentStore</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>from</td><td>integer</td><td>**(الزامی)** شناسه آدرس فرستنده (ID از جدول `mail\_address`). جهت واکشی تنظیمات SMTP استفاده می‌شود.</td></tr><tr><td>type</td><td>string</td><td>**(الزامی)** نوع عملیات. مقدار `outbox` باعث ارسال واقعی ایمیل می‌شود.</td></tr><tr><td>to</td><td>string/array</td><td>**(الزامی)** آدرس گیرنده(ها).</td></tr><tr><td>subject</td><td>string</td><td>**(الزامی)** موضوع ایمیل.</td></tr><tr><td>content</td><td>html/string</td><td>**(الزامی)** متن بدنه ایمیل.</td></tr><tr><td>cc</td><td>string/array</td><td>(اختیاری) گیرندگان رونوشت.</td></tr><tr><td>bcc</td><td>string/array</td><td>(اختیاری) گیرندگان رونوشت مخفی.</td></tr><tr><td>attachments</td><td>array\[string\]</td><td>(اختیاری) آرایه‌ای از مسیر فایل‌های آپلود شده (روی دیسک Liara) جهت پیوست.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%A7%D9%88%D9%84%DB%8C%D9%87-%28persist">1. **ذخیره اولیه (Persist):** اطلاعات اولیه ایمیل (گیرندگان، موضوع، متن) در جدول `mail_sent` درج شده و `mail_id` دریافت می‌شود.
2. **پردازش پیوست‌ها (DB):**
    - اگر آرایه `attachments` ارسال شده باشد، سیستم روی آن‌ها حلقه می‌زند.
    - متادیتای هر فایل (mime, size, url) از دیسک ابری (Liara) استخراج می‌شود.
    - اطلاعات در جدول `mail_attachment` با جهت `sent` ذخیره می‌شود.
3. **منطق ارسال (SMTP):** اگر `type == 'outbox'` باشد: 
    1. **واکشی تنظیمات:** بر اساس `from` (شناسه آدرس)، اطلاعات سرور (Host, Port, User, Pass) و برند (Brand/Title, Signature) از جداول `mail_address`, `mail_servers`, `offices` استخراج می‌شود.
    2. **کانفیگ پویا (Dynamic Config):** تنظیمات `mail.mailers.smtp` در لاراول به صورت Runtime با اطلاعات واکشی شده جایگزین می‌شود.
    3. **ارسال:**
        - امضا (Signature) در صورت وجود به انتهای متن اضافه می‌شود.
        - هدر `From` با ترکیب آدرس+دامنه و عنوان+برند تنظیم می‌شود.
        - فایل‌های پیوست مجدداً از دیسک خوانده شده و به ایمیل ضمیمه می‌شوند (`$message-&gt;attach`).
        - ایمیل ارسال می‌گردد.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670165000
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28post-%2Fsent%2Fst"><div class="flowchart"><div class="flow-item">Start (POST /sent/store)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Insert into `mail_sent`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has Attachments?</div><div style="position: relative; left: -140px; top: 10px;"><div class="flow-arrow-label-left" style="top: -25px; left: 60px;">Yes</div><div class="flow-item-process" style="width: 140px; background-color: #e0f2f1;">Loop Attachments  
Get Meta from Disk  
Insert `mail_attachment`</div></div><div style="position: relative; top: 10px;"><div class="flow-arrow-label-right" style="top: -25px; left: 10px;">No</div><div class="flow-arrow" style="height: 50px;">↓</div></div><div class="flow-item-decision" style="margin-top: 80px;">Type == 'outbox'?</div><div style="position: relative; left: -160px; top: 20px;"><div class="flow-arrow-label-left" style="top: -30px; left: 70px;">No</div><div class="flow-item-success" style="width: 120px; background-color: #f5f5f5; color: #333;">End (Saved Only)</div></div><div style="position: relative; left: 160px; top: -60px;"><div class="flow-arrow-label-right" style="top: 50px; left: -30px;">Yes</div><div class="flow-item-process" style="margin-top: 80px; width: 220px; background-color: #fff8e1;">**Fetch SMTP Credentials**  
(Join address, servers, offices)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 220px; background-color: #fff3e0;">**Set Runtime Config**  
Config::set('mail.smtp', ...)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 220px; background-color: #c8e6c9;">**Send Mail**  
Add Signature + Attachments</div><div class="flow-arrow">↓</div><div class="flow-item-success" style="width: 120px;">End (Sent)</div></div></div></div>

# DELETE /v2/mail/sent/trash

# Mail: Move Sent to Trash

این اندپوینت برای انتقال یک ایمیل ارسال شده به "زباله‌دان" (Trash) استفاده می‌شود.   
این عملیات یک **حذف نرم (Soft Delete)** است؛ رکورد فیزیکی از دیتابیس پاک نمی‌شود، بلکه فیلد `type` آن به `trash` تغییر می‌کند تا در لیست‌های عادی نمایش داده نشود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Fsent%2Ft"><div class="endpoint-info"><div>**URL:** `/v2/mail/sent/trash`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** MailController@mailSentTrash</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Body/Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه ایمیل مورد نظر در جدول `mail\_sent`.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش بسیار ساده است:

<div class="api-docs" id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA%3A-%DA%A9">1. **به‌روزرسانی وضعیت:** کوئری آپدیت روی جدول `mail_sent` اجرا می‌شود.
2. **شرط:** رکوردی که `id` آن برابر با `mail_id` ورودی باشد.
3. **تغییر:** مقدار ستون `type` به رشته `trash` تغییر می‌یابد.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670168000
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Fmail%2F"><div class="flowchart"><div class="flow-item">Start (DELETE /mail/sent/trash)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px; background-color: #ffebee;">**Update `mail\_sent`**  
SET type = 'trash'  
WHERE id = request.mail_id</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success</div></div></div>

# GET /v2/mail/inbox/get

# Mail: Get Inbox Details

این اندپوینت برای دریافت جزئیات کامل یک ایمیل ورودی (Inbox) استفاده می‌شود.   
اطلاعات شامل متن کامل ایمیل، فایل‌های پیوست، امتیاز اسپم و متادیتای فرستنده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Finbox%2F"><div class="endpoint-info"><div>**URL:** `/v2/mail/inbox/get`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailInboxGet</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه منحصر‌به‌فرد ایمیل در جدول `mail\_inbox`.</td></tr></tbody></table>

</div>## Logic Details

فرآیند واکشی اطلاعات به صورت زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%A7%DB%8C%D9%85%DB%8C%D9%84%3A-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%AF">1. **واکشی ایمیل:** جستجو در جدول `mail_inbox` با شرط `id = mail_id` و `status = 1` (فعال).
2. **واکشی پیوست‌ها:** جستجو در جدول `mail_attachment` با شرط `mail_id` و جهت `direction = 'inbox'`.
3. **آماده‌سازی داده‌ها:**
    - مقدار `avatar` همیشه `false` بازگردانده می‌شود.
    - مقادیر `tag` و `label` در صورت `null` بودن در دیتابیس، به مقدار `false` تبدیل می‌شوند.
    - شامل اطلاعات تحلیل اسپم (`spam\_score`, `spam\_reason`) است.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670170000,
      "data": {
        "id": 105,
        "direction": "inbox",
        "sender": "example@domain.com",
        "sender_name": "John Doe",
        "reply_to": "reply@domain.com",
        "subject": "Weekly Report",
        "avatar": false,
        "attachment": [
          {
            "id": 12,
            "mail_id": 105,
            "file_name": "report.pdf",
            "url": "https://storage..."
          }
        ],
        "content": "<html>...</html>",
        "service_id": 1,
        "type": "text/html",
        "tag": false,
        "label": "Work",
        "spam_score": 0.1,
        "spam_reason": "Pass",
        "created_at": "2023-10-10 10:00:00",
        "read_at": null
      }
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Finbox%2Fge"><div class="flowchart"><div class="flow-item">Start (GET /inbox/get)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px;">**Query Mail Inbox**  
WHERE id = request.mail_id  
AND status = 1</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Found?</div><div style="position: relative; left: -140px; top: 10px;"><div class="flow-arrow-label-left" style="top: -25px; left: 60px;">No (Exception)</div><div class="flow-item-error" style="width: 140px;">Return 400 Error</div></div><div style="position: relative; top: 10px;"><div class="flow-arrow-label-right" style="top: -25px; left: 10px;">Yes</div><div class="flow-arrow" style="height: 40px;">↓</div></div><div class="flow-item-process" style="margin-top: 60px; width: 250px; background-color: #e3f2fd;">**Fetch Attachments**  
FROM `mail_attachment`  
WHERE mail_id AND direction='inbox'</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px; background-color: #fff3e0;">**Format Response**  
Handle Nulls (tag/label)  
Set Avatar False</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Data</div></div></div></body></html>

# GET /v2/mail/inbox/get

# Mail: Get Inbox Details

این اندپوینت برای دریافت جزئیات کامل یک ایمیل ورودی (Inbox) استفاده می‌شود.   
اطلاعات شامل متن کامل ایمیل، فایل‌های پیوست، امتیاز اسپم و متادیتای فرستنده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Finbox%2F"><div class="endpoint-info"><div>**URL:** `/v2/mail/inbox/get`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MailController@mailInboxGet</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه منحصر‌به‌فرد ایمیل در جدول `mail\_inbox`.</td></tr></tbody></table>

</div>## Logic Details

فرآیند واکشی اطلاعات به صورت زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%A7%DB%8C%D9%85%DB%8C%D9%84%3A-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%AF">1. **واکشی ایمیل:** جستجو در جدول `mail_inbox` با شرط `id = mail_id` و `status = 1` (فعال).
2. **واکشی پیوست‌ها:** جستجو در جدول `mail_attachment` با شرط `mail_id` و جهت `direction = 'inbox'`.
3. **آماده‌سازی داده‌ها:**
    - مقدار `avatar` همیشه `false` بازگردانده می‌شود.
    - مقادیر `tag` و `label` در صورت `null` بودن در دیتابیس، به مقدار `false` تبدیل می‌شوند.
    - شامل اطلاعات تحلیل اسپم (`spam\_score`, `spam\_reason`) است.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670170000,
      "data": {
        "id": 105,
        "direction": "inbox",
        "sender": "example@domain.com",
        "sender_name": "John Doe",
        "reply_to": "reply@domain.com",
        "subject": "Weekly Report",
        "avatar": false,
        "attachment": [
          {
            "id": 12,
            "mail_id": 105,
            "file_name": "report.pdf",
            "url": "https://storage..."
          }
        ],
        "content": "<html>...</html>",
        "service_id": 1,
        "type": "text/html",
        "tag": false,
        "label": "Work",
        "spam_score": 0.1,
        "spam_reason": "Pass",
        "created_at": "2023-10-10 10:00:00",
        "read_at": null
      }
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28get-%2Finbox%2Fge"><div class="flowchart"><div class="flow-item">Start (GET /inbox/get)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px;">**Query Mail Inbox**  
WHERE id = request.mail_id  
AND status = 1</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Found?</div><div style="position: relative; left: -140px; top: 10px;"><div class="flow-arrow-label-left" style="top: -25px; left: 60px;">No (Exception)</div><div class="flow-item-error" style="width: 140px;">Return 400 Error</div></div><div style="position: relative; top: 10px;"><div class="flow-arrow-label-right" style="top: -25px; left: 10px;">Yes</div><div class="flow-arrow" style="height: 40px;">↓</div></div><div class="flow-item-process" style="margin-top: 60px; width: 250px; background-color: #e3f2fd;">**Fetch Attachments**  
FROM `mail_attachment`  
WHERE mail_id AND direction='inbox'</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px; background-color: #fff3e0;">**Format Response**  
Handle Nulls (tag/label)  
Set Avatar False</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Data</div></div></div></body></html>

# DELETE /v2/mail/inbox/trash

# Mail: Move Inbox to Trash

این اندپوینت برای انتقال یک ایمیل ورودی (Inbox) به "زباله‌دان" (Trash) استفاده می‌شود.   
مشابه اندپوینت بخش ارسالی‌ها، این عملیات نیز یک **حذف نرم (Soft Delete)** است و رکورد فیزیکی از دیتابیس پاک نمی‌شود؛ تنها فیلد `type` آن به `trash` تغییر می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Finbox%2F"><div class="endpoint-info"><div>**URL:** `/v2/mail/inbox/trash`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** MailController@mailInboxTrash</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Body/Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه ایمیل مورد نظر در جدول `mail\_inbox`.</td></tr></tbody></table>

</div>## Logic Details

مراحل پردازش:

<div class="api-docs" id="bkmrk-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3%3A">1. **به‌روزرسانی دیتابیس:** کوئری آپدیت روی جدول `mail_inbox` اجرا می‌شود.
2. **شرط:** رکوردی که `id` آن برابر با `mail_id` ورودی باشد.
3. **تغییر وضعیت:** مقدار ستون `type` به رشته `trash` تغییر می‌یابد تا از صندوق ورودی اصلی مخفی شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670175000
    }
    ```

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Fmail%2F"><div class="flowchart"><div class="flow-item">Start (DELETE /mail/inbox/trash)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 250px; background-color: #e0f7fa;">**Update `mail\_inbox`**  
SET type = 'trash'  
WHERE id = request.mail_id</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success</div></div></div>

# DELETE /v2/mail/trash/delete

# Mail: Permanent Delete (Trash)

این اندپوینت برای **حذف فیزیکی و دائمی** یک ایمیل استفاده می‌شود.   
معمولاً زمانی فراخوانی می‌شود که کاربر بخواهد آیتمی را از پوشه "زباله‌دان" (Trash) به طور کامل پاک کند. برخلاف اندپوینت‌های قبلی که فقط برچسب (Type) را تغییر می‌دادند، این متد رکورد را از دیتابیس حذف می‌کند (Hard Delete).

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmail%2Ftrash%2F"><div class="endpoint-info"><div>**URL:** `/v2/mail/trash/delete`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** MailController@mailTrashDelete</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.">- دسترسی معتبر JWT.

</div>## Body/Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>mail\_id</td><td>integer</td><td>**(الزامی)** شناسه ایمیل مورد نظر برای حذف.</td></tr><tr><td>type</td><td>string</td><td>**(الزامی)** مشخص‌کننده جدول مبدا.   
مقادیر مورد انتظار: - `inbox`: حذف از جدول `mail_inbox`
- `sent`: حذف از جدول `mail_sent`

</td></tr></tbody></table>

</div>## Logic Details

مراحل پردازش به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A7%D9%86%D8%AA%D8%AE%D8%A7%D8%A8-%D8%AC%D8%AF%D9%88%D9%84-%D9%BE%D9%88%DB%8C%D8%A7%3A-%D9%86%D8%A7">1. **انتخاب جدول پویا:** نام جدول هدف با ترکیب پیشوند `mail_` و پارامتر `type` ساخته می‌شود (مثلاً `mail_inbox` یا `mail_sent`).
2. **حذف رکورد:** دستور `DELETE` روی جدول انتخاب شده با شرط `id = mail_id` اجرا می‌شود.
3. **نکته امنیتی/فنی:** از آنجا که نام جدول به صورت پویا ساخته می‌شود، فرانت‌اند باید دقیقاً مقدار `inbox` یا `sent` را ارسال کند. ارسال مقادیر اشتباه ممکن است منجر به خطای SQL یا تلاش برای حذف از جداول نادرست شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**```json
    {
      "status": true,
      "time": 1670180000
    }
    ```

</div>### پاسخ خطا

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request` (در صورت بروز خطا در دیتابیس)

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-%28delete-%2Ftrash"><div class="flowchart"><div class="flow-item">Start (DELETE /trash/delete)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 280px; background-color: #fff3e0;">**Determine Table Name**  
Table = "mail_" + request.type  
(e.g., mail_inbox / mail_sent)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="width: 280px; background-color: #ffcdd2;">**Hard Delete Query**  
DELETE FROM {Table}  
WHERE id = request.mail_id</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success</div></div></div>

# GET /v2/modules/lottery/registrations

# Module: Lottery Registrations List

این اندپوینت برای دریافت لیست ثبت‌نام‌کنندگان در ماژول قرعه‌کشی (Lottery) استفاده می‌شود.   
این سرویس لیست فاکتورهایی را برمی‌گرداند که شامل آیتم خدمات با شناسه خاص (۱۷) هستند و محاسبات مالی سنگینی (سود، زیان، تراز دریافتی و پرداختی) برای هر ردیف انجام می‌دهد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmodules%2Flot"><div class="endpoint-info"><div>**URL:** `/v2/modules/lottery/registrations`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ModuleController@lotteryRegistrations</div><div>**Middleware Stack:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D9%85%D8%B9%D8%AA%D8%A8%D8%B1-jwt.-%D8%AF%D8%B3">- دسترسی معتبر JWT.
- دسترسی به `branch` ارسال شده.

</div>## Query Parameters

**توجه:** پارامترها به صورت آرایه `json` و پارامتر مجزای `branch` ارسال می‌شوند.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>**(الزامی - سطح بالا)** شناسه شعبه مورد نظر.</td></tr><tr><td>json\[length\]</td><td>integer</td><td>تعداد رکورد در هر صفحه (Pagination Limit).</td></tr><tr><td>json\[start\]</td><td>integer</td><td>آفست شروع (Pagination Offset).</td></tr><tr><td>json\[draw\]</td><td>integer</td><td>شمارنده درخواست (جهت هماهنگی DataTables).</td></tr><tr><td>json\[advanced\]\[r\]</td><td>integer</td><td>**(اختیاری)** فیلتر جستجو بر اساس شماره سریال فاکتور (سیستم به طور خودکار ۱۰,۰۰۰ واحد برای تطبیق با دیتابیس از آن کم می‌کند).</td></tr></tbody></table>

</div>## Logic Details

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

<div class="api-docs" id="bkmrk-%D8%AA%D9%86%D8%B8%DB%8C%D9%85-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C%3A-%D9%85%D9%86%D8%B7">1. **تنظیم صفحه‌بندی:** منطق صفحه‌بندی لاراول (`paginate`) با محاسبه `currentPage` بر اساس `start` و `length` تنظیم می‌شود.
2. **فیلترینگ سخت‌گیرانه (Hardcoded):**
    - `factors.operator = 1`: فقط فاکتورهای مربوط به اپراتور با شناسه ۱ (احتمالاً سیستم یا ادمین اصلی).
    - `factor_items.details->id = 17`: شناسه ۱۷ در JSON جزئیات، نشان‌دهنده سرویس "قرعه‌کشی" است.
    - `product = 'service'`: نوع آیتم باید خدمت باشد.
    - وضعیت فاکتور نباید ۲ (ابطال) یا ۵ (احتمالاً پیش‌نویس حذف شده) باشد.
3. **محاسبات مالی (Financial Analysis):** برای هر رکورد یافت شده، تابع استاتیک `TradeController::financial` فراخوانی می‌شود. این تابع موارد زیر را محاسبه می‌کند: 
    - **مبالغ کل:** جمع خرید، فروش، مرجوعی و جریمه‌ها.
    - **اسناد مالی:** بررسی جدول `pays` برای دریافت‌ها، پرداخت‌ها و اسناد تجمیعی (Aggregation).
    - **تراز (Balance):** محاسبه مانده بدهکاری/بستانکاری مسافر (`BalanceReceived`) و تامین‌کننده (`BalancePaid`).
    - **سود (Profit):** محاسبه دقیق سود با کسر تخفیفات و کارمزدها.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:** شامل آرایه `data` با کلیدهای `snake\_case` (به دلیل تنظیمات کنترلر).

</div>```json
{
  "status": true,
  "time": 1715000000,
  "draw": 1,
  "recordsTotal": 50,
  "data": [
    {
      "id": 101,                  // شناسه آیتم فاکتور
      "system_id": 5050,          // شناسه فاکتور
      "serial": 1001,             // سریال خام دیتابیس
      "details": {                // جزئیات سرویس قرعه‌کشی
        "id": 17,
        "name": "Lottery Name"
      },
      "customer_id": 200,
      "sale": 5000000,
      "discount": false,          // یا آبجکت تخفیف
      "status": 1,
      "description": "توضیحات فاکتور",
      "financial": {              // خروجی تابع Financial با فرمت snake_case
        "operator": {
          "title": "Admin User",
          "id": 1
        },
        "leader": {
          "id": 200,
          "title_fa": "علی محمدی",
          "mobile": "0912..."
        },
        "financial": {
          "sum_passenger": 1,
          "sum_buy": 4000000,
          "sum_sale": 5000000,
          "sum_receive": 5000000,
          "balance_received": 0,  // تراز مالی مسافر (0 یعنی تسویه)
          "balance_paid": -4000000, // تراز مالی تامین کننده
          "profit": 1000000,      // سود خالص
          "percent_profit": 25
        },
        "suppliers": { ... },     // لیست تامین‌کنندگان
        "serial_id": 11001        // سریال نمایشی (10000 + serial)
      }
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%E2%86%93-prepare-quer"><div class="flowchart"><div class="flow-item">Start</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Prepare Query**  
Join `factors` &amp; `factor_items`  
Filter: Branch, Operator=1, ItemID=17</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Search Param (r)?</div><div style="position: relative; height: 40px;"><div class="flow-arrow-label-right" style="top: -10px;">Yes</div><div class="flow-arrow-label-left" style="top: -10px;">No</div></div><div style="display: flex; justify-content: space-between; width: 300px; margin: 0 auto;"><div class="flow-item-process" style="width: 120px; font-size: 10px;">Filter Serial = (r - 10,000)</div><div class="flow-item-process" style="width: 120px; font-size: 10px;">No Filter</div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Execute Pagination</div><div class="flow-arrow">↓</div><div style="border: 2px dashed #ff9800; padding: 10px; border-radius: 5px; margin: 10px 0;">**Loop through Results**<div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Calculate Financials**  
(TradeController::financial)  
Calc Sums, Balances, Profit</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Discount Info</div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>

# POST /v2/trade/tickets

# Trade: Tickets &amp; Vouchers Data

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftrade%2Fticke"><div class="endpoint-info"><div>**URL:** `/v2/trade/tickets`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2TradeController@ticketsTrade</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%88%D9%85%DB%8C-%28public">- دسترسی عمومی (Public) یا محدود شده (در کد میدلوری مشخص نشده اما احتمالا نیاز به توکن دارد).
- نیاز به ارسال `branch` معتبر.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه برای فیلتر کردن فاکتورها.</td></tr><tr><td>id</td><td>mixed</td><td>**(الزامی)** شناسه سند مورد نظر. رفتار این فیلد دوگانه است: - **Array/Object:** اگر آرایه باشد، به عنوان لیست `id`های جدول `factors` در نظر گرفته می‌شود.
- **Single Value:** اگر تک مقدار باشد، به عنوان **شماره سریال نمایشی** در نظر گرفته می‌شود و سیستم به طور خودکار `ReferenceExtension` را از آن کم می‌کند تا سریال دیتابیس (`serial`) را پیدا کند.

</td></tr><tr><td>lang\[id\]</td><td>string</td><td>**(اختیاری)** کد زبان (پیش‌فرض: `fa`).</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش این سرویس شامل مراحل زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%B1%D9%81%D8%B1%D9%86%D8%B3-%28factor%29">1. **واکشی رفرنس (Factor):** اطلاعات پایه فاکتور با Join به جداول `operators` و `customers` دریافت می‌شود.
2. **بررسی قابلیت چاپ:**
    - اگر `print == 0` باشد، خطای "عدم قابلیت چاپ" برمی‌گرداند.
    - اگر `status == 5` باشد، خطای وضعیت همراه با توضیحات فاکتور برمی‌گرداند.
3. **تعیین حالت نمایش (Print Mode):**
    - `1`: مشاهده بدون قیمت.
    - `2`: مشاهده با قیمت.
    - `3`: عدم مشاهده بلیت و واچر (مسدود).
4. **پردازش آیتم‌ها (Factor Items):**
    - **بررسی وضعیت آیتم:** آیتم‌های `refund` همیشه بررسی می‌شوند، اما آیتم‌های `online` بر اساس فیلد JSON `Status` بررسی می‌شوند.
    - **استخراج مسافران:**
        - اگر سرویس هتل (`accommodation`) باشد، لیست `roommate` از JSON استخراج می‌شود.
        - اطلاعات کامل مسافر از `customers` واکشی می‌شود.
        - **کشینگ (Redis):** اطلاعات کشور (`countries`) برای هر مسافر در Redis کش می‌شود تا فشار بر دیتابیس کاهش یابد.
    - **استخراج تامین‌کننده:** اطلاعات `colleagues` با استفاده از Redis کش و بازیابی می‌شود.
    - **ساختاردهی خروجی:** آیتم‌ها بر اساس نوع (`product`/`byproduct`) و شناسه مسافر گروه‌بندی می‌شوند. آیتم‌های استردادی (`refund`) به صورت تو در تو داخل آیتم اصلی قرار می‌گیرند.
5. **اطلاعات تماس (Branding):** اگر فیلد `colleague\_auth` مقدار داشته باشد، اطلاعات آژانس همکار (لوگو، آدرس، تلفن) جایگزین اطلاعات پیش‌فرض می‌شود.

</div>## Response Structure

**نکته مهم:** در کد فعلی، حتی در صورت موفقیت‌آمیز بودن عملیات و بازگشت دیتا، مقدار `status` برابر با `false` و کد `5008` برگردانده می‌شود (احتمالاً یک استاندارد داخلی یا لگاسی کد).

```json
{
  "status": false,  // ! توجه: طبق کد موجود فالس برمی‌گرداند
  "code": 5008,
  "data": [
    {
      "confirmation": 1,
      "serial_id": 100500,
      "track_code": "09-1200", // فرمت: ماه-سریال
      "print": {
        "id": 1,
        "title": { "fa": "مشاهده بدون قیمت", "en": "view without price" }
      },
      "internal": true,
      "contact_information": {
        "logo": "url...",
        "address": "Tehran...",
        "phone": "021..."
      },
      "operator": {
        "first_name": "Admin",
        "last_name": "User",
        "mobile": "0912..."
      },
      "leader": {
        "firstname_fa": "علی",
        "lastname_fa": "علوی",
        "mobile": "0912..."
      },
      "data": {
        // آرایه‌ای از آیتم‌ها گروه‌بندی شده بر اساس شناسه آیتم/مسافر
        "route_123": [
           {
             "serial_id": 50,
             "action": "route",
             "passenger": { ... }, // آبجکت کامل مسافر
             "sell": false,        // یا مبلغ اگر print=2 باشد
             "route": { ... }      // جزئیات پرواز
           }
        ]
      },
      "created": "2024-05-10 10:00:00"
    }
    // ممکن است شامل آبجکت‌های خطا هم باشد اگر یکی از فاکتورها مشکل داشته باشد
    // { "status": false, "code": 5006, "message": "..." }
  ]
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%E2%86%93-input-id-typ"><div class="flowchart"><div class="flow-item">Start</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Input ID Type?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="width: 180px;"><div class="flow-arrow-label-left">Array</div><div class="flow-item-process">WhereIn('id', list)</div></div><div style="width: 180px;"><div class="flow-arrow-label-right">Single</div><div class="flow-item-process">Where('serial', input - Extension)</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Factors with Joins</div><div class="flow-arrow">↓</div><div style="border: 2px dashed #4caf50; padding: 15px; border-radius: 8px; margin: 10px 0;">**Loop Factors**<div class="flow-arrow">↓</div><div class="flow-item-decision">Printable?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Return Error 5006/5001</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">Determine Print Mode (1, 2, 3)</div><div class="flow-arrow">↓</div><div style="background-color: #e3f2fd; padding: 5px; border-radius: 4px;">**Process Items**  
- Check Item Status  
- Extract Passengers (Redis Cache)  
- Extract Suppliers (Redis Cache)  
- Handle Refunds</div><div class="flow-arrow">↓</div><div class="flow-item-process">Format Output &amp; Contact Info</div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON (Code 5008)</div></div></div>

# POST /v2/trade/contract

# Trade: Contract Generation

این اندپوینت وظیفه **تولید متن کامل قرارداد** را بر عهده دارد.   
سیستم با استفاده از قالب‌های HTML ذخیره شده در دیتابیس و ترکیب آن‌ها با اطلاعات پویای فاکتور (مسافران، خدمات، قیمت‌ها)، خروجی نهایی قرارداد را برای نمایش یا چاپ آماده می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftrade%2Fcontr"><div class="endpoint-info"><div>**URL:** `/v2/trade/contract`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2TradeController@contractTradeApi</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA.-">- نیاز به احراز هویت.
- بررسی دسترسی به `branch` ارسال شده.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شماره سریال نمایشی فاکتور.   
<small>سیستم به صورت داخلی `ReferenceExtension` را از این عدد کم می‌کند تا سریال واقعی را در دیتابیس بیابد.</small></td></tr><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه.</td></tr><tr><td>lang\[id\]</td><td>string</td><td>**(الزامی)** کد زبان برای انتخاب متن و قالب قرارداد (مثلاً `fa`).</td></tr></tbody></table>

</div>## Logic Details

فرآیند تولید قرارداد شامل مراحل زیر است:

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%DA%A9%D8%B4%DB%8C-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D9%BE%D8%A7%DB%8C%D9%87%3A-">1. **واکشی اطلاعات پایه:** جستجوی فاکتور با اتصال (Join) به جداول `operators` و `customers` بر اساس سریال و شعبه.
2. **اعتبارسنجی‌ها:**
    - اگر `print == 0` باشد، خطای "عدم قابلیت چاپ" (5006) برمی‌گرداند.
    - اگر `status == 5` باشد، خطای وضعیت (5001) همراه با توضیحات فاکتور برمی‌گرداند.
3. **انتخاب قالب (Template Engine):**
    - سیستم قالبی را از جدول `pages` انتخاب می‌کند که `type` آن برابر با `contract_{route}` باشد (مثلاً `contract_internal`).
4. **پردازش هوشمند آیتم‌ها:**
    - محاسبه مجموع ارقام با استفاده از `ApiTradeController::financial`.
    - **مدیریت استرداد (Refund Logic):** اگر آیتمی از نوع `refund` باشد، آیتم اصلی متناظر با آن از لیست خدمات قرارداد حذف می‌شود تا سرویس کنسل شده نمایش داده نشود.
5. **تولید جداول HTML:** دو جدول به صورت پویا در کد ساخته می‌شوند: 
    - `%passengers-table%`: لیست نام، کدملی و تاریخ تولد مسافران.
    - `%services-table%`: لیست شرح خدمات باقی‌مانده.
6. **جایگذاری متغیرها:** متغیرهایی مانند `%leader%`, `%total%`, `%company%` در متن قالب جایگزین می‌شوند.

</div>## Response Structure

در صورت موفقیت، کد HTML قرارداد در فیلد `contract` و متادیتای مورد نیاز در `data` قرار می‌گیرد.

```json
{
  "status": true,
  "time": 1715001200,
  "contract": "... HTML CONTENT ...",
  "data": {
    "confirmation": "2024-05-10 12:30:00", // زمان تایید یا null
    "serial_id": 1500,       // شناسه اصلی دیتابیس
    "internal": true,        // وضعیت داخلی/خارجی بودن مسیر
    "leader": {
      "id": 105,
      "firstname_en": "Ali",
      "lastname_en": "Rezaei",
      "firstname_fa": "علی",
      "lastname_fa": "رضایی",
      "mobile": "09121234567"
    },
    "slug": "xYz123",
    "track_code": "02-2500", // فرمت: ماه - (سریال + اکستنشن)
    "created": "2024-05-10 10:00:00"
  }
}
```

### پاسخ‌های خطا

```json
// خطای عدم وجود یا وضعیت نامعتبر
{
  "status": false,
  "code": 5001,
  "message": "قرارداد یافت نشد" 
}
// خطای غیرقابل چاپ بودن
{
  "status": false,
  "code": 5006,
  "message": "رفرنس مورد نظر قابلیت چاپ ندارد."
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%E2%86%93-query-factor"><div class="flowchart"><div class="flow-item">Start</div><div class="flow-arrow">↓</div><div class="flow-item-process">Query Factor (Join Ops/Cus)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Factor Exists?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error 5001</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-decision">Printable?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error 5006</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">**Fetch Template**  
From `pages` table</div><div class="flow-arrow">↓</div><div style="background-color: #e3f2fd; padding: 10px; border-radius: 8px;">**Data Processing**  
1. Calculate Financials  
2. Filter Refunds (Hide Cancelled Items)  
3. Build HTML Tables (Pass/Service)  
4. Replace %Placeholders%</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON + HTML</div></div></div>

# POST /v2/trade/confirmation

# Trade: Contract Confirmation

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

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftrade%2Fconfi"><div class="endpoint-info"><div>**URL:** `/v2/trade/confirmation`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2TradeController@confirmationTrade</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%28">- نیاز به احراز هویت (احتمالاً JWT با توجه به سایر متدها).
- دسترسی به `branch` ارسال شده.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>serial\_id</td><td>integer</td><td>**(الزامی)** شناسه فاکتور (Contract ID) که باید تایید شود.</td></tr><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه جهت فیلترینگ امنیتی.</td></tr><tr><td>notices</td><td>boolean</td><td>**(اختیاری)** اگر `true` ارسال شود، سیستم اقدام به ارسال پیامک اطلاع‌رسانی به مسافر می‌کند.</td></tr></tbody></table>

</div>## Logic Details

فرآیند تایید شامل مراحل زیر است:

<div class="api-docs" id="bkmrk-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1%3A-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-">1. **یافتن فاکتور:** جستجو در جدول `factors` بر اساس `serial_id` و `branch`.
2. **ثبت تاییدیه:** به‌روزرسانی ستون `confirmation` با زمان جاری سرور (`Carbon::now`).
3. **ارسال اعلان (Notification Logic):** اگر پارامتر `notices` ارسال شده باشد: 
    - شماره موبایل مسافر (Customer) از جدول `customers` استخراج می‌شود.
    - اطلاعات شعبه (نام فارسی و تلفن) از جدول `offices` استخراج می‌شود.
    - **ساخت پیام:** متنی حاوی تایید قرارداد، لینک کوتاه (`mmah.ir/c/{slug}`)، تاریخ شمسی و تلفن پشتیبانی ایجاد می‌شود.
    - **صف‌بندی:** جاب `SendNotification` به صف اولویت بالا (`fastJob`) ارسال می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- **Body:**

</div>```json
{
  "status": true,
  "time": 1715001200
}
```

### پاسخ خطا

<div class="api-docs" id="bkmrk-status-code%3A-404-not">- **Status Code:** `404 Not Found` (حتی در صورت خطای داخلی سرور کد 404 برگردانده می‌شود).
- **Body:**

</div>```json
{
  "status": false,
  "code": 5007,
  "message": "خطا در ارسال اطلاعات.",
  "trace": [ ... ]
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-%E2%86%93-fetch-factor"><div class="flowchart"><div class="flow-item">Start</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Fetch Factor Details**  
(ID, Slug, CustomerID)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Update Database**  
SET confirmation = NOW()</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Notice Req?</div><div style="position: relative; height: 60px;"><div class="flow-arrow-label-right" style="top: -10px;">No</div><div class="flow-arrow-label-left" style="top: -10px;">Yes</div></div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="width: 200px;"><div class="flow-item-process" style="background-color: #fff3e0;">**Prepare SMS**  
1. Get Customer Mobile  
2. Get Branch Info  
3. Build Msg (Link+Date)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Dispatch to `fastJob`</div></div><div style="width: 100px;"></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success JSON</div></div></div>

# POST /v2/credit-card

# Credit Card: Issue Request

این اندپوینت وظیفه **احراز هویت پیشرفته** و **صدور کارت اعتباری** برای مسافر را بر عهده دارد.   
در این فرآیند، اطلاعات هویتی و مالکیتی سیم‌کارت از طریق سرویس‌های شاهکار و ثبت‌احوال (Jibit) استعلام شده و در صورت تأیید، یک کارت بانکی مجازی با الگوریتم استاندارد بانکی تولید و ۵۰,۰۰۰ ریال از کیف پول اپراتور کسر می‌گردد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcredit-card"><div class="endpoint-info"><div>**URL:** `/v2/credit-card`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CreditCards@createCreditCard</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT).
- اپراتور باید دسترسی به شعبه (`branch`) ارسال شده را داشته باشد.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>passenger</td><td>integer</td><td>**(الزامی)** شناسه مسافر (Customer ID) که باید در سیستم فعال باشد و نام/نام خانوادگی فارسی داشته باشد.</td></tr><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه صادرکننده (جهت تولید پیش‌شماره کارت).</td></tr><tr><td>mobile</td><td>string</td><td>**(اختیاری)** شماره موبایل جهت استعلام مالکیت. اگر ارسال نشود، از شماره موبایل پروفایل مسافر استفاده می‌شود.</td></tr><tr><td>identity\_code</td><td>string</td><td>**(اختیاری)** کدملی جهت استعلام. اگر ارسال نشود، از کدملی پروفایل مسافر استفاده می‌شود.</td></tr><tr><td>operator</td><td>object</td><td>**(تزریق سیستمی)** آبجکت اپراتور که معمولاً توسط میدل‌ور به ریکوئست اضافه می‌شود (جهت ثبت در تراکنش کیف پول).</td></tr></tbody></table>

</div>## Logic Details

فرآیند صدور کارت دارای منطق پیچیده اعتبارسنجی به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%A7%D9%88%D9%84%DB%8C%D9%87%3A-%D9%85%D8%B3">1. **اعتبارسنجی اولیه:**
    - مسافر باید وجود داشته باشد، وضعیت آن فعال (`status=1`) باشد و نام/نام‌خانوادگی فارسی پر شده باشد.
    - فرمت شماره موبایل و کد ملی (الگوریتم چک‌دیجیت) بررسی می‌شود.
    - بررسی تکراری نبودن: اگر مسافر از قبل کارت فعال (وضعیت ۱ یا ۳) داشته باشد، خطا بازگردانده می‌شود.
2. **استعلام تطابق هویتی (Jibit Identity Check):**   
    <small>این مرحله تنها در صورتی اجرا می‌شود که مسافر قبلاً احراز نشده باشد (`identity\_check IS NULL`).</small>
    - سرویس `getIdentitySimilarity` فراخوانی می‌شود.
    - اگر درصد تشابه نام و نام خانوادگی کمتر از **90%** باشد، خطا بازگردانده می‌شود.
    - نتیجه استعلام در دیتابیس مشتری (`identity\_check` و `identity\_data`) کش می‌شود.
3. **استعلام مالکیت سیم‌کارت (Shahkar):**
    - سرویس `getMatchingService` برای تطابق کدملی و موبایل فراخوانی می‌شود.
    - در صورت عدم تطابق مالکیت، فرآیند متوقف شده و خطا برمی‌گردد.
4. **تولید شماره کارت (Luhn Algorithm):**
    - **پیش‌شماره (BIN):** بر اساس کد شعبه ساخته می‌شود.   
        اگر شعبه &gt;= 90 باشد: `990` + `branch+10`   
        در غیر این صورت: `9900` + `branch+10`
    - **ساختار:** \[BIN\] + \[AccountType:1\] + \[PassengerID:9digits\]
    - ارقام باقی‌مانده تصادفی تولید شده و رقم آخر (Check Digit) با فرمول **Luhn** محاسبه می‌شود.
    - CVV2 تصادفی و انقضا ۳ ساله تنظیم می‌شود.
5. **عملیات مالی و ذخیره‌سازی:**
    - ثبت کارت در جدول `credit_cards`.
    - کسر مبلغ **50,000 ریال** از کیف پول اپراتور با عنوان "بابت استعلام هویت مسافر".
    - ارسال پیامک اطلاع‌رسانی به مسافر از طریق صف `fastJob`.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`
- **Body:** Empty (بدون محتوا).

</div>### پاسخ‌های خطا (نمونه)

```json
{
  "error": {
    "code": 1000, // یا کدهای خطای جیبیت
    "message": "اطلاعات هویتی مسافر با اطلاعات هویتی موجود در سیستم ثبت احوال مطابقت ندارد."
  },
  "meta": {
    "timestamp": 1715001200
  }
}
```

**کدهای وضعیت HTTP:**

<div class="api-docs" id="bkmrk-422-unprocessable-en">- `422 Unprocessable Entity`: خطای اعتبارسنجی ورودی، نقص اطلاعات پروفایل یا عدم تطابق هویتی.
- `500 Internal Server Error`: خطای سیستمی یا خطای اتصال به جیبیت.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-vali"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Validate Inputs &amp; Passenger Profile</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Card Exists?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">Yes</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error: Active Card</div></div><div class="flow-arrow">↓ (No)</div><div class="flow-item-decision">Identity Check is Null?</div><div style="display: flex; justify-content: space-between; margin-top: 20px;"><div style="width: 45%; margin-right: auto;"><div class="flow-arrow-label-left" style="text-align: center;">Yes</div><div class="flow-item-process" style="background-color: #fff3e0;">**Jibit Identity API**  
Check Name Similarity</div><div class="flow-item-decision">Similiarity &gt; 90%?</div><div class="flow-item-error" style="margin-top: 10px; font-size: 10px;">(No) -&gt; Error 422</div></div><div style="width: 45%; border-left: 2px dashed #ccc;"><div class="flow-arrow-label-right" style="text-align: center;">No (Already Checked)</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Jibit Shahkar API**  
Match Mobile &amp; NationalCode</div><div class="flow-item-decision">Match OK?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error: Ownership</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">**Generate Card**  
Calc BIN + Luhn Check Digit</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Finalize**  
1. Insert Card  
2. Deduct Wallet (50k)  
3. Queue SMS</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 201 Created</div></div></div>

# GET /v2/credit-card/index

# Credit Card: List &amp; Index

این اندپوینت وظیفه **واکشی و نمایش لیست کارت‌های اعتباری** صادر شده را بر عهده دارد.   
مهم‌ترین ویژگی این بخش، اعمال لایه‌ی امنیتی روی شماره کارت‌ها (Masking) جهت حفاظت از داده‌های حساس و همچنین فیلترینگ هوشمند بر اساس دسترسی شعبه (Branch Access) می‌باشد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcredit-card"><div class="endpoint-info"><div>**URL:** `/v2/credit-card/index`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CreditCards@indexCreditCard</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT).
- داده‌ها بر اساس پارامتر `branch` فیلتر می‌شوند (ایزوله‌سازی داده‌های شعب).

</div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه درخواست‌کننده. اگر کاربر ادمین مرکزی نباشد، لیست فقط محدود به رکوردهای این شعبه می‌شود.</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>**(الزامی)** تعداد رکورد مورد نظر در هر صفحه (Limit).</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>**(الزامی)** آفست (Offset) شروع رکوردها. سیستم از این عدد برای محاسبه شماره صفحه فعلی استفاده می‌کند.</td></tr></tbody></table>

</div></div>## Logic Details

منطق پردازش لیست شامل مراحل امنیتی و محاسباتی زیر است:

<div class="api-docs" id="bkmrk-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%B3%D8%B7%D8%AD-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%B4%D8%B9%D8%A8">1. **کنترل سطح دسترسی شعبه (Branch Filtering):**
    - سیستم بررسی می‌کند که آیا `branch` ارسال شده برابر با **1** (دفتر مرکزی) است یا خیر.
    - اگر `branch != 1` باشد، شرط `whereJsonContains('branch', $branch)` به کوئری اضافه می‌شود. این یعنی یک کارت ممکن است به چند شعبه دسترسی داشته باشد (ساختار JSON).
2. **غنی‌سازی داده‌ها (Data Enrichment):**
    - جدول `credit_cards` با جدول `customers` به صورت **Left Join** ترکیب می‌شود.
    - هدف: استخراج نام و نام خانوادگی فارسی مسافر (`first\_name\_fa`, `last\_name\_fa`) جهت نمایش در لیست.
3. **محاسبه صفحه‌بندی (Pagination Logic):**
    - لاراول به صورت پیش‌فرض با `page` کار می‌کند، اما ورودی دیتاتیبل معمولاً `start` و `length` است.
    - فرمول تبدیل: `$page = ($start == 0 ? $length : $start + $length) / $length`.
4. **لایه امنیت و تغییر فرمت (Security Transformation):**
    - **ماسک کردن شماره کارت:** تابع `Functions::creditCardSecurity` فراخوانی می‌شود.   
        الگوریتم: نمایش ۴ رقم اول + `********` + نمایش ۴ رقم آخر.   
        نتیجه: `6219********1234`.
    - **مدیریت مقادیر خالی:** فیلدهای `withdrawal_limit` و `blocked_amount` بررسی می‌شوند؛ اگر `null` باشند، مقدار `false` برگردانده می‌شود تا در فرانت‌اند خطای تایپ رخ ندهد.

</div>## Response Structure

### پاسخ موفق

```json
{
  "items": [
    {
      "id": 105,
      "status": 1, // 1: Active, 0: Inactive
      "card": {
        "number": "6219********4321", // Masked for security
        "withdrawal_limit": false,      // false if null
        "blocked_amount": 500000
      },
      "passenger": {
        "id": 2045,
        "first_name": "علی",
        "last_name": "محمدی"
      }
    }
    // ... more items
  ],
  "meta": {
    "timestamp": 1715005000,
    "table": {
      "total": 50,
      "per_page": 10,
      "current_page": 1,
      "last_page": 5,
      "from": 1,
      "to": 10
    }
  }
}
```

### مدیریت خطا

در صورت بروز خطای دیتابیس یا منطقی، ساختار زیر برای دیباگ برگردانده می‌شود:

```json
{
  "error": {
    "code": "HY000",
    "message": "SQLSTATE[HY000]: General error: ...",
    "file": "/app/Http/Controllers/V2/CreditCards.php",
    "line": 85
  },
  "meta": { "timestamp": 1715005123 }
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-is-b"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is Branch == 1?</div><div style="display: flex; justify-content: space-between; margin-top: 20px; direction: ltr;"><div style="width: 45%; margin-right: auto;"><div class="flow-arrow-label-left" style="text-align: center; color: #666; font-size: 12px;">No (Specific Branch)</div><div class="flow-item-process" style="background-color: #fff3e0;">**Add Filter**  
whereJsonContains('branch', id)</div></div><div style="width: 45%; border-left: 2px dashed #ccc; padding-left: 10px;"><div class="flow-arrow-label-right" style="text-align: center; color: #666; font-size: 12px;">Yes (HQ)</div><div class="flow-item-process" style="background-color: #f5f5f5; color: #888;">No Filter Applied</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Execute Query**  
Left Join Customers table  
Apply Pagination</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Transform Data**  
1. Mask Card Number (****)  
2. Format Response JSON</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK</div></div></div>

# GET /v2/credit-card

# Credit Card: Show Details

این اندپوینت برای **مشاهده جزئیات یک کارت اعتباری خاص** استفاده می‌شود.   
ویژگی منحصر‌به‌فرد این متد، **حساسیت به مالکیت (Ownership Awareness)** است. سیستم بررسی می‌کند که آیا درخواست‌کننده (Operator) همان صاحب کارت است یا خیر. اگر مالک باشد، اطلاعات محرمانه (CVV2، تاریخ انقضا و شماره کامل) نمایش داده می‌شود؛ در غیر این صورت، داده‌ها ماسک می‌شوند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcredit-card"><div class="endpoint-info"><div>**URL:** `/v2/credit-card`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CreditCards@showCreditCard</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT).
- دسترسی به اطلاعات کامل (CVV2/Expire) فقط مخصوص **صاحب کارت** است.

</div>## Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(اختیاری\*)** شناسه منحصر‌به‌فرد کارت اعتباری. (معمولاً یا این فیلد یا `passenger_id` باید ارسال شود).</td></tr><tr><td>passenger\_id</td><td>integer</td><td>**(اختیاری\*)** شناسه مسافر. برای یافتن کارت فعال یک مسافر خاص استفاده می‌شود.</td></tr></tbody></table>

</div></div>\* توجه: اگرچه پارامترها اختیاری (Optional) تعریف شده‌اند، اما برای دریافت خروجی معتبر باید حداقل یکی از آن‌ها ارسال شود تا کوئری نتیجه‌ای برگرداند.

<div class="api-docs" id="bkmrk--1"></div>## Logic Details &amp; Security

منطق پردازش شامل مراحل زیر است:

<div class="api-docs" id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D9%88-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%28query">1. **جستجو و اتصال (Query &amp; Join):**
    - جستجو در جدول `credit_cards` بر اساس `id` یا `passenger_id`.
    - اتصال (Left Join) به جدول `customers` برای دریافت نام مسافر.
    - دریافت اولین رکورد منطبق (`first()`).
2. **بررسی مالکیت (Ownership Check):**
    - سیستم شناسه کاربری که لاگین کرده (`$request->operator->id`) را با شناسه صاحب کارت (`passenger_id`) مقایسه می‌کند.
3. **سطح‌بندی نمایش داده‌ها (Visibility Logic):**
    - **حالت ۱: کاربر مالک کارت است**
        - شماره کارت: **کامل** نمایش داده می‌شود (از دیتابیس خوانده می‌شود).
        - فیلدهای حساس: `cvv2` و `expire_date` به پاسخ اضافه می‌شوند.
    - **حالت ۲: کاربر مالک نیست (مثلاً ادمین یا اپراتور دیگر)**
        - شماره کارت: توسط تابع `Functions::creditCardSecurity` **ماسک** می‌شود.
        - فیلدهای حساس: `cvv2` و `expire_date` **ارسال نمی‌شوند**.

</div>## Response Structure

### حالت اول: پاسخ به مالک کارت (Owner View)

```json
{
  "payload": {
    "id": 105,
    "status": 1,
    "card": {
      "number": "6219861012345678", // Full Number
      "withdrawal_limit": false,
      "blocked_amount": false,
      "cvv2": "452",              // Visible
      "expire_date": "1405/02"    // Visible
    },
    "passenger": {
      "id": 2045,
      "first_name": "علی",
      "last_name": "محمدی"
    }
  },
  "meta": { "timestamp": 1715005000 }
}
```

### حالت دوم: پاسخ به سایرین (Admin/Public View)

```json
{
  "payload": {
    "id": 105,
    "status": 1,
    "card": {
      "number": "6219********5678", // Masked Number
      "withdrawal_limit": false,
      "blocked_amount": false
      // CVV2 and Expire Date are OMITTED
    },
    "passenger": {
      "id": 2045,
      "first_name": "علی",
      "last_name": "محمدی"
    }
  },
  "meta": { "timestamp": 1715005000 }
}
```

### خطای ۴۲۲ (پیدا نشد)

اگر با پارامترهای ارسالی کارتی پیدا نشود:

```json
{
  "error": {
    "code": 1000,
    "message": "کارت اعتباری پیدا نشد."
  },
  "meta": { ... }
}
```

<div class="api-docs" id="bkmrk--2"><div dir="ltr"></div></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-find"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Find Card (ID or PassengerID)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Found?</div><div style="display: flex; justify-content: flex-end; width: 60%; margin: -20px auto 20px;"><div style="text-align: center;"><div class="flow-arrow-label-right" style="font-size: 12px; color: red;">No</div><div class="flow-item-process" style="background-color: #ffebee; border-color: #ef9a9a;">Return 422 Error</div></div></div><div class="flow-arrow" style="margin-top: -10px;">↓ Yes</div><div class="flow-item-decision">Is Operator == Passenger?</div><div style="display: flex; justify-content: space-between; margin-top: 20px; direction: ltr;"><div style="width: 48%;"><div class="flow-arrow-label-left" style="text-align: center; color: #2e7d32; font-weight: bold;">Yes (Owner)</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Full Access**  
Show Full Number  
Add CVV2 &amp; Expire</div></div><div style="width: 48%; border-left: 2px dashed #ccc; padding-left: 10px;"><div class="flow-arrow-label-right" style="text-align: center; color: #f57c00; font-weight: bold;">No (Admin)</div><div class="flow-item-process" style="background-color: #fff3e0;">**Restricted Access**  
Mask Number (****)  
Hide Sensitive Data</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Payload</div></div></div>

# POST /v2/articles/{id}/views

# Article: Increment Views

این اندپوینت وظیفه **افزایش شمارنده بازدید** یک مقاله خاص را بر عهده دارد.   
این متد معمولاً زمانی فراخوانی می‌شود که کاربر وارد صفحه جزئیات مقاله (Single Page) می‌شود. عملیات افزایش بازدید به صورت مستقیم روی دیتابیس (Atomic) انجام شده و تعداد جدید بازدیدها بلافاصله برگردانده می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles%2F%7Bi"><div class="endpoint-info"><div>**URL:** `/v2/articles/{id}/views`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** ArticleController@incrementViews</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد (طبق تعریف گروه Middleware).

</div>## Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه منحصر‌به‌فرد مقاله (Article ID) که در آدرس URL قرار می‌گیرد.</td></tr></tbody></table>

</div></div>## Logic Details

روند پردازش به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%DB%8C-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%28find-o">1. **جستجوی رکورد (Find or Fail):**
    - از متد `Article::findOrFail($id)` استفاده می‌شود.
    - اگر مقاله‌ای با این شناسه وجود نداشته باشد، لاراول به صورت خودکار یک خطای **404 Not Found** استاندارد پرتاب می‌کند و ادامه کد اجرا نمی‌شود.
2. **افزایش اتمیک (Atomic Increment):**
    - از متد `increment('views')` استفاده می‌شود.
    - این دستور مستقیماً یک کوئری `UPDATE articles SET views = views + 1 WHERE id = ...` به دیتابیس ارسال می‌کند. این روش از Race Condition جلوگیری کرده و پرفورمنس بالایی دارد (نیازی به خواندن، ویرایش در PHP و ذخیره مجدد نیست).
3. **بازگشت پاسخ:**
    - تعداد بازدیدهای جدید (آپدیت شده) همراه با برچسب زمان بازگردانده می‌شود.

</div>## Response Structure

### پاسخ موفق (200 OK)

```json
{
  "status": true,
  "time": 1715006000,
  "views": 1543 // تعداد بازدید جدید پس از افزایش
}
```

### خطای یافت نشد (404 Not Found)

اگر شناسه مقاله در دیتابیس موجود نباشد (توسط `findOrFail`):

```json
{
  "message": "No query results for model [App\\Models\\Article] 9999"
  // یا ساختار استاندارد خطای لاراول بسته به تنظیمات Exception Handler
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-find"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Find Article (findOrFail)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Exists?</div><div style="display: flex; justify-content: flex-end; width: 60%; margin: -20px auto 20px;"><div style="text-align: center;"><div class="flow-arrow-label-right" style="font-size: 12px; color: red;">No</div><div class="flow-item-process" style="background-color: #ffebee; border-color: #ef9a9a;">Return 404</div></div></div><div class="flow-arrow" style="margin-top: -10px;">↓ Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Increment DB Column**  
UPDATE `views` = `views` + 1</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON  
(New View Count)</div></div></div>

# RESOURCE  /v2/articles

# Article Resource Management

این بخش شامل ۵ اندپوینت استاندارد برای مدیریت کامل مقالات (Articles) می‌باشد.   
ویژگی‌های کلیدی این کنترلر شامل **فیلترینگ پیشرفته** در لیست‌گیری (بر اساس دسته‌بندی، تگ و مکان) و **مدیریت روابط متا تگ‌ها** (PageMetatags) هنگام ساخت و ویرایش است.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List Articles (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles-me"><div class="endpoint-info"><div>**URL:** `/v2/articles`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ArticleController@index</div></div></div>### پارامترهای فیلترینگ (Query Params)

<div class="api-docs" id="bkmrk-parameter-type-descr"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>categories</td><td>string (JSON)</td><td>آرایه‌ای از دسته‌بندی‌ها به صورت رشته JSON.   
مثال: `["tech", "news"]`  
از منطق `orWhereJsonContains` استفاده می‌کند.</td></tr><tr><td>tags</td><td>string (JSON)</td><td>آرایه‌ای از تگ‌ها به صورت رشته JSON.   
مثال: `["laravel", "api"]`</td></tr><tr><td>place</td><td>mixed</td><td>شناسه (ID) یا نام مستعار (Slug) یک مکان.  
**منطق خاص:** ابتدا ID مکان را از جدول `articles_places` پیدا می‌کند، سپس دسته‌بندی‌های مرتبط را از `articles_categories` می‌یابد و مقالات را بر اساس ستون `places` فیلتر می‌کند.</td></tr><tr><td>sortById</td><td>boolean</td><td>اگر ارسال شود، ترتیب `DESC` (نزولی) بر اساس ID اعمال می‌شود. در غیر این صورت پیش‌فرض `ASC` است.</td></tr><tr><td>limit</td><td>integer</td><td>محدودیت تعداد رکوردها قبل از صفحه‌بندی (توجه: نتیجه نهایی همچنان Paginate شده است).</td></tr></tbody></table>

</div></div>### مثال پاسخ (Response)

```json
{
  "status": true,
  "time": 1715008000,
  "data": [
    { "id": 10, "title": "...", "views": 150, ... } // ArticleResource Object
  ],
  "links": {
    "first": "...",
    "last": "...",
    "prev": null,
    "next": "..."
  }
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Article (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles-me-1"><div class="endpoint-info"><div>**URL:** `/v2/articles`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** ArticleController@store</div></div></div>یک مقاله جدید ایجاد می‌کند. همزمان اگر آرایه `metatags` ارسال شود، رکوردهای مربوطه در جدول `page_metatags` ساخته می‌شوند.   
فیلد `operator` به صورت خودکار از توکن کاربر احراز هویت شده برداشته می‌شود.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title</td><td>string</td><td>(الزامی) عنوان مقاله</td></tr><tr><td>slug</td><td>string</td><td>(الزامی) آدرس یکتا</td></tr><tr><td>branch</td><td>integer</td><td>(الزامی) شناسه شعبه</td></tr><tr><td>categories</td><td>array</td><td>لیست دسته‌بندی‌ها (در دیتابیس JSON می‌شود)</td></tr><tr><td>tags</td><td>array</td><td>لیست تگ‌ها (در دیتابیس JSON می‌شود)</td></tr><tr><td>sub\_title</td><td>string</td><td>عنوان فرعی</td></tr><tr><td>body</td><td>text</td><td>متن اصلی مقاله</td></tr><tr><td>summary</td><td>text</td><td>خلاصه مقاله</td></tr><tr><td>thumbnail</td><td>string</td><td>آدرس تصویر شاخص</td></tr><tr><td>published\_at</td><td>datetime</td><td>تاریخ انتشار (پیش‌فرض: زمان حال)</td></tr><tr><td>score</td><td>integer</td><td>امتیاز مقاله</td></tr><tr><td>metatags</td><td>array\[obj\]</td><td>لیست متا تگ‌ها برای سئو. ساختار هر آبجکت:   
`{ "key": "description", "value": "text..." }`</td></tr></tbody></table>

</div>  ---

</div>## 3. Show Article (Show)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles%2F%7Bi"><div class="endpoint-info"><div>**URL:** `/v2/articles/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ArticleController@show</div></div></div>جزئیات کامل یک مقاله را برمی‌گرداند. از Route Model Binding لاراول استفاده می‌کند (اگر پیدا نشود ۴۰۴ می‌دهد).

<div class="api-docs" id="bkmrk--2">  ---

</div>## 4. Update Article

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles%2F%7Bi-1"><div class="endpoint-info"><div>**URL:** `/v2/articles/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** ArticleController@update</div></div></div>اطلاعات مقاله را ویرایش می‌کند.   
**منطق هوشمند متاتگ‌ها:** در آرایه `metatags`، اگر آیتمی دارای `id` باشد، آن رکورد در دیتابیس **آپدیت** می‌شود. اگر فاقد ID باشد، به عنوان یک متاتگ **جدید** برای این مقاله ایجاد می‌شود.

<div class="api-docs" id="bkmrk-start-update-%E2%86%93-updat"><div class="flowchart"><div class="flow-item">Start Update</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update Main Article Fields</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has Metatags?</div><div class="flow-arrow">↓ Yes</div><div style="border: 1px dashed #999; padding: 10px; background: #f9f9f9; border-radius: 8px;">**Loop through items:**   
  
<span style="font-size: 12px;">Item has "id"?</span>   
<span style="color: green;">YES</span> → `PageMetatag::find($id)->update()`   
<span style="color: orange;">NO</span> → `PageMetatag::create()`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return { status: true }</div></div>  ---

</div>## 5. Delete Article

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Farticles%2F%7Bi-2"><div class="endpoint-info"><div>**URL:** `/v2/articles/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** ArticleController@destroy</div></div></div>مقاله را حذف می‌کند. متاتگ‌های وابسته (اگر Cascade در دیتابیس تنظیم شده باشد) حذف می‌شوند، در غیر این صورت فقط رکورد مقاله حذف می‌شود.

```json
{
    "status": true,
    "time": 1715009000
}
```

# RESOURCE /v2/categories

# Category Resource Management

این بخش شامل مدیریت کامل **دسته‌بندی‌ها (Categories)** است.   
این کنترلر به طور پیش‌فرض در متد لیست‌گیری، فقط **دسته‌بندی‌های اصلی (بدون والد)** را برمی‌گرداند. همچنین از فیلترهای مکان و نوع پشتیبانی می‌کند.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List Categories (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcategories-"><div class="endpoint-info"><div>**URL:** `/v2/categories`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CategoryController@index</div></div></div>**نکته مهم:** این اندپوینت دارای فیلتر سخت‌گیرانه `whereNull('main')` است. یعنی فقط دسته‌بندی‌هایی که فیلد `main` آن‌ها خالی است (دسته‌بندی‌های والد) در خروجی ظاهر می‌شوند.

### پارامترهای فیلترینگ (Query Params)

<div class="api-docs" id="bkmrk-parameter-type-descr"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>mixed</td><td>(معمولاً خودکار) فیلتر بر اساس شعبه.</td></tr><tr><td>type</td><td>string</td><td>فیلتر بر اساس نوع دسته‌بندی (مثلاً article, video و...).</td></tr><tr><td>place</td><td>mixed</td><td>شناسه (ID) یا نام مستعار (Slug) مکان.   
سیستم ابتدا شناسه مکان را از جدول `articles_places` پیدا کرده و سپس دسته‌بندی‌هایی که ستون `places` آن‌ها برابر با آن شناسه باشد را فیلتر می‌کند.</td></tr><tr><td>limit</td><td>integer</td><td>محدودیت تعداد آیتم‌ها در هر صفحه.</td></tr></tbody></table>

</div></div>### مثال پاسخ

```json
{
  "status": true,
  "time": 1715011000,
  "data": [
    {
      "id": 5,
      "title": "تکنولوژی",
      "slug": "tech",
      "image": "https://...",
      "main": null, // همیشه null است در این لیست
      "places": "[1, 2]" // JSON String
    }
  ],
  "links": { ... }
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Category (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcategories--1"><div class="endpoint-info"><div>**URL:** `/v2/categories`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CategoryController@store</div></div></div>یک دسته‌بندی جدید ایجاد می‌کند.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title</td><td>string</td><td>(الزامی) عنوان دسته‌بندی</td></tr><tr><td>slug</td><td>string</td><td>(الزامی) آدرس یکتا</td></tr><tr><td>main</td><td>integer|null</td><td>شناسه دسته‌بندی والد (اگر زیرمجموعه است). اگر خالی باشد، دسته اصلی محسوب می‌شود.</td></tr><tr><td>image</td><td>string</td><td>آدرس تصویر</td></tr><tr><td>description</td><td>text</td><td>توضیحات</td></tr><tr><td>places</td><td>array</td><td>لیستی از مکان‌های مرتبط. (در دیتابیس به صورت JSON ذخیره می‌شود).</td></tr><tr><td>branch</td><td>integer</td><td>(از طریق توکن/ریکوئست) شناسه شعبه.</td></tr></tbody></table>

</div>  ---

</div>## 3. Show Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcategories%2F"><div class="endpoint-info"><div>**URL:** `/v2/categories/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CategoryController@show</div></div></div>مشاهده جزئیات یک دسته‌بندی خاص.

<div class="api-docs" id="bkmrk--2">  ---

</div>## 4. Update Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcategories%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/categories/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** CategoryController@update</div></div></div>ویرایش اطلاعات دسته‌بندی. ورودی‌ها دقیقاً مشابه متد Store هستند.

```json
{
    "status": true,
    "time": 1715012000
}
```

<div class="api-docs" id="bkmrk--3"><div dir="ltr"></div>  ---

</div>## 5. Delete Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcategories%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/categories/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CategoryController@destroy</div></div></div>حذف کامل دسته‌بندی.

<div class="api-docs" id="bkmrk--4">  </div>## Index Logic Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-appl"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Apply Filters:  
1. Branch = Request-&gt;branch  
2. Main IS NULL (Root Categories)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has "place" param?</div><div style="display: flex; justify-content: space-between; margin-top: 20px; direction: ltr;"><div style="width: 48%;"><div class="flow-arrow-label-left" style="text-align: center;">Yes</div><div class="flow-item-process" style="background-color: #e3f2fd;">Find ID from `articles_places`  
(by ID or Slug)  
↓  
Filter: `whereIn('places', [ID])`</div></div><div style="width: 48%;"><div class="flow-arrow-label-right" style="text-align: center;">No</div><div class="flow-item-process" style="border: 1px dashed #ccc;">Skip Place Filter</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Pagination (15 items)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Resource Collection</div></div></div>

# RESOURCE /v2/tags

# Tag Resource Management

این بخش شامل مدیریت کامل **تگ‌ها (Tags)** است که برای برچسب‌گذاری روی مقالات و سایر محتواها استفاده می‌شود.   
این کنترلر از ساختار استاندارد Resource در لاراول پیروی می‌کند.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List Tags (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftags-method"><div class="endpoint-info"><div>**URL:** `/v2/tags`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** TagController@index</div></div></div>لیست تگ‌ها را به صورت صفحه‌بندی شده (۱۵ آیتم در هر صفحه) برمی‌گرداند.

### مثال پاسخ

```json
{
  "status": true,
  "time": 1715015000,
  "data": [
    {
      "id": 1,
      "title": "Laravel",
      "slug": "laravel",
      "description": "PHP Framework"
    }
  ],
  "links": {
    "first": "http://.../v2/tags?page=1",
    "last": "http://.../v2/tags?page=5",
    "prev": null,
    "next": "http://.../v2/tags?page=2"
  }
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Tag (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftags-method-1"><div class="endpoint-info"><div>**URL:** `/v2/tags`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** TagController@store</div></div></div>یک تگ جدید ایجاد می‌کند. پارامتر `branch` به صورت خودکار از درخواست (احتمالاً از توکن کاربر یا میدل‌ور) دریافت و ثبت می‌شود.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title</td><td>string</td><td>(الزامی) عنوان تگ</td></tr><tr><td>slug</td><td>string</td><td>(الزامی) آدرس یکتا (معمولاً انگلیسی)</td></tr><tr><td>description</td><td>text</td><td>توضیحات تگ (اختیاری)</td></tr></tbody></table>

</div></div>### مثال پاسخ موفق

```json
{
  "status": true,
  "time": 1715015100,
  "data": {
      "id": 12,
      "title": "New Tag",
      "slug": "new-tag",
      "description": "..."
  }
}
```

<div class="api-docs" id="bkmrk--2"><div dir="ltr"></div>  ---

</div>## 3. Show Tag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftags%2F%7Bid%7D-m"><div class="endpoint-info"><div>**URL:** `/v2/tags/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** TagController@show</div></div></div>مشاهده جزئیات یک تگ خاص بر اساس ID.

<div class="api-docs" id="bkmrk--3">  ---

</div>## 4. Update Tag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftags%2F%7Bid%7D-m-1"><div class="endpoint-info"><div>**URL:** `/v2/tags/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** TagController@update</div></div></div>ویرایش اطلاعات تگ. فیلدها مشابه متد Store هستند.

```json
{
    "status": true,
    "time": 1715015200
}
```

<div class="api-docs" id="bkmrk--4"><div dir="ltr"></div>  ---

</div>## 5. Delete Tag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftags%2F%7Bid%7D-m-2"><div class="endpoint-info"><div>**URL:** `/v2/tags/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** TagController@destroy</div></div></div>حذف تگ از سیستم.

<div class="api-docs" id="bkmrk-delete-request-%E2%86%93-fin"><div class="flowchart"><div class="flow-item">Delete Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Find Tag by ID</div><div class="flow-arrow">↓</div><div class="flow-item-process">Delete Record</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return {status: true}</div></div></div>

# RESOURCE /v2/landing_pages

# Landing Page Management

این بخش شامل مدیریت کامل **صفحات فرود (Landing Pages)** است.   
این صفحات معمولاً برای کمپین‌های خاص یا صفحات ایستا استفاده می‌شوند و شامل مدیریت سئو (Meta Tags) به صورت تودرتو هستند.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List Landing Pages (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Flanding_pag"><div class="endpoint-info"><div>**URL:** `/v2/landing_pages`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LandingPageController@index</div></div></div>**توجه:** در این متد از صفحه‌بندی (Pagination) استفاده نشده و تمام رکوردهای موجود در جدول برگردانده می‌شوند.

### مثال پاسخ

```json
{
  "status": true,
  "time": 1715016000,
  "data": [
    {
      "id": 1,
      "title": "Black Friday Sale",
      "slug": "black-friday",
      "image": "https://...",
      "published_at": "2024-11-20 00:00:00"
    },
    {
      "id": 2,
      "title": "Norouz Campaign",
      "slug": "norouz-1404",
      "image": "https://...",
      "published_at": "2025-03-20 00:00:00"
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Landing Page (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Flanding_pag-1"><div class="endpoint-info"><div>**URL:** `/v2/landing_pages`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** LandingPageController@store</div></div></div>ایجاد صفحه فرود جدید به همراه متاتگ‌های سئو.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title</td><td>string</td><td>(الزامی) عنوان صفحه</td></tr><tr><td>slug</td><td>string</td><td>(الزامی) آدرس یکتا</td></tr><tr><td>body</td><td>text</td><td>محتوای HTML یا متن صفحه</td></tr><tr><td>image</td><td>string</td><td>لینک تصویر شاخص</td></tr><tr><td>published\_at</td><td>datetime</td><td>تاریخ انتشار</td></tr><tr><td>meta\_tag</td><td>string</td><td>(احتمالاً متاتگ کلی یا توضیحات متا)</td></tr><tr><td>metatags</td><td>array</td><td>آرایه‌ای از اشیاء برای جدول `page_metatags`.   
ساختار: `[{"key": "keywords", "value": "test"}]`</td></tr></tbody></table>

</div></div>**رفتار سیستمی:** متاتگ‌های ارسالی در آرایه `metatags` با تایپ `landing_page` در دیتابیس ذخیره می‌شوند.

<div class="api-docs" id="bkmrk--2">  ---

</div>## 3. Show Landing Page

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Flanding_pag-2"><div class="endpoint-info"><div>**URL:** `/v2/landing_pages/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LandingPageController@show</div></div></div>دریافت جزئیات کامل یک صفحه فرود.

<div class="api-docs" id="bkmrk--3">  ---

</div>## 4. Update Landing Page

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Flanding_pag-3"><div class="endpoint-info"><div>**URL:** `/v2/landing_pages/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** LandingPageController@update</div></div></div>ویرایش اطلاعات صفحه فرود و مدیریت هوشمند متاتگ‌ها.

### منطق آپدیت متاتگ‌ها (Metatags Logic)

هنگام ارسال آرایه `metatags`، سیستم روی هر آیتم پیمایش می‌کند:

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-id-%D8%AF%D8%A7%D8%B4%D8%AA%D9%87-%D8%A8%D8%A7%D8%B4%D8%AF%3A-%D9%85">- **اگر `id` داشته باشد:** متاتگ موجود با آن ID را پیدا کرده و `key` و `body` (مقدار value) آن را آپدیت می‌کند.
- **اگر `id` نداشته باشد:** یک متاتگ جدید ایجاد می‌کند.   
    <span style="color: red; font-size: 0.9em;">(نکته کدنویسی: طبق کد موجود، متاتگ‌های جدیدی که در متد آپدیت ساخته می‌شوند، تایپ `article` می‌گیرند، در حالی که در متد Store تایپ `landing_page` بود).</span>

<div class="flowchart"><div class="flow-item">Update Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update LandingPage Basic Info</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Loop Metatags Array</div><div style="display: flex; justify-content: space-between; margin-top: 20px; direction: ltr;"><div style="width: 48%;"><div class="flow-arrow-label-left" style="text-align: center;">Has "id"?</div><div class="flow-item-process" style="background-color: #e3f2fd;">Find PageMetatag(id)  
Update key &amp; value</div></div><div style="width: 48%;"><div class="flow-arrow-label-right" style="text-align: center;">No "id"</div><div class="flow-item-process" style="background-color: #e8f5e9;">Create NEW PageMetatag  
(type='article')</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return {status: true}</div></div>  ---

</div>## 5. Delete Landing Page

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Flanding_pag-4"><div class="endpoint-info"><div>**URL:** `/v2/landing_pages/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** LandingPageController@destroy</div></div></div>حذف صفحه فرود (و احتمالاً متاتگ‌های وابسته اگر Cascade در دیتابیس تنظیم شده باشد).

# RESOURCE /v2/page_metatags

# Page Metatag Management (Direct)

این بخش شامل مدیریت مستقیم و مستقل **متاتگ‌های سئو (Meta Tags)** است.   
معمولاً متاتگ‌ها همراه با مقاله یا صفحه فرود ویرایش می‌شوند، اما این اندپوینت‌ها برای اصلاحات سریع یا مدیریت سیستمی کاربرد دارند.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List All Metatags (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fpage_metata"><div class="endpoint-info"><div>**URL:** `/v2/page_metatags`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** PageMetatagController@index</div></div></div>**هشدار عملکردی:** این متد از `PageMetatag::all()` استفاده می‌کند و تمام رکوردهای جدول را بدون صفحه‌بندی برمی‌گرداند. در صورت زیاد بودن داده‌ها، پاسخ سنگین خواهد بود.

### مثال پاسخ

```json
{
  "status": true,
  "time": 1715018000,
  "data": [
    {
      "id": 101,
      "page_id": 5,
      "type": "article",
      "key": "keywords",
      "body": "news, tech, ai",
      "details": null,
      "status": 1,
      "created_at": "...",
      "updated_at": "..."
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Metatag (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fpage_metata-1"><div class="endpoint-info"><div>**URL:** `/v2/page_metatags`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** PageMetatagController@store</div></div></div>ایجاد یک متاتگ جدید و انتساب آن به یک صفحه یا مقاله.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>page\_id</td><td>integer</td><td>(الزامی) شناسه صفحه‌ای که این متاتگ به آن تعلق دارد.</td></tr><tr><td>type</td><td>string</td><td>(الزامی) نوع صفحه والد (مثلاً `article` یا `landing_page`).</td></tr><tr><td>key</td><td>string</td><td>(الزامی) نام ویژگی متا (مثلاً `description`, `keywords`, `og:title`).</td></tr><tr><td>body</td><td>text</td><td>(الزامی) مقدار محتوای متا (Content Value).</td></tr><tr><td>details</td><td>text/json</td><td>جزئیات اضافی (اختیاری).</td></tr><tr><td>status</td><td>integer</td><td>وضعیت فعال/غیرفعال (مثلاً 1 یا 0).</td></tr></tbody></table>

</div>  ---

</div>## 3. Show Metatag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fpage_metata-2"><div class="endpoint-info"><div>**URL:** `/v2/page_metatags/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** PageMetatagController@show</div></div></div>مشاهده جزئیات یک متاتگ خاص.

<div class="api-docs" id="bkmrk--2">  ---

</div>## 4. Update Metatag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fpage_metata-3"><div class="endpoint-info"><div>**URL:** `/v2/page_metatags/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** PageMetatagController@update</div></div></div>ویرایش کامل یک متاتگ. ورودی‌ها دقیقاً مشابه متد Store هستند.

```json
{
    "status": true,
    "time": 1715018200
}
```

<div class="api-docs" id="bkmrk--3"><div dir="ltr"></div>  ---

</div>## 5. Delete Metatag

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fpage_metata-4"><div class="endpoint-info"><div>**URL:** `/v2/page_metatags/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** PageMetatagController@destroy</div></div></div>حذف متاتگ از دیتابیس.

# RESOURCE /v2/places

# Article Places Management

این بخش مربوط به مدیریت **مکان‌های نمایش (Places)** مقالات است.   
مکان‌ها احتمالاً نواحی خاصی در سایت هستند (مثل "اسلایدر اصلی"، "اخبار فوری") که مقالات یا دسته‌بندی‌ها به آن‌ها منتسب می‌شوند.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. List Places (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fplaces-meth"><div class="endpoint-info"><div>**URL:** `/v2/places`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** PlaceController@index</div></div></div>لیست مکان‌ها را برمی‌گرداند. برای هر مکان، دسته‌بندی‌های متصل به آن نیز (که در جدول `articles_categories` تعریف شده‌اند) واکشی و ضمیمه می‌شوند.

### فیلترها و منطق کوئری

<div class="api-docs" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-branch%3A-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-">- **فیلتر Branch:** سیستم مکان‌هایی را برمی‌گرداند که `branch` آن‌ها برابر با برنچ کاربر جاری باشد **یا** برابر با `0` (سراسری) باشد.
- **فیلتر Type:** اگر پارامتر `type` در URL ارسال شود، نتایج بر اساس آن فیلتر می‌شوند.

</div>### ساختار پاسخ (Different Response Structure)

توجه کنید که آرایه اصلی در کلید `items` قرار دارد.

```json
{
  "items": [
    {
      "id": 1,
      "title": "Home Slider",
      "type": "slider",
      "branch": 0,
      "status": 1,
      "categories": [
        {
          "id": 5,
          "title": "Top News",
          "places": "[1, 2]" 
        }
      ]
    }
  ],
  "meta": {
    "total": 1,
    "time": 1715020000
  }
}
```

<div class="api-docs" id="bkmrk--1"><div dir="ltr"></div>  ---

</div>## 2. Create Place (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fplaces-meth-1"><div class="endpoint-info"><div>**URL:** `/v2/places`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** PlaceController@store</div></div></div>ایجاد یک مکان جدید در جدول `articles_places`.

### بدنه درخواست (Body Parameters)

<div class="api-docs" id="bkmrk-field-type-descripti"><div class="table-wrapper"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title</td><td>string</td><td>(الزامی) عنوان مکان</td></tr><tr><td>type</td><td>string</td><td>نوع مکان (مثلاً slider, sidebar)</td></tr><tr><td>status</td><td>integer</td><td>وضعیت (1 فعال، 0 غیرفعال)</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه (معمولاً از توکن یا ورودی گرفته می‌شود)</td></tr></tbody></table>

</div>  ---

</div>## 3. Show Place

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fplaces%2F%7Bid%7D"><div class="endpoint-info"><div>**URL:** `/v2/places/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** PlaceController@show</div></div></div>نمایش جزئیات یک مکان خاص.   
در اینجا نیز کوئری دوم اجرا می‌شود تا تمام دسته‌بندی‌هایی که این مکان در فیلد `places` (JSON) آن‌ها وجود دارد، پیدا شده و به خروجی اضافه شوند.

<div class="api-docs" id="bkmrk--2">  ---

</div>## 4. Update Place

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fplaces%2F%7Bid%7D-1"><div class="endpoint-info"><div>**URL:** `/v2/places/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** PlaceController@update</div></div></div>ویرایش اطلاعات مکان. پارامترها مشابه متد Store است.

```json
{
    "status": true,
    "time": 1715020500
}
```

<div class="api-docs" id="bkmrk--3"><div dir="ltr"></div>  ---

</div>## 5. Delete Place

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fplaces%2F%7Bid%7D-2"><div class="endpoint-info"><div>**URL:** `/v2/places/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** PlaceController@destroy</div></div></div>حذف رکورد از جدول `articles_places`.

# RESOURCE /v2/media

# Media Management (File Storage)

این ماژول وظیفه مدیریت فایل‌ها و رسانه‌های سیستم را بر عهده دارد.   
برخلاف ذخیره‌سازی محلی، این کنترلر مستقیماً با **فضای ذخیره‌سازی ابری (Liara Object Storage)** در ارتباط است. فایل‌ها بر اساس نوع، شعبه، سال و ماه در پوشه‌بندی‌های منظم ذخیره شده و متادیتای آن‌ها (شامل سایز، مسیر و ارتباط با سایر موجودیت‌ها) در دیتابیس نگهداری می‌شود.

<div class="api-docs" id="bkmrk-">  ---

</div>## 1. Upload File (Store)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmedia-metho"><div class="endpoint-info"><div>**URL:** `/v2/media`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MediaController@store</div><div>**Content-Type:** multipart/form-data</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>file</td><td>File</td><td>**(الزامی)** فایل باینری جهت آپلود (تصویر، سند و...).</td></tr><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه (جهت ساختار پوشه‌بندی).</td></tr><tr><td>type</td><td>string</td><td>**(الزامی)** نوع فایل (مثلاً `slider`, `avatar`, `document`) که نام پوشه اصلی را تعیین می‌کند.</td></tr><tr><td>path</td><td>string</td><td>**(اختیاری)** پیشوند مسیر ذخیره‌سازی. اگر ارسال نشود، پیش‌فرض `uploads` در نظر گرفته می‌شود.</td></tr><tr><td>related\_id</td><td>integer</td><td>**(اختیاری)** شناسه رکورد مرتبط (مثلاً ID هتل یا مقاله).</td></tr><tr><td>related\_category</td><td>string</td><td>**(اختیاری)** دسته‌بندی رکورد مرتبط (مثلاً `articles`).</td></tr><tr><td>operator</td><td>object</td><td>**(تزریق سیستمی)** آبجکت اپراتور (از طریق میدل‌ور Auth) برای ثبت در فیلد `operator_id`.</td></tr></tbody></table>

</div>### Upload Logic Details

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88-%D8%A2%D9%85%D8%A7%D8%AF%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-">1. **دریافت و آماده‌سازی فایل:**
    - استخراج پسوند (Extension) و نام اصلی فایل.
    - نام فایل در دیتابیس بدون پسوند ذخیره می‌شود.
2. **ساخت مسیر ذخیره‌سازی (Path Generation):**   
    مسیر فایل به صورت دینامیک بر اساس تاریخ جاری و ورودی‌ها ساخته می‌شود:   
    `{base_path}/{branch}/{type}/{Year}/{Month}/{ext}/`
    - اگر پارامتر `path` ارسال شود، به عنوان `base_path` استفاده می‌شود.
    - در غیر این صورت، `uploads` به عنوان پیش‌فرض قرار می‌گیرد.
3. **ذخیره‌سازی فیزیکی (Liara Disk):**
    - استفاده از `Storage::disk('liara')->putFileAs(...)`.
    - فایل با نام اصلی + پسوند در مسیر ساخته شده آپلود می‌شود.
    - سپس سایز واقعی فایل آپلود شده از دیسک ابری استعلام می‌شود (`size()`).
4. **ثبت در دیتابیس:**
    - رکورد جدید با اطلاعات کامل (شامل مسیر کامل `path`، سایز، نوع و ارتباطات) در جدول `media` ایجاد می‌شود.

</div>### Flowchart (Upload Process)

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-extr"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Extract File info  
(Ext, Name)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has Custom Path?</div><div style="display: flex; justify-content: space-between; margin-top: 10px;"><div style="width: 45%; text-align: center;"><div class="flow-arrow-label-left">Yes</div><div class="flow-item-process" style="font-size: 0.8em;">Base = $request-&gt;path</div></div><div style="width: 45%; text-align: center;"><div class="flow-arrow-label-right">No</div><div class="flow-item-process" style="font-size: 0.8em;">Base = 'uploads'</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Generate Path Structure**  
base/branch/type/Y/m/ext/</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Liara Storage Upload**  
putFileAs() &amp; get Size()</div><div class="flow-arrow">↓</div><div class="flow-item-success">Create DB Record &amp; Return</div></div>  ---

</div>## 2. List Media (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmedia-metho-1"><div class="endpoint-info"><div>**URL:** `/v2/media`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** MediaController@index</div></div></div>لیست فایل‌های آپلود شده به صورت **صفحه‌بندی شده (۱۵ تایی)**.

### فیلترهای جستجو (Query Params)

<div class="api-docs" id="bkmrk-operator_id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%A2">- `operator_id`: شناسه آپلود کننده.
- `related_id`: شناسه رکورد مرتبط.
- `related_category`: دسته‌بندی مرتبط.
- `type`: نوع فایل.
- `branch`: شعبه.
- `status`: وضعیت فایل.
- `sortById`: اگر ارسال شود (true)، مرتب‌سازی نزولی (جدیدترین‌ها) اعمال می‌شود.

  ---

</div>## 3. Update Media Status

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmedia%2F%7Bid%7D-"><div class="endpoint-info"><div>**URL:** `/v2/media/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** MediaController@update</div></div></div>**توجه:** در متد آپدیت، فایل فیزیکی یا مسیر آن تغییر نمی‌کند. تنها می‌توان **وضعیت (Status)** فایل را تغییر داد.

```json
{
  "status": 0 // غیرفعال کردن فایل
}
```

<div class="api-docs" id="bkmrk--1">  ---

</div>## 4. Delete Media

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fmedia%2F%7Bid%7D--1"><div class="endpoint-info"><div>**URL:** `/v2/media/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** MediaController@destroy</div></div></div>عملیات حذف دومرحله‌ای:

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-">1. ابتدا با استفاده از مسیر ذخیره شده در دیتابیس (`path`)، فایل فیزیکی از دیسک **Liara** حذف می‌شود (`Storage::disk('liara')-&gt;delete`).
2. سپس رکورد از دیتابیس پاک می‌شود.

</div>

# GET /v2/travel_requests

# Travel Requests List

این اندپوینت لیست درخواست‌های سفر (مانند پرواز، هتل، قطار و ...) را با قابلیت‌های پیشرفته فیلترینگ و **غنی‌سازی (Data Enrichment)** بازمی‌گرداند.   
نکته متمایز این کنترلر، منطق **Scoping** (تعیین سطح دسترسی بر اساس گروه کاربری) و همچنین اتصال به چندین جدول (Operators, Customers, Colleagues, Cities, Factors) و دیتابیس **Redis** برای ساخت یک آبجکت پاسخ کامل است.

<div class="api-docs" id="bkmrk-">  ---

</div>## List Requests (Index)

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftravel_requ"><div class="endpoint-info"><div>**URL:** `/v2/travel_requests`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** TravelRequestsController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>**(الزامی)** شناسه شعبه.</td></tr><tr><td>group</td><td>string</td><td>تعیین کننده سطح دسترسی (`requester\_id`): - `base` / `b2e`: دسترسی ادمین به درخواست‌های یک کاربر خاص (نیاز به ارسال `requester\_id`).
- `colleague` / `b2b`: لیست درخواست‌های خود اپراتور B2B.
- `agent` / `b2c`: لیست درخواست‌های خود اپراتور B2C.

</td></tr><tr><td>requester\_id</td><td>integer</td><td>در صورتی که `group` برابر با `base` باشد، برای فیلتر کردن درخواست‌های یک کاربر خاص استفاده می‌شود.</td></tr><tr><td>operator\_id</td><td>integer</td><td>فیلتر بر اساس اپراتور هندل‌کننده درخواست.</td></tr><tr><td>method</td><td>string</td><td>فیلتر نوع اصلی خدمت (مثلاً `flight`, `hotel`).</td></tr><tr><td>submethod</td><td>string</td><td>فیلتر زیرمجموعه خدمت.</td></tr><tr><td>status</td><td>string</td><td>فیلتر وضعیت درخواست.</td></tr><tr><td>payment\_status</td><td>string</td><td>فیلتر وضعیت پرداخت.</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>تعداد آیتم در صفحه (پیش‌فرض: ۳۰).</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>آفست شروع (پیش‌فرض: ۰). شماره صفحه به صورت دینامیک محاسبه می‌شود.</td></tr></tbody></table>

</div>### Logic Details

<div class="api-docs" id="bkmrk-%D9%85%D9%86%D8%B7%D9%82-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C-%28pagi">1. **منطق صفحه‌بندی (Pagination Calculation):**   
    سیستم از متد استاندارد `page` لاراول استفاده نمی‌کند، بلکه شماره صفحه را بر اساس `start` و `length` محاسبه می‌کند:   
    `Current Page = (start + length) / length`   
    اگر `start=0` باشد، صفحه ۱ در نظر گرفته می‌شود.
2. **منطق Scoping (تعیین Requester):**
    - اگر `group` برابر **base/b2e** باشد: کوئری روی `requester\_id` ورودی فیلتر می‌شود.
    - اگر **colleague/b2b** یا **agent/b2c** باشد: کوئری روی `operator-&gt;id` (کاربر لاگین شده) قفل می‌شود.
    - در غیر این صورت: همه درخواست‌ها نمایش داده می‌شوند.
3. **غنی‌سازی داده‌ها (Data Enrichment Loop):** پس از دریافت داده‌های خام از جدول `travel\_requests`، روی هر آیتم عملیات زیر انجام می‌شود: 
    - **Polymorphic Requester:** بسته به `operator\_group`، اطلاعات درخواست‌دهنده از جدول `customers` (برای B2C) یا `colleagues` (برای B2B از طریق جدول واسط `colleague\_auth`) واکشی می‌شود.
    - **Cities:** نام فارسی و انگلیسی `origin` و `destination` از جدول `cities` جوین می‌شود.
    - **Operator:** جزئیات اپراتور هندل‌کننده (شامل آواتار) اضافه می‌شود.
    - **Reference (Factor):**
        - سریال فاکتور از جدول `factors` خوانده شده و با **10000** جمع می‌شود.
        - عنوان فاکتور از **Redis** با کلید `reference:{id}:information:title:fa` استعلام می‌شود.
    - **Accommodation:** اگر هتل باشد، اطلاعات آن از طریق متد استاتیک `StaticController::getAccommodation` دریافت می‌شود.
    - **Dynamic Title Construction:** یک عنوان توصیفی شامل "مبدا + مقصد + هتل + تاریخ‌ها" ساخته می‌شود. (تاریخ‌ها برای نمایش صحیح در متون فارسی با کاراکتر کنترل یونیکد `\u{200E}` محصور می‌شوند).
    - **Details:** فیلد `details` که JSON است، دیکد می‌شود.

</div>### Flowchart (Request Processing)

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-calc"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Calc Pagination**  
Page = (Start + Length) / Length</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Check 'group' param</div><div style="display: flex; justify-content: space-between; margin-top: 10px;"><div style="width: 30%; text-align: center;"><div class="flow-arrow-label-left">base/b2e</div><div class="flow-item-process" style="font-size: 0.7em;">Filter by Input  
Requester_ID</div></div><div style="width: 30%; text-align: center;"><div class="flow-arrow-label-left">b2b/b2c</div><div class="flow-item-process" style="font-size: 0.7em;">Filter by Auth  
Operator ID</div></div><div style="width: 30%; text-align: center;"><div class="flow-arrow-label-right">Others</div><div class="flow-item-process" style="font-size: 0.7em;">No Filter  
(Admin View)</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**DB Query &amp; Filters**  
Apply branch, method, status...</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Data Mapping (Loop)**  
Fetch Cities, Operator, Factor (Redis), Hotel info</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #f3e5f5;">**Generate Title**  
Origin + Dest + Hotel + Dates</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON (Items + Meta)</div></div></div>### Response Structure

```json
{
  "items": [
    {
      "id": 123,
      "branch": 1,
      "requester_id": { "id": 50, "first_name": "...", "last_name": "..." }, // Object (Customer or Colleague)
      "origin": { "id": 1, "fa_name": "تهران", "en_name": "Tehran" },
      "destination": { "id": 2, "fa_name": "مشهد", "en_name": "Mashhad" },
      "reference": {
         "id": 99,
         "serial": 10055, // Original + 10000
         "title": "تور مشهد" // From Redis
      },
      "accommodation": {
         "title": { "fa": "هتل درویشی" },
         "category": { "title": "hotel", "subtitle": false }
      },
      "title": "تهران به مشهد هتل درویشی از 1403/01/01 تا 1403/01/04", // Generated String
      "details": { ... } // Decoded JSON
      // ... other fields
    }
  ],
  "meta": {
    "timestamp": 1715000000,
    "table": {
      "total": 100,
      "per_page": 30,
      "current_page": 1,
      "last_page": 4,
      "from": 1,
      "to": 30,
      "next_page": 2
    }
  }
}
```

# POST /v2/travel_requests/status/{id}

# Change Request Status

این اندپوینت برای **تغییر وضعیت** یک درخواست سفر و همچنین **تخصیص اپراتور** مسئول استفاده می‌شود.   
نکته مهم در این متد، منطق شرطی برای ثبت شماره فاکتور (`reference\_id`) است که تنها در وضعیت خاصی (وضعیت ۴) اعمال می‌شود.

<div class="api-docs" id="bkmrk-">  ---

</div>## Update Status &amp; Assign Operator

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftravel_requ"><div class="endpoint-info"><div>**URL:** `/v2/travel_requests/status/{id}`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** TravelRequestsController@changeStatus</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>status</td><td>integer</td><td>**(الزامی)** کد وضعیت جدید درخواست.</td></tr><tr><td>operator\_id</td><td>integer</td><td>**(اختیاری)** شناسه اپراتور مسئول رسیدگی. اگر مقدار خالی یا `0` ارسال شود، در دیتابیس `NULL` ثبت می‌شود.</td></tr><tr><td>reference\_id</td><td>integer</td><td>**(شرطی)** شناسه فاکتور صادر شده. این فیلد تنها زمانی در دیتابیس آپدیت می‌شود که `status` برابر با **4** باشد.</td></tr></tbody></table>

</div>### Logic Details

<div class="api-docs" id="bkmrk-%D8%A2%D9%85%D8%A7%D8%AF%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%3A-">1. **آماده‌سازی داده‌ها:**
    - مقدار `operator_id` بررسی می‌شود؛ در صورت وجود مقدار معتبر ثبت می‌شود، در غیر این صورت (استفاده از Elvis Operator `?:`) مقدار `NULL` لحاظ می‌شود.
    - مقدار `status` مستقیماً از ورودی خوانده می‌شود.
2. **شرط ثبت فاکتور (Reference Logic):**   
    سیستم تنها در صورتی فیلد `reference_id` را آپدیت می‌کند که دو شرط زیر همزمان برقرار باشند: 
    - وضعیت جدید (`status`) برابر با **4** باشد.
    - مقدار `reference\_id` در ورودی ارسال شده و دارای مقدار باشد.
    
    *در غیر این صورت، حتی اگر `reference\_id` ارسال شود، نادیده گرفته می‌شود.*
3. **به‌روزرسانی دیتابیس:** عملیات با استفاده از `Query Builder` روی جدول `travel\_requests` انجام می‌شود.

</div>### Flowchart (Status Change Logic)

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-set-"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Set Operator  
(Val or NULL)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Status == 4 ?</div><div style="display: flex; justify-content: space-between; margin-top: 10px;"><div style="width: 45%; text-align: center;"><div class="flow-arrow-label-left">Yes</div><div class="flow-item-decision" style="font-size: 0.8em; margin-top: 5px;">Has Ref ID?</div><div style="display: flex; justify-content: center; margin-top: 5px;"><div class="flow-arrow-label-left" style="margin: 0 5px;">Yes</div></div><div class="flow-item-process" style="font-size: 0.8em; background-color: #e8f5e9;">Add reference_id  
to update list</div></div><div style="width: 45%; text-align: center;"><div class="flow-arrow-label-right">No</div><div class="flow-item-process" style="font-size: 0.8em; opacity: 0.6;">Ignore reference_id</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**DB Update**  
Update travel_requests WHERE id = $id</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Success JSON</div></div></div>### Response Example

```json
{
  "payload": {
    "status": true
  },
  "meta": {
    "timestamp": 1715000000
  }
}
```

# RESOURCE /v2/accommodation/facilities

# Accommodation Facilities CRUD

این کنترلر وظیفه مدیریت **امکانات اقامتگاه‌ها (Facilities)** را بر عهده دارد.   
ویژگی بارز این پیاده‌سازی، استفاده مستقیم از **Query Builder** لاراول (`DB::table`) به جای Eloquent Models برای تمامی عملیات‌ها (خواندن، نوشتن، ویرایش و حذف) است که باعث افزایش پرفورمنس در کوئری‌های ساده می‌شود.

<div class="api-docs" id="bkmrk-">  ---

</div>## List All Facilities

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** FacilityController@index</div></div></div>دریافت لیست کامل امکانات بدون هیچ‌گونه فیلترینگ یا صفحه‌بندی.

### Response Example

```json
{
  "status": true,
  "time": 1715000000,
  "data": [
    {
      "id": 1,
      "category_id": 5,
      "title_en": "Free WiFi",
      "title_fa": "اینترنت رایگان",
      "icon": "wifi-icon.svg",
      "description": "High speed internet",
      "branch": 1,
      "status": 1
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1">  ---

</div>## Create Facility

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-1"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** FacilityController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>category\_id</td><td>integer</td><td>شناسه دسته‌بندی امکانات.</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه.</td></tr><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی.</td></tr><tr><td>icon</td><td>string</td><td>آدرس یا نام فایل آیکون.</td></tr><tr><td>description</td><td>string</td><td>توضیحات تکمیلی.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت فعال/غیرفعال (معمولاً 1 یا 0).</td></tr></tbody></table>

</div>### Implementation Note

سیستم از `DB::table('facilities')->insertGetId()` برای درج رکورد و دریافت همزمان ID استفاده می‌کند. سپس بلافاصله یک کوئری `SELECT` اجرا می‌شود تا آبجکت کامل ساخته شده را در پاسخ بازگرداند.

<div class="api-docs" id="bkmrk--2">  ---

</div>## Show Facility Details

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-2"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** FacilityController@show</div></div></div>نمایش جزئیات یک رکورد خاص بر اساس ID.

<div class="api-docs" id="bkmrk--3">  ---

</div>## Update Facility

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-3"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** FacilityController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی.</td></tr><tr><td>icon</td><td>string</td><td>آدرس یا نام فایل آیکون.</td></tr><tr><td>description</td><td>string</td><td>توضیحات.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت.</td></tr></tbody></table>

<div class="api-notice" dir="rtl">**نکته مهم:** در متد آپدیت، فیلدهای `category_id` و `branch` در آرایه به‌روزرسانی وجود ندارند. این بدان معناست که پس از ایجاد، امکان تغییر دسته‌بندی یا شعبه یک Facility از طریق این اندپوینت وجود ندارد (Immutable Fields).</div>  ---

</div>## Delete Facility

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-4"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** FacilityController@destroy</div></div></div>حذف فیزیکی رکورد از دیتابیس (Hard Delete) با استفاده از دستور `delete()` کوئری بیلدر.

# RESOURCE /v2/accommodation/facilities_category

# Accommodation Facility Categories

این کنترلر وظیفه مدیریت **دسته‌بندی‌های امکانات (Facility Categories)** را بر عهده دارد (مانند: تفریحی، بهداشتی، عمومی و ...).   
مشابه کنترلر Facilities، این بخش نیز برای افزایش سرعت و کارایی از **Query Builder** لاراول (`DB::table`) استفاده می‌کند.

<div class="api-docs" id="bkmrk-">  ---

</div>## List Categories

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities_category`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** FacilityCategoryController@index</div></div></div>دریافت لیست کامل دسته‌بندی‌ها بدون فیلترینگ و صفحه‌بندی.

### Response Example

```json
{
  "status": true,
  "time": 1715000000,
  "data": [
    {
      "id": 1,
      "title_en": "General",
      "title_fa": "عمومی",
      "icon": "general.svg",
      "description": "General amenities",
      "branch": 1,
      "status": 1
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1">  ---

</div>## Create Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-1"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities_category`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** FacilityCategoryController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی دسته‌بندی.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی دسته‌بندی.</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه.</td></tr><tr><td>icon</td><td>string</td><td>نام یا مسیر فایل آیکون.</td></tr><tr><td>description</td><td>string</td><td>توضیحات.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت (1: فعال، 0: غیرفعال).</td></tr></tbody></table>

  ---

</div>## Show Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-2"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities_category/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** FacilityCategoryController@show</div></div></div>نمایش تکی یک دسته‌بندی بر اساس ID.

<div class="api-docs" id="bkmrk--2">  ---

</div>## Update Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-3"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities_category/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** FacilityCategoryController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی.</td></tr><tr><td>branch</td><td>integer</td><td>شناسه شعبه. (قابل ویرایش)</td></tr><tr><td>icon</td><td>string</td><td>آیکون.</td></tr><tr><td>description</td><td>string</td><td>توضیحات.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت.</td></tr></tbody></table>

<div class="api-notice" dir="rtl">**نکته:** برخلاف کنترلر Facilities، در این متد فیلد `branch` نیز در آرایه آپدیت وجود دارد و **قابل ویرایش** است.</div>  ---

</div>## Delete Category

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-4"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/facilities_category/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** FacilityCategoryController@destroy</div></div></div>حذف فیزیکی رکورد از جدول `facilities_category`.

# RESOURCE /v2/accommodation/rules

# Accommodation Rules CRUD

این کنترلر وظیفه مدیریت **قوانین اقامتگاه (Accommodation Rules)** را بر عهده دارد (مانند: قوانین کنسلی، ساعت ورود/خروج، ممنوعیت حیوانات خانگی و ...).   
داده‌ها مستقیماً در جدول `accommodation_rules` ذخیره می‌شوند و برای تمام عملیات دیتابیس از **Query Builder** استفاده شده است.

<div class="api-docs" id="bkmrk-">  ---

</div>## List All Rules

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/rules`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** RuleController@index</div></div></div>دریافت لیست کامل قوانین ثبت شده بدون فیلترینگ یا صفحه‌بندی.

### Response Example

```json
{
  "status": true,
  "time": 1715000000,
  "data": [
    {
      "id": 1,
      "accommodation": 105,
      "title_en": "No Smoking",
      "title_fa": "استعمال دخانیات ممنوع",
      "description": "Smoking is strictly prohibited inside the rooms.",
      "status": 1
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1">  ---

</div>## Create Rule

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-1"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/rules`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** RuleController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>accommodation</td><td>integer</td><td>شناسه اقامتگاه مرتبط (Relation Key).</td></tr><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی قانون.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی قانون.</td></tr><tr><td>description</td><td>string</td><td>توضیحات کامل قانون.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت (1: فعال، 0: غیرفعال).</td></tr></tbody></table>

  ---

</div>## Show Rule Details

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-2"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/rules/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** RuleController@show</div></div></div>نمایش جزئیات یک قانون خاص بر اساس شناسه.

<div class="api-docs" id="bkmrk--2">  ---

</div>## Update Rule

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-3"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/rules/{id}`</div><div>**Method:** <span class="method-put">PUT/PATCH</span></div><div>**Controller:** RuleController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>accommodation</td><td>integer</td><td>شناسه اقامتگاه. (قابل ویرایش)</td></tr><tr><td>title\_en</td><td>string</td><td>عنوان انگلیسی.</td></tr><tr><td>title\_fa</td><td>string</td><td>عنوان فارسی.</td></tr><tr><td>description</td><td>string</td><td>توضیحات.</td></tr><tr><td>status</td><td>integer</td><td>وضعیت.</td></tr></tbody></table>

<div class="api-notice" dir="rtl">**تفاوت با سایر کنترلرها:** در این کنترلر، فیلد رابطه (`accommodation`) نیز در متد Update وجود دارد و **قابل تغییر** است.</div>  ---

</div>## Delete Rule

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Faccommodati-4"><div class="endpoint-info"><div>**URL:** `/v2/accommodation/rules/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** RuleController@destroy</div></div></div>حذف فیزیکی رکورد از جدول `accommodation_rules`.

# GET /v2/comments

# Comments: List &amp; Filter

این اندپوینت برای دریافت لیستی از نظرات (Comments) با قابلیت‌های فیلترینگ و مرتب‌سازی پیشرفته طراحی شده است.   
این پیاده‌سازی از **Eloquent ORM** و **API Resources** لاراول بهره می‌برد و نتایج را به صورت صفحه‌بندی شده (15 آیتم در هر صفحه) بازمی‌گرداند.

<div class="api-docs" id="bkmrk-">  ---

</div>## List All Comments

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments-me"><div class="endpoint-info"><div>**URL:** `/v2/comments`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CommentController@index</div></div></div>### Query Parameters

این اندپوینت از پارامترهای زیر برای فیلتر و مرتب‌سازی پشتیبانی می‌کند:

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>operator\_id</td><td>integer</td><td>فیلتر بر اساس شناسه اپراتور ثبت‌کننده نظر.</td></tr><tr><td>content\_id</td><td>integer</td><td>فیلتر بر اساس شناسه محتوایی که نظر برای آن ثبت شده (مثلا شناسه مقاله).</td></tr><tr><td>category</td><td>string</td><td>فیلتر بر اساس دسته‌بندی محتوا (مثلا `'article'`, `'accommodation'`).</td></tr><tr><td>branch</td><td>integer</td><td>فیلتر بر اساس شناسه شعبه.</td></tr><tr><td>status</td><td>integer</td><td>فیلتر بر اساس وضعیت نظر (مثلا تایید شده، در انتظار، رد شده).</td></tr><tr><td>flagged</td><td>integer</td><td>فیلتر نظراتی که علامت‌گذاری (Flag) شده‌اند. (مثلا `1` برای فیلتر موارد علامت‌دار).</td></tr><tr><td>reply\_to</td><td>integer</td><td>فیلتر برای نمایش پاسخ‌های یک نظر خاص (با ارسال ID نظر اصلی).</td></tr><tr><td>sortById</td><td>any</td><td>**منطق مرتب‌سازی:**  
- اگر این پارامتر در URL **وجود داشته باشد** (مثلا `?sortById=true`)، نتایج بر اساس `id` به صورت **نزولی (Desc)** مرتب می‌شوند (جدیدترین‌ها اول).  
- در غیر این صورت (پیش‌فرض)، نتایج به صورت **صعودی (Asc)** مرتب می‌شوند (قدیمی‌ترین‌ها اول).</td></tr><tr><td>page</td><td>integer</td><td>شماره صفحه برای صفحه‌بندی (مثلا `?page=2`).</td></tr></tbody></table>

</div>### Response Structure

خروجی این اندپوینت شامل آبجکت‌های `data` (آرایه‌ای از نظرات)، `links` (لینک‌های صفحه‌بندی) و `meta` (اطلاعات متا صفحه‌بندی) است.

### Response Example

```json
{
    "status": true,
    "time": 1715000000,
    "data": [
        {
            "id": 10,
            "operatorId": 5,
            "contentId": 152,
            "category": "article",
            "branch": 1,
            "commentText": "مطلب بسیار مفیدی بود، ممنون.",
            "status": "approved",
            "isFlagged": false,
            "replyTo": null,
            "createdAt": "2024-10-22T14:30:00.000000Z"
        }
    ],
    "links": {
        "first": "http://your.api/v2/comments?page=1",
        "last": "http://your.api/v2/comments?page=5",
        "prev": null,
        "next": "http://your.api/v2/comments?page=2"
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 5,
        "path": "http://your.api/v2/comments",
        "per_page": 15,
        "to": 15,
        "total": 75
    }
}
```

# GET /v2/comments/{id}

# Show Comment Details

این اندپوینت جزئیات کامل یک نظر خاص را برمی‌گرداند.   
در این متد از قابلیت **Route Model Binding** لاراول استفاده شده است؛ به این معنی که اگر شناسه‌ی ارسال شده در URL در دیتابیس وجود نداشته باشد، فریم‌ورک به صورت خودکار خطای 404 برمی‌گرداند.

<div class="api-docs" id="bkmrk-">  ---

</div>## Get Single Comment

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments%2F%7Bi"><div class="endpoint-info"><div>**URL:** `/v2/comments/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CommentController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه منحصر به فرد نظر (Comment ID).</td></tr></tbody></table>

</div>### Response Logic

خروجی از طریق `CommentResource` فرمت‌دهی شده و در قالب استاندارد شامل وضعیت و زمان سرور بازگردانده می‌شود.

### Response Example

```json
{
    "status": true,
    "time": 1715000000,
    "data": {
        "id": 10,
        "operatorId": 5,
        "contentId": 152,
        "category": "article",
        "branch": 1,
        "commentText": "مطلب بسیار مفیدی بود، ممنون.",
        "status": "approved",
        "isFlagged": false,
        "replyTo": null,
        "createdAt": "2024-10-22T14:30:00.000000Z",
        "updatedAt": "2024-10-22T14:30:00.000000Z"
    }
}
```

# DELETE /v2/comments/{id}

# Delete Comment

این اندپوینت برای حذف یک نظر مشخص از دیتابیس استفاده می‌شود.   
این متد نیز از قابلیت **Route Model Binding** لاراول بهره می‌برد؛ به این معنی که ابتدا مدل `Comment` متناظر با شناسه ارسالی را پیدا کرده و سپس آن را حذف می‌کند. اگر شناسه نامعتبر باشد، به طور خودکار پاسخ 404 (Not Found) بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-">  ---

</div>## Delete a Single Comment

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments%2F%7Bi"><div class="endpoint-info"><div>**URL:** `/v2/comments/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CommentController@destroy</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه منحصر به فرد نظری که باید حذف شود (Comment ID).</td></tr></tbody></table>

</div>### Response on Success

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

### Response Example

```json
{
    "status": true,
    "time": 1715000000
}
```

# POST /v2/comments

# Create Comment

این اندپوینت برای ثبت یک نظر جدید در سیستم طراحی شده است. فرآیند ارسال داده به این اندپوینت به دلیل ملاحظات امنیتی، پیچیدگی خاصی دارد.   
داده‌های اصلی نظر ابتدا باید به صورت یک آبجکت JSON ساخته شده، سپس با استفاده از **کلید عمومی (Public Key)** مربوط به هر شعبه رمزنگاری (Encrypt) شوند و در نهایت به صورت Base64 در یک فیلد به نام `data` به سرور ارسال گردند.

<div class="api-docs" id="bkmrk-">  ---

</div>## Request Flow and Encryption

<div class="api-docs" id="bkmrk-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%A2%D8%A8%D8%AC%DA%A9%D8%AA-json%3A-%D8%A7%D8%A8%D8%AA"><div class="flowchart" dir="rtl">1. **ساخت آبجکت JSON:** ابتدا اطلاعات نظر را در قالب یک آبجکت JSON استاندارد آماده کنید (ساختار آن در جدول زیر آمده است).
2. **رمزنگاری (Encryption):** رشته JSON حاصل را با استفاده از **کلید عمومی RSA** که مختص `branch` مورد نظر است، رمزنگاری کنید.
3. **کدگذاری Base64:** خروجی باینری حاصل از مرحله رمزنگاری را به فرمت رشته Base64 تبدیل کنید.
4. **ارسال درخواست:** یک درخواست `POST` به این اندپوینت ارسال کنید که بدنه آن فقط شامل یک فیلد `data` حاوی رشته Base64 نهایی است.
5. **پردازش در سرور:** میدلور `decryptData` در سمت سرور با استفاده از **کلید خصوصی (Private Key)** متناظر، داده‌ها را رمزگشایی کرده و در اختیار کنترلر قرار می‌دهد.

</div>  ---

</div>## Create a New Comment

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments-me"><div class="endpoint-info"><div>**URL:** `/v2/comments`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CommentController@store</div><div>**Middleware Chain:** `authWithJwt`, `decryptData`</div></div></div>### Request Body Structure (Before Encryption)

آبجکت JSON که باید رمزنگاری و ارسال شود، باید شامل فیلدهای زیر باشد:

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>content\_id</td><td>integer</td><td>شناسه محتوایی که نظر برای آن است (مثلاً شناسه مقاله).</td><td>Required</td></tr><tr><td>category</td><td>string</td><td>نوع محتوا (مثلاً 'article', 'accommodation').</td><td>Required</td></tr><tr><td>content</td><td>string</td><td>متن اصلی نظر.</td><td>Required</td></tr><tr><td>name</td><td>string</td><td>نام نویسنده نظر.</td><td>Required</td></tr><tr><td>email</td><td>string</td><td>ایمیل نویسنده نظر.</td><td>Required, Email Format</td></tr><tr><td>phone</td><td>string</td><td>شماره تلفن نویسنده نظر.</td><td>Optional</td></tr><tr><td>status</td><td>integer</td><td>وضعیت اولیه نظر (مثلاً 0 برای "در انتظار تایید").</td><td>Required</td></tr><tr><td>reply\_to</td><td>integer</td><td>اگر این نظر پاسخی به نظر دیگری است، شناسه نظر والد در اینجا قرار می‌گیرد. در غیر این صورت null یا حذف شود.</td><td>Optional, Nullable</td></tr></tbody></table>

<div class="api-notice warning" dir="rtl">**اعتبارسنجی:** تمام فیلدهای ورودی توسط `StoreCommentRequest` اعتبارسنجی می‌شوند. در صورت ارسال داده نامعتبر، خطای 422 بازگردانده خواهد شد.</div></div>### Request Examples

**مرحله ۱: ساختار JSON خام (قبل از رمزنگاری)**

```json
{
    "content_id": 152,
    "category": "article",
    "content": "این یک تست برای ثبت کامنت است.",
    "name": "کاربر تست",
    "email": "test@example.com",
    "phone": "09120000000",
    "status": 0,
    "reply_to": null
}
```

**مرحله ۲: بدنه نهایی درخواست (بعد از رمزنگاری و Base64)**

```json
{
    "data": "eyB...[Encrypted & Base64 Encoded String]...SIn0="
}
```

### Server-Side Logic

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

<div class="api-docs" id="bkmrk-operator_id%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%A7">- `operator_id`: شناسه اپراتور لاگین کرده (در صورت وجود).
- `branch`: شناسه شعبه اپراتور.
- `ip_address`: آدرس IP درخواست‌دهنده.

</div>### Success Response

در صورت موفقیت، سرور یک پاسخ با کد 200 و حاوی آبجکت نظر جدید که از طریق `CommentResource` فرمت‌دهی شده، برمی‌گرداند.

```json
{
    "status": true,
    "time": 1715000000,
    "data": {
        "id": 11,
        "operatorId": 5,
        "contentId": 152,
        "category": "article",
        "branch": 1,
        "commentText": "این یک تست برای ثبت کامنت است.",
        "status": "pending",
        "isFlagged": false,
        "replyTo": null,
        "createdAt": "2024-10-23T10:00:00.000000Z",
        "updatedAt": "2024-10-23T10:00:00.000000Z"
    }
}
```

# PUT /v2/comments/{id}

# Update Comment

این اندپوینت برای به‌روزرسانی اطلاعات یک نظر موجود استفاده می‌شود. همانند اندپوینت ایجاد نظر، داده‌های ارسالی برای به‌روزرسانی نیز باید ابتدا رمزنگاری شده و سپس ارسال شوند.   
این متد از قابلیت **Route Model Binding** لاراول استفاده می‌کند. به این معنی که لاراول ابتدا به صورت خودکار مدل `Comment` مربوط به `{id}` ارسالی در URL را پیدا می‌کند و سپس عملیات به‌روزرسانی را روی آن انجام می‌دهد. اگر شناسه نامعتبر باشد، پاسخ 404 بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-">  ---

</div>## Encryption Flow

فرآیند رمزنگاری و ارسال داده‌ها برای این اندپوینت دقیقاً مشابه اندپوینت [ایجاد نظر (Create Comment)](#store) است. خلاصه‌ی مراحل به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%A2%D8%A8%D8%AC%DA%A9%D8%AA-json%3A-%D8%AF%D8%A7%D8%AF">1. **ساخت آبجکت JSON:** داده‌های مورد نظر برای آپدیت را در قالب یک آبجکت JSON آماده کنید.
2. **رمزنگاری و Base64:** آبجکت JSON را با **کلید عمومی (Public Key)** شعبه رمزنگاری کرده و به Base64 تبدیل کنید.
3. **ارسال درخواست:** درخواست `PUT` را به همراه بدنه حاوی فیلد `data` (شامل رشته نهایی) ارسال نمایید.

</div><div class="api-docs" id="bkmrk-%D8%AA%D9%88%D8%AC%D9%87%3A-%D9%85%DB%8C%D8%AF%D9%84%D9%88%D8%B1-decrypt"><div class="api-notice info" dir="rtl">**توجه:** میدلور `decryptData` در سمت سرور، با استفاده از **کلید خصوصی (Private Key)**، داده‌ها را رمزگشایی کرده و برای پردازش به کنترلر تحویل می‌دهد.</div>  ---

</div>## Update an Existing Comment

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments%2F%7Bi"><div class="endpoint-info"><div>**URL:** `/v2/comments/{id}`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** CommentController@update</div><div>**Middleware Chain:** `authWithJwt`, `decryptData`</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>شناسه منحصر به فرد نظری که باید به‌روزرسانی شود (Comment ID).</td></tr></tbody></table>

</div>### Request Body Structure (Before Encryption)

آبجکت JSON که باید رمزنگاری و در فیلد `data` ارسال شود، می‌تواند شامل فیلدهای زیر باشد. تمام فیلدها اختیاری هستند و فقط فیلدهایی که قصد تغییر آن‌ها را دارید، ارسال کنید.

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>content</td><td>string</td><td>متن جدید نظر.</td><td>Required</td></tr><tr><td>status</td><td>integer</td><td>وضعیت جدید نظر (مثلاً 1 برای "تایید شده").</td><td>Required, Integer</td></tr><tr><td>flagged</td><td>boolean</td><td>علامت‌گذاری نظر به عنوان "پرچم‌دار". مقدار `true` یا `false`.</td><td>Required, Boolean</td></tr></tbody></table>

<div class="api-notice warning" dir="rtl">**اعتبارسنجی:** فیلدهای ورودی توسط `UpdateCommentRequest` اعتبارسنجی می‌شوند. هر سه فیلد باید در آبجکت JSON وجود داشته باشند.</div></div>### Request Examples

**مرحله ۱: ساختار JSON خام (قبل از رمزنگاری)**

```json
{
    "content": "متن ویرایش شده نظر.",
    "status": 1,
    "flagged": false
}
```

**مرحله ۲: بدنه نهایی درخواست (بعد از رمزنگاری و Base64)**

```json
{
    "data": "AbC...[Encrypted & Base64 Encoded String]...dEfG=="
}
```

### Success Response

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

```json
{
    "status": true,
    "time": 1715000000
}
```

### Error Responses

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-400-%28bad-request%29">- **کد 400 (Bad Request):** در صورتی که میدلور `decryptData` نتواند داده‌ها را رمزگشایی کند، این خطا به همراه پیام `Decryption failed.` بازگردانده می‌شود.
- **کد 404 (Not Found):** اگر شناسه‌ی `{id}` ارسال شده در URL در دیتابیس موجود نباشد.
- **کد 422 (Unprocessable Entity):** اگر داده‌های رمزگشایی شده از اعتبارسنجی `UpdateCommentRequest` عبور نکنند (مثلاً یکی از فیلدها ارسال نشود).

</div>

# POST /v2/comments/{commentId}/{action}

# Like or Dislike a Comment

این اندپوینت برای ثبت لایک یا دیسلایک برای یک نظر مشخص استفاده می‌شود. عملیات مورد نظر (لایک یا دیسلایک) از طریق پارامتر `action` در URL تعیین می‌گردد.   
این متد به صورت اتمیک عمل نمی‌کند، اما شمارنده‌های `likes` و `dislikes` را در مدل `Comment` یک واحد افزایش می‌دهد.

<div class="api-docs" id="bkmrk-">  ---

</div>## Increment Like/Dislike Counter

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments%2F%7Bc"><div class="endpoint-info"><div>**URL:** `/v2/comments/{commentId}/{action}`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CommentController@likeOrDislike</div><div>**Middleware:** `authWithJwt`</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>commentId</td><td>integer</td><td>شناسه منحصر به فرد نظری که لایک یا دیسلایک می‌شود.</td></tr><tr><td>action</td><td>string</td><td>عملیات مورد نظر. این پارامتر باید یکی از دو مقدار زیر باشد: - `like`: برای افزایش شمارنده لایک.
- `dislike`: برای افزایش شمارنده دیسلایک.

</td></tr></tbody></table>

</div>### Request Body

این اندپوینت به هیچ داده‌ای در بدنه درخواست (Request Body) نیاز ندارد.

### Server-Side Logic

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7">- ابتدا سیستم با استفاده از متد `findOrFail`، نظر مربوط به `commentId` را پیدا می‌کند. اگر شناسه نامعتبر باشد، به طور خودکار خطای 404 بازگردانده می‌شود.
- اگر پارامتر `action` برابر با `'like'` باشد، فیلد `likes` یک واحد افزایش می‌یابد.
- اگر پارامتر `action` برابر با `'dislike'` باشد، فیلد `dislikes` یک واحد افزایش می‌یابد.
- در نهایت، تغییرات در دیتابیس ذخیره شده و شمارنده‌های جدید بازگردانده می‌شوند.

</div><div class="api-docs" id="bkmrk-%D8%B1%D9%81%D8%AA%D8%A7%D8%B1-%D8%AF%D8%B1-%D8%B5%D9%88%D8%B1%D8%AA-%D8%A7%D8%B1%D8%B3%D8%A7%D9%84-"><div class="api-notice warning" dir="rtl">**رفتار در صورت ارسال `action` نامعتبر:** اگر مقداری غیر از `'like'` یا `'dislike'` برای پارامتر `action` ارسال شود، هیچ خطایی رخ نمی‌دهد و هیچ شمارنده‌ای تغییر نمی‌کند. درخواست با موفقیت پردازش شده و مقادیر فعلی لایک و دیسلایک بازگردانده می‌شود.</div></div>### Success Response

در صورت موفقیت‌آمیز بودن عملیات، سرور یک پاسخ با کد 200 به همراه مقادیر به‌روز شده‌ی شمارنده‌های `likes` و `dislikes` برمی‌گرداند.

```json
{
    "status": true,
    "time": 1715000000,
    "data": {
        "likes": 25,
        "dislikes": 3
    }
}
```

### Error Response

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-404-%28not-found%29%3A-">- **کد 404 (Not Found):** در صورتی که `commentId` ارسال شده در URL در دیتابیس موجود نباشد.

</div>

# DELETE /v2/comments/{commentId}/{action}

# Remove a Like or Dislike from a Comment

این اندپوینت برای کاهش (حذف) یک لایک یا دیسلایک از یک نظر مشخص استفاده می‌شود. این عملیات، معکوس اندپوینت `POST` برای افزودن لایک/دیسلایک است و به کاربر اجازه می‌دهد تا رأی خود را پس بگیرد.

<div class="api-docs" id="bkmrk-">  ---

</div>## Decrement Like/Dislike Counter

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcomments%2F%7Bc"><div class="endpoint-info"><div>**URL:** `/v2/comments/{commentId}/{action}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CommentController@removeLikeOrDislike</div><div>**Middleware:** `authWithJwt`</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>commentId</td><td>integer</td><td>شناسه منحصر به فرد نظری که رأی آن باید حذف شود.</td></tr><tr><td>action</td><td>string</td><td>نوع رأیی که باید حذف شود. این پارامتر باید یکی از دو مقدار زیر باشد: - `like`: برای کاهش شمارنده لایک.
- `dislike`: برای کاهش شمارنده دیسلایک.

</td></tr></tbody></table>

</div>### Request Body

این اندپوینت به هیچ داده‌ای در بدنه درخواست (Request Body) نیاز ندارد.

### فلوچارت منطق سرور (Server Logic Flow)

روند پردازش درخواست در سمت سرور به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%86%D8%B8%D8%B1-%28find-com">1. **دریافت نظر (Find Comment):** سیستم با استفاده از `commentId` ارسالی در URL، نظر مربوطه را با متد `findOrFail` از دیتابیس جستجو می‌کند. 
    - اگر نظر پیدا نشود، به صورت خودکار پاسخ **404 Not Found** بازگردانده می‌شود.
2. **بررسی نوع عملیات (Check Action):** مقدار پارامتر `action` بررسی می‌شود.
3. **منطق کاهش شمارنده (Conditional Decrement):**
    - **اگر `action` برابر با `like` باشد:** سیستم ابتدا بررسی می‌کند که آیا شمارنده `likes` بزرگتر از صفر است (`$comment->likes > 0`). اگر شرط برقرار باشد، یک واحد از آن کم می‌شود. در غیر این صورت (اگر شمارنده صفر باشد)، هیچ تغییری اعمال نمی‌شود.
    - **اگر `action` برابر با `dislike` باشد:** سیستم بررسی می‌کند که آیا شمارنده `dislikes` بزرگتر از صفر است (`$comment->dislikes > 0`). اگر شرط برقرار باشد، یک واحد از آن کم می‌شود. در غیر این صورت، تغییری اعمال نمی‌شود.
4. **ذخیره تغییرات (Save Changes):** تغییرات روی مدل `Comment` در دیتابیس ذخیره می‌شود (`$comment->save()`).
5. **ارسال پاسخ نهایی (Return Response):** یک پاسخ موفقیت‌آمیز با کد 200 حاوی شمارنده‌های به‌روز شده بازگردانده می‌شود.

<div class="api-notice warning" dir="rtl">**رفتار محافظتی:** این منطق به طور هوشمند از منفی شدن شمارنده‌ها جلوگیری می‌کند. اگر شمارنده یک نظر از قبل صفر باشد و درخواستی برای کاهش آن ارسال شود، مقدار آن تغییری نکرده و همان صفر باقی می‌ماند.</div></div>### Success Response

در صورت موفقیت‌آمیز بودن عملیات، سرور یک پاسخ با کد 200 به همراه مقادیر به‌روز شده‌ی شمارنده‌های `likes` و `dislikes` برمی‌گرداند.

```json
{
    "status": true,
    "time": 1715000000,
    "data": {
        "likes": 24,
        "dislikes": 2
    }
}
```

### Error Response

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-404-%28not-found%29%3A-">- **کد 404 (Not Found):** در صورتی که `commentId` ارسال شده در URL در دیتابیس موجود نباشد.

</div>

# POST /v2/invoice/process

# Hub: Create Payment Invoice

این اندپوینت برای **ایجاد یک صورت‌حساب قابل پرداخت** طراحی شده است.   
فرآیند شامل ثبت یک "قبض" (Bill)، یافتن یک درگاه پرداخت فعال، تولید یک فاکتور منحصر به فرد با اسلاگ (Slug) یکتا، و در نهایت بازگرداندن یک لینک پرداخت برای کاربر است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Finvoice%2Fpro"><div class="endpoint-info"><div>**URL:** `/v2/invoice/process`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** HubController@createInvoice</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- اپراتور باید به شعبه (`branch`) مربوطه دسترسی داشته باشد (این اطلاعات توسط میدل‌ور به درخواست تزریق می‌شود).

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>price</td><td>integer</td><td>**(الزامی)** مبلغ کل صورت‌حساب به ریال. حداقل مبلغ مجاز **10,000** ریال است.</td></tr><tr><td>type</td><td>string</td><td>**(الزامی)** نوع پرداخت. در حال حاضر فقط مقدار `'credit'` پشتیبانی می‌شود.</td></tr><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه آبجکتی که این پرداخت به آن مرتبط است (مثلاً شناسه یک رزرو یا سفارش).</td></tr><tr><td>driver</td><td>string</td><td>**(اختیاری)** نام درایور درگاه پرداخت خاص (مانند `'zarinpal'` یا `'behpardakht'`). اگر ارسال نشود، درگاه پیش‌فرض شعبه انتخاب می‌شود.</td></tr><tr><td>return\_url</td><td>string</td><td>**(اختیاری)** آدرسی که کاربر پس از اتمام عملیات پرداخت به آن هدایت می‌شود.</td></tr><tr><td>operator</td><td>object</td><td>**(تزریق سیستمی)** آبجکت اپراتور لاگین شده که توسط میدل‌ور به درخواست اضافه می‌شود.</td></tr><tr><td>branch</td><td>integer</td><td>**(تزریق سیستمی)** شناسه شعبه‌ای که اپراتور به آن تعلق دارد.</td></tr></tbody></table>

</div>## Logic Details

فرآیند ایجاد لینک پرداخت دارای چندین مرحله کلیدی است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%A7%D9%88%D9%84%DB%8C%D9%87%3A-%D8%A8%D8%B1">1. **اعتبارسنجی اولیه:**
    - بررسی می‌شود که فیلدهای الزامی `price`, `type`, و `id` در درخواست وجود داشته باشند.
    - مبلغ `price` باید بزرگتر یا مساوی **10,000 ریال** باشد.
    - نوع پرداخت (`type`) باید دقیقاً برابر با `'credit'` باشد. در غیر این صورت خطا بازگردانده می‌شود.
2. **ثبت رکورد قبض (Bill):**
    - یک رکورد جدید در جدول `airplus_bills` ایجاد می‌شود.
    - این رکورد حاوی اطلاعات پایه پرداخت مانند مبلغ، شناسه اپراتور، شعبه و شناسه آبجکت مرتبط است. وضعیت اولیه آن `1` (فعال) ثبت می‌شود.
3. **انتخاب درگاه پرداخت (Gateway Selection):**
    - تابع کمکی `Functions::getGatewayConfig` فراخوانی می‌شود.
    - **اولویت اول:** اگر فیلد `driver` در درخواست ارسال شده باشد، سیستم به دنبال یک درگاه فعال با همان نام درایور برای آن شعبه می‌گردد.
    - **اولویت دوم (Fallback):** اگر `driver` ارسال نشده باشد، سیستم از جدول `office_config` به دنبال درگاه پیش‌فرض (`DEFAULT\_BANKING\_GATEWAY`) برای آن شعبه می‌گردد.
    - اگر هیچ درگاه فعالی (نه با درایور مشخص و نه به عنوان پیش‌فرض) یافت نشود، خطا بازگردانده می‌شود.
4. **ایجاد فاکتور نهایی (Invoice):**
    - با استفاده از تابع `Functions::generateSlugUnique`، یک رشته تصادفی و **منحصر به فرد** به طول ۸ کاراکتر به عنوان `slug` فاکتور تولید می‌شود. این تابع تضمین می‌کند که اسلاگ در جدول `airplus_invoices` تکراری نباشد.
    - یک رکورد جدید در جدول `airplus_invoices` ثبت می‌شود که حاوی اسلاگ، اطلاعات درگاه، مبلغ، و شناسه قبض (Bill) ایجاد شده در مرحله ۲ است.
5. **تولید لینک و ارسال پاسخ:**
    - یک URL پرداخت با استفاده از دامنه ثابت `https://ipg.airplus.app/invoice/payment/` و اسلاگ منحصر به فرد تولید می‌شود.
    - این URL به همراه مبلغ در قالب یک پاسخ موفقیت‌آمیز به کلاینت بازگردانده می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`
- **Body:** یک آبجکت حاوی لینک پرداخت نهایی.

</div>```json
{
    "payload": {
        "status": "payment_link",
        "amount": 50000,
        "url": "https://ipg.airplus.app/invoice/payment/aB3xZ7cR"
    },
    "meta": {
        "timestamp": 1715001200
    }
}
```

### پاسخ‌های خطا (نمونه)

<div class="api-docs" id="bkmrk-422-unprocessable-en">- `422 Unprocessable Entity`: خطای اعتبارسنجی ورودی‌ها (مانند عدم ارسال فیلدها یا مبلغ کم).
- `400 Bad Request`: نوع پرداخت نامعتبر است یا هیچ درگاه پرداخت فعالی برای شعبه یافت نشده است.

</div>```json
{
  "error": {
    "code": 1000,
    "message": "درگاه پرداخت فعال یافت نشد"
  }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-vali"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Validate Inputs (price, type, id)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Inputs Valid &amp; price &gt;= 10k?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error: 422</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-decision">type == 'credit'?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error: 400 Invalid Type</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e8f5e9;">Create record in `airplus_bills` table</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Get Gateway Config**  
1. Check for `driver` input  
2. Fallback to branch default</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Gateway Found?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Error: 400 No Gateway</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">**Generate Unique Slug**  
(Loop until unique in `airplus_invoices`)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Finalize Invoice**  
1. Insert into `airplus_invoices`  
2. Link to Bill &amp; Gateway</div><div class="flow-arrow">↓</div><div class="flow-item-process">Construct Payment URL</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 201 Created with URL</div></div></div>

# POST /v2/invoice/payment/details

# Hub: Get Invoice Payment Details

این اندپوینت برای **استعلام وضعیت و دریافت جزئیات تراکنش** یک فاکتور خاص استفاده می‌شود.   
معمولاً پس از بازگشت کاربر از درگاه بانک، کلاینت با ارسال `slug` فاکتور به این اندپوینت، وضعیت نهایی پرداخت (موفق یا ناموفق)، شماره پیگیری، شماره کارت و علت خطا (در صورت شکست) را دریافت می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Finvoice%2Fpay"><div class="endpoint-info"><div>**URL:** `/v2/invoice/payment/details`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** HubController@paymentSlugData</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>slug</td><td>string</td><td>**(الزامی)** شناسه منحصر به فرد (۸ کاراکتری) فاکتور که در مرحله ایجاد فاکتور تولید شده است.</td></tr></tbody></table>

</div>## Logic Details

فرآیند استعلام شامل مراحل دقیق زیر است:

<div class="api-docs" id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%DB%8C-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1%3A-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85">1. **جستجوی فاکتور:**
    - سیستم در جدول `airplus_invoices` به دنبال رکوردی با `slug` ارسال شده می‌گردد.
    - اگر رکوردی یافت نشود، پاسخ خطای `409` با پیام "سند ارسالی به بانک یافت نشد" بازگردانده می‌شود.
2. **پردازش داده‌های درگاه (Gateway Parsing):**
    - اگر فاکتور پیدا شد، ستون `result` (که حاوی پاسخ JSON از بانک است) پردازش می‌شود.
    - سیستم بر اساس نوع درگاه (`drive`)، اطلاعات **شماره پیگیری** و **شماره کارت** را استخراج می‌کند: 
        - **Behpardakht:** از فیلدهای `SaleReferenceId` و `CardHolderPan` استفاده می‌شود.
        - **Sep (Saman):** از فیلدهای `TraceNo` و `SecurePan` استفاده می‌شود.
    - همچنین رکورد قبض (Bill) مرتبط از جدول `airplus_bills` بازیابی می‌شود تا نوع سرویس (`object\_type`) مشخص گردد.
3. **بررسی وضعیت نهایی (Status Check):**
    - **حالت موفق (Status == 3):** اگر وضعیت فاکتور برابر با **3** باشد، پرداخت موفقیت‌آمیز بوده است. جزئیات کامل تراکنش با کد `200` ارسال می‌شود.
    - **حالت ناموفق (Status != 3):** اگر وضعیت هر چیزی غیر از 3 باشد: 
        1. پیام خطای دقیق از پاسخ بانک استخراج می‌شود (مثلاً `errorDesc` برای سامان یا پیام عمومی برای به‌پرداخت).
        2. پاسخ با کد وضعیت `409` ارسال می‌شود که حاوی جزئیات تراکنش + آبجکت `error` است.

</div>## Response Structure

### پاسخ موفق (پرداخت تایید شده)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok">- **Status Code:** `200 OK`

</div>```json
{
    "payload": {
        "type": "hotel",
        "amount": 1500000,
        "tracking_code": "17459821",
        "drive": "sep",
        "datetime": "2024-05-18 14:30:00",
        "card": "610433******1234",
        "return_url": "https://client-app.com/callback"
    },
    "meta": {
        "timestamp": 1716025200
    }
}
```

### پاسخ ناموفق (خطای پرداخت یا یافت نشدن)

<div class="api-docs" id="bkmrk-status-code%3A-409-con">- **Status Code:** `409 Conflict`
- در این حالت، بادی پاسخ همچنان شامل اطلاعات تراکنش (در صورت وجود) است تا به کاربر نمایش داده شود، اما یک آبجکت خطا نیز دارد.

</div>```json
{
    "payload": {
        "type": "flight",
        "amount": 5000000,
        "tracking_code": null,
        "drive": "behpardakht",
        "datetime": "2024-05-18 14:35:00",
        "card": null,
        "return_url": null
    },
    "meta": {
        "timestamp": 1716025500
    },
    "error": {
        "code": 1000,
        "message": "خطای مربوط به پرداخت"
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-find"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Find Invoice by `slug`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Invoice Found?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Return 409 (Not Found)</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Parse Gateway Data**  
Extract Tracking &amp; Card No based on driver (Sep/Behpardakht)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Status == 3 ?</div><div style="position: relative; padding-left: 50px; border-left: 2px dashed #ccc; margin-left: 50%; transform: translateX(-50%);"><div class="flow-arrow-label-left" style="top: -20px; left: -40px;">Yes</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK  
(Payment Details)</div><div class="flow-arrow-label-right" style="top: -45px; right: -30px;">No</div><div style="position: absolute; top: 40px; right: -80px; height: 50px; border-right: 2px dashed #ccc;">  
</div></div><div class="flow-arrow">↓ (No)</div><div class="flow-item-process" style="background-color: #ffebee;">**Extract Error Msg**  
Get specific error from gateway response</div><div class="flow-arrow">↓</div><div class="flow-item-error">Return 409 Conflict  
(Details + Error Obj)</div></div></div>

# POST /v2/invoice/payment/wallet

# Hub: Pay using Wallet

این اندپوینت یک فرآیند پرداخت ترکیبی را مدیریت می‌کند که به کاربر اجازه می‌دهد از **کیف پول داخلی شعبه** برای تسویه حساب استفاده کند.   
این مسیر دو سناریوی اصلی دارد:   
۱. **موجودی کافی:** مبلغ کامل از کیف پول کسر شده و تراکنش با موفقیت ثبت می‌شود.   
۲. **موجودی ناکافی:** موجودی کیف پول به طور کامل مصرف شده و برای باقیمانده مبلغ، یک لینک پرداخت آنلاین (از طریق اندپوینت `createInvoice`) ایجاد می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Finvoice%2Fpay"><div class="endpoint-info"><div>**URL:** `/v2/invoice/payment/wallet`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** HubController@payByWallet</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- اطلاعات اپراتور و شعبه (`operator`, `branch`) توسط میدل‌ور به درخواست تزریق می‌شود.

</div>## Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>**(الزامی)** نوع موجودیتی که پرداخت برای آن انجام می‌شود. اگر مقدار آن `'bill'` باشد، مبلغ از روی رکورد قبض محاسبه می‌شود.</td></tr><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه موجودیت (مثلاً شناسه قبض یا سفارش).</td></tr><tr><td>amount</td><td>integer</td><td>**(شرطی)** مبلغ کل به ریال. ارسال این فیلد تنها زمانی الزامی است که `type` مقداری غیر از `'bill'` داشته باشد.</td></tr><tr><td>operator</td><td>object</td><td>**(تزریق سیستمی)** آبجکت اپراتور لاگین شده.</td></tr><tr><td>branch</td><td>integer</td><td>**(تزریق سیستمی)** شناسه شعبه‌ای که اپراتور به آن تعلق دارد.</td></tr></tbody></table>

</div>## Logic Details

این اندپوینت فرآیند پیچیده‌ای را دنبال می‌کند که به دو شاخه اصلی تقسیم می‌شود:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D9%88-%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D9%87-">1. **اعتبارسنجی و محاسبه مبلغ:**
    - بررسی می‌شود که فیلدهای `type` و `id` ارسال شده باشند.
    - **اگر `type` برابر با `'bill'` باشد:**
        - رکورد مربوطه از جدول `airplus_bills` با وضعیت `1` (فعال) جستجو می‌شود.
        - مبلغ نهایی از فرمول `($bill->amount + $bill->tax) - $bill->discount` محاسبه می‌شود.
        - اگر صورتحساب یافت نشود، خطای `422` بازگردانده می‌شود.
    - **در غیر این صورت:** مبلغ از فیلد `amount` در درخواست خوانده می‌شود.
    - مبلغ محاسبه شده باید حداقل **10,000 ریال** باشد.
2. **بررسی موجودی کیف پول:**
    - تابع `getBalanceWallet` فراخوانی شده تا موجودی فعلی کیف پول شعبه (`branch`) را دریافت کند.
    - شرایط بررسی می‌شود: اگر مبلغ مورد نیاز از موجودی کیف پول بیشتر باشد، به سناریوی "موجودی ناکافی" می‌رویم.
3. **سناریو ۱: موجودی ناکافی**
    - مبلغ باقیمانده (`$amount - $balance`) محاسبه می‌شود.
    - اگر مبلغ باقیمانده کمتر از 10,000 ریال باشد، برای ایجاد لینک پرداخت، حداقل مبلغ 10,000 ریال در نظر گرفته می‌شود.
    - یک درخواست جدید به صورت داخلی ساخته شده و به متد `createInvoice` **فوروارد می‌شود** تا برای مبلغ باقیمانده، یک لینک پرداخت آنلاین تولید کند.
    - خروجی این اندپوینت در این حالت، دقیقاً مشابه خروجی اندپوینت `createInvoice` خواهد بود (یک لینک پرداخت).
4. **سناریو ۲: موجودی کافی**
    - وضعیت صورتحساب (در جدول `airplus_bills`) به `5` (پرداخت شده از کیف پول) تغییر می‌کند.
    - یک رکورد **بدهی (Debit)** جدید در جدول `wallet` برای شعبه ثبت می‌شود. این رکورد شامل شناسه اپراتور، مبلغ کسر شده و توضیحات تراکنش است.
    - پاسخ موفقیت‌آمیز با کد `201` بازگردانده می‌شود که حاوی شناسه تراکنش کیف پول است.

</div>## Response Structure

### پاسخ موفق (پرداخت کامل از کیف پول)

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`

</div>```json
{
    "payload": {
        "status": "succeed",
        "id": 542,
        "datetime": "2024-05-18 15:00:00"
    },
    "meta": {
        "timestamp": 1716027000
    }
}
```

### پاسخ موفق (پرداخت ترکیبی - موجودی ناکافی)

<div class="api-docs" id="bkmrk-status-code%3A-201-cre-1">- **Status Code:** `201 Created`
- در این حالت، پاسخ مشابه اندپوینت `createInvoice` است و شامل لینک پرداخت برای مبلغ باقیمانده می‌باشد.

</div>```json
{
    "payload": {
        "status": "payment_link",
        "amount": 450000,
        "url": "https://ipg.airplus.app/invoice/payment/kL9sW1aP"
    },
    "meta": {
        "timestamp": 1716027100
    }
}
```

### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-status-code%3A-422-unp">- **Status Code:** `422 Unprocessable Entity`
- برای خطاهای اعتبارسنجی مانند عدم ارسال فیلد، یافت نشدن صورتحساب یا مبلغ کمتر از حد مجاز.

</div>```json
{
  "error": {
    "code": 1000,
    "message": "صورتحساب یافت نشد."
  },
  "meta": {
    "timestamp": 1716027200
  }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-vali"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Validate Inputs &amp; Calculate Amount</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Inputs Valid &amp; Amount &gt;= 10k?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-error" style="float: right; margin-right: -140px; width: 120px;">Return 422 (Validation Error)</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e3f2fd;">Check Branch Wallet Balance</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Sufficient Balance?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: 0px; right: -30px;">No</div><div class="flow-arrow" style="float: right; margin-right: -100px;">↓</div><div class="flow-item-process" style="float: right; margin-right: -100px; background-color: #fff3e0;">Calculate Remainder</div><div class="flow-arrow" style="float: right; margin-right: -100px;">↓</div><div class="flow-item-process" style="float: right; margin-right: -100px; background-color: #fff3e0;">**Forward to `createInvoice`**</div><div class="flow-arrow" style="float: right; margin-right: -100px;">↓</div><div class="flow-item-success" style="float: right; margin-right: -100px;">Return 201 (Payment Link)</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e8f5e9;">Update Bill Status to 5</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">Create Debit Record in `wallet` table</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 201 (Succeed Status)</div></div></div>

# GET /v2/core/bill/{type}

# Hub: Get Branch Bills

این اندپوینت برای دریافت لیست **صورتحساب‌های پرداخت نشده** (`status = 1`) یک شعبه خاص طراحی شده است.   
کلاینت می‌تواند با ارسال نوع سرویس (مانند `hotel`, `flight` و...)، فقط صورتحساب‌های مربوط به آن سرویس را فیلتر و دریافت کند. خروجی شامل جزئیات کامل هر صورتحساب به همراه مبلغ نهایی قابل پرداخت است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fbill%2F%7B"><div class="endpoint-info"><div>**URL:** `/v2/core/bill/{type}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** HubController@indexBranchBill</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- شناسه شعبه (`branch`) به صورت خودکار از توکن کاربر استخراج و در کوئری استفاده می‌شود.

</div>## URL Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>**(الزامی)** نوع سرویسی که صورتحساب برای آن صادر شده است. مقادیر ممکن: `'hotel'`, `'flight'`, `'tour'` و غیره.</td></tr></tbody></table>

</div>## Logic Details

فرآیند دریافت صورتحساب‌ها به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA%3A-%D8%B4%D9%86%D8%A7">1. **استخراج اطلاعات:** شناسه شعبه (`branch`) از توکن JWT و نوع سرویس (`type`) از URL خوانده می‌شود.
2. **کوئری از دیتابیس:**
    - یک کوئری به جدول `airplus_bills` ارسال می‌شود.
    - این کوئری نتایج را بر اساس **سه شرط اصلی** فیلتر می‌کند: 
        1. `branch`: باید با شناسه شعبه کاربر لاگین کرده مطابقت داشته باشد.
        2. `status`: باید دقیقاً برابر با **1** باشد (فقط صورتحساب‌های در انتظار پرداخت).
        3. `object_type`: باید با پارامتر `type` ارسال شده در URL مطابقت داشته باشد.
3. **پردازش و تبدیل داده‌ها (`map`):**
    - اگر یک یا چند صورتحساب پیدا شود، سیستم روی هرکدام از آن‌ها یک فرآیند تبدیل اجرا می‌کند:
    - یک فیلد جدید به نام `total` با استفاده از فرمول `(((amount + tax) - discount) + markup)` محاسبه و به خروجی اضافه می‌شود.
    - یک آبجکت `details` ساخته می‌شود که حاوی `month_title` است. این فیلد با استفاده از تابع `CalendarUtils::strftime` نام فارسی ماه (مانند "آذر ماه") را از روی تاریخ ایجاد صورتحساب استخراج می‌کند.
    - ساختار نهایی هر آیتم برای خوانایی بیشتر در پاسخ API بازآرایی می‌شود.
4. **ساختار پاسخ نهایی:**
    - اگر هیچ صورتحسابی یافت نشود، پاسخ با کد `200 OK` بازگردانده شده و مقدار فیلد `items` برابر با `false` خواهد بود.
    - **نکته مهم:** بلوک کد مربوط به خطای `404` در کنترلر به دلیل نحوه عملکرد تابع `get()`، عملاً اجرا نمی‌شود و پاسخ همیشه `200` است.
    - اگر صورتحساب‌ها پیدا شوند، آرایه‌ای از آبجکت‌های تبدیل شده در فیلد `items` قرار می‌گیرد.

</div>## Response Structure

### پاسخ موفق (در صورت یافتن صورتحساب)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok">- **Status Code:** `200 OK`

</div>```json
{
    "items": [
        {
            "id": 101,
            "details": {
                "month_title": "آذر ماه"
            },
            "description": "بابت رزرو هتل اسپیناس پالاس",
            "amount": 5000000,
            "tax": 450000,
            "discount": 0,
            "markup": 0,
            "total": 5450000,
            "created_at": "2025-12-09 10:00:00",
            "updated_at": "2025-12-09 10:00:00",
            "expired_at": "2025-12-11 10:00:00"
        }
    ],
    "meta": {
        "timestamp": 1733735400
    }
}
```

### پاسخ موفق (در صورت عدم وجود صورتحساب)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- توجه کنید که در این حالت، فیلد `items` به جای آرایه خالی، مقدار `false` دارد.

</div>```json
{
    "items": false,
    "meta": {
        "timestamp": 1733735400
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get-%2F"><div class="flowchart"><div class="flow-item">Start Request (GET /v2/core/bill/{type})</div><div class="flow-arrow">↓</div><div class="flow-item-process">Extract `branch` from JWT &amp; `type` from URL</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Query `airplus_bills` WHERE:  
- `branch` = user_branch  
- `status` = 1  
- `object_type` = {type}</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Bills Collection is Empty?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: 0px; left: -30px;">Yes</div><div class="flow-arrow" style="float: left; margin-left: -100px;">↓</div><div class="flow-item-process" style="float: left; margin-left: -100px;">Set `items` field to `false`</div><div class="flow-arrow" style="float: left; margin-left: -100px;">↓</div><div class="flow-item-success" style="float: left; margin-left: -100px;">Return 200 OK</div></div><div class="flow-arrow">↓ (No)</div><div class="flow-item-process" style="background-color: #e8f5e9;">Map over results:  
- Calculate `total`  
- Generate `month_title`  
- Format structure</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with `items` array</div></div></div>

# GET /v2/core/hub/information

# Hub: Get Basic Information

این اندپوینت به عنوان یک مسیر عمومی برای دریافت اطلاعات پایه‌ای و مرکزی (Hub Information) عمل می‌کند. رفتار این اندپوینت بر اساس پارامتر `action` که در کوئری استرینگ ارسال می‌شود، تغییر می‌کند.   
در حال حاضر، این مسیر تنها برای دریافت لیست تمام **دفاتر (Offices)** پیاده‌سازی شده است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fin"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/information`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** HubController@getBasicInformation</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Query Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>action</td><td>string</td><td>**(اختیاری)** عملکرد مورد نظر را مشخص می‌کند. اگر مقدار آن برابر با `'offices'` باشد، لیست دفاتر برگردانده می‌شود. در غیر این صورت، خروجی متفاوت خواهد بود.</td></tr></tbody></table>

</div>## Logic Details

منطق این اندپوینت کاملاً به پارامتر `action` وابسته است:

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B4%D8%B1%D8%B7%3A-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A7%D8%A8%D8%AA">1. **بررسی شرط:** سیستم ابتدا بررسی می‌کند که آیا پارامتر `action` در درخواست وجود دارد و مقدار آن برابر با `'offices'` است یا خیر.
2. **سناریو ۱: اگر `action = 'offices'` باشد:**
    - یک کوئری به جدول `offices` در دیتابیس زده می‌شود و فیلدهای `id`, `brand_fa` و `brand_en` برای تمام رکوردها استخراج می‌شود.
    - نتایج با استفاده از متد `map` پردازش می‌شوند تا ساختار خروجی تغییر کند: 
        - فیلد `id` بدون تغییر باقی می‌ماند.
        - یک آبجکت جدید به نام `title` ایجاد می‌شود که دارای دو کلید `fa` و `en` است.
        - مقدار هر عنوان با فرمول زیر ساخته می‌شود:   
            `(شناسه دفتر + 10,000) - نام برند`   
            برای مثال، اگر `id=25` و `brand_fa='آژانس تابان'` باشد، خروجی `'10025 - آژانس تابان'` خواهد بود.
    - آرایه تبدیل شده در فیلد `items` پاسخ نهایی قرار می‌گیرد.
3. **سناریو ۲: اگر `action` ارسال نشود یا مقداری غیر از `'offices'` داشته باشد:**
    - شرط اصلی برقرار نمی‌شود و بلوک کد مربوط به کوئری دفاتر اجرا نمی‌گردد.
    - در این حالت، متغیر `$items` تعریف نمی‌شود.
    - **نکته کلیدی:** به دلیل تعریف نشدن متغیر `$items`، در پاسخ JSON نهایی، مقدار فیلد `items` برابر با **`null`** خواهد بود.

</div>## Response Structure

### پاسخ موفق (برای `action=offices`)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok">- **Status Code:** `200 OK`

</div>```json
{
    "items": [
        {
            "id": 25,
            "title": {
                "fa": "10025 - آژانس تابان",
                "en": "10025 - Taban Agency"
            }
        },
        {
            "id": 26,
            "title": {
                "fa": "10026 - آژانس سپهر",
                "en": "10026 - Sepehr Agency"
            }
        }
    ],
    "meta": {
        "timestamp": 1733736000
    }
}
```

### پاسخ برای سایر مقادیر `action` (یا عدم ارسال آن)

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- در این حالت، مقدار `items` برابر با `null` است.

</div>```json
{
    "items": null,
    "meta": {
        "timestamp": 1733736100
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get-%2F"><div class="flowchart"><div class="flow-item">Start Request (GET /v2/core/hub/information)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Query Param `action` == 'offices'?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">No</div><div class="flow-item-process" style="float: left; margin-left: -130px; background-color: #fff3e0;">`$items` is not initialized</div><div class="flow-arrow" style="float: left; margin-left: -130px;">↓</div><div class="flow-item-success" style="float: left; margin-left: -130px;">Return 200 OK with `items: null`</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e3f2fd;">Query `offices` table (select id, brand_fa, brand_en)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">Map results to new structure:  
`title.fa = (id + 10k) + ' - ' + brand_fa`  
`title.en = (id + 10k) + ' - ' + brand_en`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with formatted `items` array</div></div></div>

# GET /v2/core/hub/analyze

# Hub: Flight Reservation Analysis

این اندپوینت یک گزارش تحلیلی جامع از تمام رزروهای **پرواز آنلاین** (`product='online'`, `byproduct='aircraft'`) ارائه می‌دهد. داده‌ها بر اساس **سال شمسی**، **تأمین‌کننده (Supplier)**، و **تأمین‌کننده سیستمی (System Supplier)** دسته‌بندی و agregare می‌شوند. خروجی شامل تعداد کل رزروها و مجموع مبالغ خرید در هر دسته‌بندی است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fan"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/analyze`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** HubController@hubAnalysis</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- این گزارش به صورت سراسری (Global) است و به شعبه کاربر لاگین کرده محدود نمی‌شود.

</div>## Logic Details

فرآیند تحلیل داده‌ها در این اندپوینت طی چندین مرحله پیچیده انجام می‌شود:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%DB%8C-%D8%A7%D9%88%D9%84">1. **استخراج داده‌های اولیه:**
    - یک کوئری به جدول `factor_items` ارسال می‌شود تا تمام رکوردهایی که دارای شرایط زیر هستند، استخراج شوند: 
        - `product` برابر با `'online'`
        - `byproduct` برابر با `'aircraft'`
    - نتایج بر اساس `id` به صورت نزولی (`DESC`) مرتب می‌شوند تا ابتدا جدیدترین رزروها پردازش شوند.
2. **پردازش تک‌تک رزروها:**
    - سیستم در یک حلقه (loop) تمام رزروهای استخراج شده را پیمایش می‌کند.
    - برای هر رزرو، فیلد `details` که یک رشته JSON است، به آرایه PHP تبدیل می‌شود.
    - **اعتبارسنجی حیاتی:** سیستم بررسی می‌کند که آیا مسیر `$details['Book']['DepartureSegment']` در آبجکت JSON وجود دارد یا نه. اگر وجود نداشته باشد، آن رزرو از فرآیند تحلیل **حذف شده** و حلقه به تکرار بعدی می‌رود.
3. **گروه‌بندی و تجمیع داده‌ها (Aggregation):**
    - **استخراج تاریخ:** سال و ماه شمسی از فیلد `created_at` رزرو با استفاده از `CalendarUtils::strftime` استخراج می‌شود (مثلاً `1404-09`).
    - **استخراج تأمین‌کنندگان:** شناسه‌های `Supplier` و `SystemSupplier` از آبجکت `DepartureSegment` خوانده می‌شوند.
    - **کش کردن نام تأمین‌کننده:**
        - برای جلوگیری از کوئری‌های تکراری به دیتابیس، یک آرایه به نام `$colleagues` به عنوان کش داخلی عمل می‌کند.
        - هر بار که یک شناسه تأمین‌کننده جدید یافت می‌شود، سیستم یک بار به جدول `colleagues` کوئری می‌زند تا نام دفتر (`office`) را پیدا کند.
        - اگر نام دفتر پیدا شود، در کش ذخیره می‌شود. در غیر این صورت، خود **شناسه تأمین‌کننده** به عنوان نام پیش‌فرض استفاده می‌شود.
    - **افزایش شمارنده‌ها:** برای هر رزرو معتبر، شمارنده‌های `count` (تعداد) و `paid` (مجموع مبلغ خرید از فیلد `buy`) در سطوح مختلف ساختار داده افزایش می‌یابند: 
        1. مجموع کل (Grand Total)
        2. مجموع کل سال مربوطه
        3. مجموع کل تأمین‌کننده (Supplier) در آن سال
        4. مجموع ماهانه تأمین‌کننده (Supplier) در آن سال
        5. همین فرآیند به طور موازی برای تأمین‌کننده سیستمی (System Supplier) نیز تکرار می‌شود.
4. **ساختار نهایی خروجی:**
    - نتیجه نهایی یک آبجکت بسیار تودرتو است که داده‌ها را به تفکیک سال، تأمین‌کننده، و ماه نمایش می‌دهد.
    - این آبجکت در فیلد `payload` پاسخ نهایی قرار می‌گیرد.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- ساختار خروجی بسیار تودرتو است. کلیدهای آبجکت‌های `years`، `suppliers`، `system_suppliers` و `months` به ترتیب شناسه‌های سال، تأمین‌کننده، و ماه هستند.

</div>```json
{
    "payload": {
        "years": {
            "1404": { // سال شمسی
                "suppliers": {
                    "55": { // شناسه Supplier
                        "id": 55,
                        "title": "Ghasreshirin", // نام استخراج شده از جدول colleagues
                        "total": {
                            "count": 10, // کل رزروها از این Supplier در سال 1404
                            "paid": 95000000 // مجموع خرید از این Supplier در سال 1404
                        },
                        "months": {
                            "08": { // ماه شمسی
                                "total": {
                                    "count": 4,
                                    "paid": 38000000
                                }
                            },
                            "09": {
                                "total": {
                                    "count": 6,
                                    "paid": 57000000
                                }
                            }
                        }
                    }
                },
                "system_suppliers": {
                    "10": { // شناسه SystemSupplier
                        "id": 10,
                        "title": "Parsian System",
                        "total": {
                            "count": 10, // کل رزروها از این SystemSupplier در سال 1404
                            "paid": 95000000
                        },
                        "months": {
                            "08": { /* ... */ },
                            "09": { /* ... */ }
                        }
                    }
                },
                "total": { // مجموع کل برای سال 1404
                    "count": 10,
                    "paid": 95000000
                }
            }
        },
        "total": { // مجموع کل در تمام سال‌ها
            "count": 10,
            "paid": 95000000
        }
    },
    "meta": {
        "timestamp": 1733736600
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get-%2F"><div class="flowchart"><div class="flow-item">Start Request (GET /v2/core/hub/analyze)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Query `factor_items` WHERE `product`='online' AND `byproduct`='aircraft'</div><div class="flow-arrow">↓</div><div class="flow-item-process">Initialize `analyze` &amp; `colleagues` arrays</div><div class="flow-arrow">↓</div><div class="flow-item-loop">**For each reservation in results:**<div class="flow-sub-item"><div class="flow-item-process">Decode `details` JSON</div><div class="flow-arrow">↓</div><div class="flow-item-decision">`details['Book']['DepartureSegment']` exists?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">No</div><div class="flow-item-process" style="float: left; margin-left: -130px; background-color: #ffcdd2;">Continue to next reservation</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">Extract Year, Month, Supplier IDs</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is Supplier ID new?</div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #fff9c4;">Query `colleagues` table &amp; cache the title</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Aggregate Data:**  
Increment `count` &amp; `paid` at all levels (Grand, Year, Supplier, Month) for both Supplier &amp; SystemSupplier</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with `payload`</div></div></div>

# GET /v2/core/hub/reservation

# Hub: Get Master Reservation List

این اندپوینت یک لیست جامع و **صفحه‌بندی شده** از تمام رزروهای ثبت شده در سیستم مرکزی (Hub) را برمی‌گرداند. برای هر رزرو، اطلاعات از جداول مختلفی مانند `offices`، `factor_items`، `colleagues`، و `wallet` استخراج و با داده‌های اصلی ادغام می‌شود تا یک خروجی کامل و غنی‌شده ارائه دهد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fre"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/reservation`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** HubController@hubReservation</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Query Parameters (Custom Pagination)

این اندپوینت از یک ساختار صفحه‌بندی سفارشی استفاده می‌کند که معمولاً توسط کتابخانه‌های جدولی مانند (jQuery DataTables) ارسال می‌شود. پارامترها باید در یک آبجکت به نام `paginate` ارسال شوند.

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>paginate\[length\]</td><td>integer</td><td>**(الزامی)** تعداد آیتم‌های مورد نظر در هر صفحه.</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>**(الزامی)** آفست (offset) شروع رکوردها. برای صفحه اول `0`، برای صفحه دوم (با length=10)، `10` و به همین ترتیب.</td></tr></tbody></table>

</div>**Example URL:** `/v2/core/hub/reservation?paginate[length]=15&paginate[start]=0`

<div class="api-docs" id="bkmrk--1"></div>## Logic Details

فرآیند دریافت و پردازش رزروها بسیار گسترده است و شامل مراحل زیر می‌شود:

<div class="api-docs" id="bkmrk-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%A7%D9%88%D9%84%DB%8C%D9%87-%D9%88-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8">1. **کوئری اولیه و صفحه‌بندی:**
    - یک کوئری اصلی به جدول `hub_reservation` زده می‌شود.
    - این کوئری با جدول `offices` بر اساس `hub_reservation.branch` جوین می‌شود تا نام فارسی شعبه (`brand_fa`) استخراج شود.
    - نتایج بر اساس `id` به صورت نزولی مرتب می‌شوند.
    - با استفاده از پارامترهای `length` و `start`، صفحه‌بندی سفارشی روی نتایج اعمال می‌شود.
2. **حلقه غنی‌سازی داده‌ها (Data Enrichment Loop):**
    - سیستم روی هر یک از رزروهای برگشتی از کوئری اصلی، یک حلقه اجرا کرده و برای هرکدام، مجموعه‌ای از عملیات و کوئری‌های اضافی را انجام می‌دهد:
    - **نکته عملکردی (Performance Consideration):** این رویکرد (کوئری در حلقه) به مشکل **N+1 Query** منجر می‌شود و ممکن است با افزایش تعداد آیتم‌ها در هر صفحه، عملکرد را تحت تأثیر قرار دهد.
3. **مراحل داخل حلقه برای هر رزرو:**
    1. **واکشی آیتم فاکتور:** از جدول `factor_items`، آیتم مربوط به رزرو (`item_id`) واکشی می‌شود. اگر آیتم یافت نشود، کل آن رزرو از خروجی نهایی **حذف می‌شود**.
    2. **واکشی نام سرویس‌ها:** شناسه‌های عددی `service` و `sub_service` با اجرای کوئری روی جدول `colleagues` به نام متنی (فیلد `office`) تبدیل می‌شوند.
    3. **تولید عنوان آیتم (`title_item`):** با فراخوانی متد پیچیده `TradeController::getReferenceItemTitle`، یک عنوان توصیفی و قابل فهم برای آیتم (مانند "هواپیما تهران به مشهد | ۱۴۰۴/۰۹/۱۸ ۱۲:۳۰") تولید می‌شود. این متد به شدت از **کش Redis** برای بهینه‌سازی عملکرد استفاده می‌کند.
    4. **واکشی اطلاعات اپراتور:** با فراخوانی متد `StaticController::getOperators`، شناسه اپراتور به یک آبجکت کامل شامل نام، نام خانوادگی و سایر مشخصات تبدیل می‌شود. در صورت عدم وجود اپراتور، مقدار `false` بازگردانده می‌شود.
    5. **پردازش فیلدهای JSON:** رشته‌های JSON موجود در فیلدهای `supplier_details` و `details` به آبجکت/آرایه تبدیل می‌شوند.
    6. **ضمیمه کردن داده‌های جدید:** اطلاعات پردازش شده (نام سرویس، عنوان، اطلاعات اپراتور و...) به آبجکت اصلی رزرو اضافه می‌شوند.
    7. **بررسی استرداد (Refund):** سیستم در جدول `wallet` جستجو می‌کند تا ببیند آیا تراکنش استردادی (`target_type='refund'`) برای این رزرو ثبت شده است یا خیر. در صورت وجود، جزئیات آن به فیلد `refund` اضافه می‌شود؛ در غیر این صورت، این فیلد مقدار `false` می‌گیرد.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-200-ok-">- **Status Code:** `200 OK`
- فیلد `items` حاوی آرایه‌ای از آبجکت‌های رزرو غنی‌شده است.
- فیلد `meta.table` حاوی جزئیات کامل صفحه‌بندی است.

</div>```json
{
    "items": [
        {
            "id": 12345,
            "branch": "آژانس تابان", // از جدول offices
            "item_id": 54321,
            "operator": { // از متد getOperators
                "id": 101,
                "text": "1100 - علی اکبری",
                "query": { /* ... details ... */ }
            },
            "service": "Ghasreshirin", // نام تبدیل شده
            "sub_service": "Parsian System", // نام تبدیل شده
            "supplier_details": { /* آبجکت JSON */ },
            "details": { /* آبجکت JSON */ },
            "status": "issued",
            "created_at": "2025-12-09T12:00:00.000000Z",
            
            // --- Fields added in the loop ---
            "title_item": "هواپیما تهران به مشهد | ۱۴۰۴/۰۹/۱۸ ۱۲:۳۰ | W5065 | ماهان", // از getReferenceItemTitle
            "item": { // آبجکت کامل از factor_items
                "id": 54321,
                "product": "online",
                "byproduct": "aircraft",
                "details": { /* ... */ },
                "buy": 5400000,
                "sell": 5500000
                /* ... */
            },
            "refund": { // یا false اگر وجود نداشته باشد
                "id": 789,
                "credit": 5000000,
                "description": "استرداد بابت کنسلی پرواز"
            }
        }
    ],
    "meta": {
        "timestamp": 1733737200,
        "table": {
            "total": 500,
            "per_page": 15,
            "current_page": 1,
            "last_page": 34,
            "from": 1,
            "to": 15
        }
    }
}
```

<div class="api-docs" id="bkmrk--2"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get-%2F"><div class="flowchart"><div class="flow-item">Start Request (GET /v2/core/hub/reservation)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">Execute Paginated Query on `hub_reservation` with JOIN on `offices`.  
(Using custom `start`/`length` params)</div><div class="flow-arrow">↓</div><div class="flow-item-loop">**For each reservation in results:**<div class="flow-sub-item"><div class="flow-item-process">Query `factor_items` using `item_id`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Item Found?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">No</div><div class="flow-item-process" style="float: left; margin-left: -130px; background-color: #ffcdd2;">Skip to next reservation</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">Query `colleagues` for Service &amp; Sub-Service names</div><div class="flow-arrow">↓</div><div class="flow-item-process">Call `getReferenceItemTitle` (Cached Title Generation)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Call `getOperators` to format operator data</div><div class="flow-arrow">↓</div><div class="flow-item-process">Query `wallet` to find refund details</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">Assemble all new data into the final reservation object</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK with `items` array &amp; `meta.table`</div></div></div>

# POST /v2/core/hub/reservation

# Hub: Create Manual Wallet Transaction

این اندپوینت برای ثبت دستی یک تراکنش مالی (سند) در کیف پول (`wallet`) استفاده می‌شود. اگرچه URL به "رزرو" اشاره دارد، اما عملکرد اصلی آن ایجاد یک رکورد بدهکار (`debit`) یا بستانکار (`credit`) برای یک شعبه خاص است. این عملیات معمولاً توسط مدیران هاب برای اصلاح حساب یا ثبت سند دستی انجام می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fre"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/reservation`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** HubController@hubStoreReservation</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- شناسه اپراتور (`operator`) به صورت خودکار از توکن یا میدل‌ور استخراج می‌شود (`$request-&gt;get('operator')`).

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>branch</td><td>integer/string</td><td>**(الزامی)** شناسه شعبه‌ای که تراکنش برای آن ثبت می‌شود. این مقدار هم در فیلد `branch` و هم در فیلد `object` ذخیره می‌گردد.</td></tr><tr><td>target\_type</td><td>string</td><td>نوع مقصد تراکنش (مثلاً `user`, `office`, `provider`).</td></tr><tr><td>target</td><td>integer/string</td><td>شناسه مقصد تراکنش (ID مربوط به `target_type`).</td></tr><tr><td>diagnosis</td><td>string</td><td>**(الزامی)** نوع ماهیت تراکنش. مقادیر مجاز: - `'credit'`: افزایش اعتبار (بستانکار)
- `'debit'`: کاهش اعتبار (بدهکار)

</td></tr><tr><td>value</td><td>numeric</td><td>**(الزامی)** مبلغ تراکنش. بسته به `diagnosis` در ستون `credit` یا `debit` قرار می‌گیرد.</td></tr><tr><td>description</td><td>string</td><td>توضیحات تراکنش (بابت چه چیزی).</td></tr></tbody></table>

</div>## Logic Details

فرآیند ثبت در کنترلر به صورت زیر است:

<div class="api-docs" id="bkmrk-%D8%A2%D9%85%D8%A7%D8%AF%D9%87%E2%80%8C%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%3A-">1. **آماده‌سازی داده‌ها:**
    - فیلد `object_type` به صورت سخت‌کد شده برابر با `'branch'` قرار می‌گیرد.
    - فیلد `object` برابر با مقدار ارسالی `branch` تنظیم می‌شود.
    - شناسه اپراتور انجام‌دهنده عملیات از `$request->get('operator')->id` خوانده می‌شود.
2. **تعیین ماهیت مالی (Diagnosis Logic):**
    - اگر `diagnosis == 'credit'` باشد: مقدار `value` در ستون **credit** و ستون debit برابر 0 می‌شود.
    - اگر `diagnosis == 'debit'` باشد: مقدار `value` در ستون **debit** و ستون credit برابر 0 می‌شود.
3. **درج در دیتابیس:** آرایه ساخته شده مستقیماً با استفاده از `DB::table('wallet')->insert()` در جدول ذخیره می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`
- **Body:** Empty (بدون محتوا).

</div>### پاسخ خطا

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- در صورت بروز هرگونه Exception در فرآیند درج.

</div>```json
{
    "error": {
        "code": 1000,
        "message": "SQLSTATE[23000]: Integrity constraint violation..." // پیام خطا
    },
    "meta": {
        "timestamp": 1733737200
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28post%29"><div class="flowchart"><div class="flow-item">Start Request (POST)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Extract Operator from Request Attributes</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Check Diagnosis</div><div style="display: flex; justify-content: space-between; width: 300px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ (credit)</div><div class="flow-item-process" style="width: 120px; font-size: 12px;">Set Credit = Value  
Set Debit = 0</div></div><div style="text-align: center;"><div class="flow-arrow">↓ (debit)</div><div class="flow-item-process" style="width: 120px; font-size: 12px;">Set Debit = Value  
Set Credit = 0</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Prepare Insert Array  
(Set object_type='branch')</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::table('wallet')-&gt;insert(...)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Success?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">No</div><div class="flow-item-error" style="float: left; margin-left: -130px;">Return 400 Bad Request</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-success">Return 201 Created</div></div></div>

# PATCH /v2/core/hub/reservation/refund

# Hub: Process Reservation Refund

این اندپوینت برای انجام عملیات استرداد (Refund) یک رزرو ثبت‌شده در سیستم هاب استفاده می‌شود. فرآیند شامل محاسبه مبلغ جریمه (به صورت درصدی یا مبلغ ثابت)، ثبت مبلغ قابل بازگشت به عنوان یک تراکنش بستانکار در کیف پول شعبه، و در نهایت به‌روزرسانی وضعیت رزرو به "استرداد شده" است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fre"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/reservation/refund`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** HubController@hubRefund</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه (ID) رزروی که باید استرداد شود.</td></tr><tr><td>value\_type</td><td>string</td><td>**(الزامی)** نوع محاسبه جریمه. مقادیر مجاز: - `'percent'`: جریمه به صورت درصدی از مبلغ خرید (`buy`) رزرو محاسبه می‌شود.
- مقادیر دیگر (مثلاً `'fixed'`): جریمه به عنوان یک مبلغ ثابت در نظر گرفته می‌شود.

</td></tr><tr><td>value</td><td>numeric</td><td>**(الزامی)** مقدار جریمه. اگر `value_type` برابر `'percent'` باشد، این مقدار درصد است (مثلاً `10` برای ۱۰٪). در غیر این صورت، این مقدار مبلغ ریالی جریمه است.</td></tr><tr><td>description</td><td>string</td><td>*(اختیاری)* توضیحات اضافی که به انتهای توضیحات پیش‌فرض تراکنش کیف پول اضافه می‌شود.</td></tr></tbody></table>

</div>## Logic Details

فرآیند استرداد در کنترلر به صورت گام‌به‌گام زیر انجام می‌شود:

<div class="api-docs" id="bkmrk-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D8%B1%D8%B2%D8%B1%D9%88%3A-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A8%D8%A7">1. **یافتن رزرو:**
    - سیستم با استفاده از `id` ارسال شده، در جدول `hub_reservation` جستجو می‌کند تا رکورد رزرو مورد نظر را پیدا کند.
2. **محاسبه جریمه (Penalty):**
    - اگر `value_type` در درخواست برابر با `'percent'` باشد، مبلغ جریمه از فرمول زیر محاسبه می‌شود:  
        `$penalty = ($reservation->buy * $request->value) / 100`
    - در غیر این صورت، مقدار `value` ارسالی مستقیماً به عنوان مبلغ ثابت جریمه در نظر گرفته می‌شود.
3. **ایجاد تراکنش کیف پول (Wallet Transaction):**
    - یک رکورد جدید برای درج در جدول `wallet` آماده می‌شود:
    - **Mبلغ بستانکاری (credit):** برابر است با مبلغ خرید اولیه رزرو منهای جریمه محاسبه‌شده (`$reservation->buy - $penalty`).
    - **توضیحات (description):** یک رشته توصیفی به صورت خودکار ساخته می‌شود که شامل شناسه رزرو، مبلغ جریمه، و توضیحات اختیاری ارسال شده در درخواست است.
    - این رکورد در جدول `wallet` درج می‌شود و اعتبار به حساب شعبه بازمی‌گردد.
4. **به‌روزرسانی وضعیت رزرو:**
    - رکورد اصلی رزرو در جدول `hub_reservation` به‌روزرسانی می‌شود:
    - فیلد `refund_penalty` با مبلغ جریمه محاسبه‌شده پر می‌شود.
    - فیلد `status` به مقدار ثابت **۴** (به معنای استرداد شده) تغییر می‌کند.
    - فیلد `updated_at` با زمان فعلی به‌روز می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`
- **Body:** Empty (بدون محتوا).

</div>### پاسخ خطا

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- این خطا زمانی رخ می‌دهد که رزروی با `id` ارسال شده پیدا نشود.

</div>```json
{
    "error": {
        "code": 1000,
        "message": "رزرو یافت نشد."
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28patch"><div class="flowchart"><div class="flow-item">Start Request (PATCH /.../refund)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Query `hub_reservation` by `id`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Reservation Found?</div><div style="position: relative;"><div class="flow-arrow-label-left">No</div><div class="flow-item-error" style="float: left; margin-left: -150px;">Return 400 - "رزرو یافت نشد."</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-decision">Is `value_type` == 'percent'?</div><div style="display: flex; justify-content: space-around; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="width: 180px; font-size: 12px;">Calculate Penalty:  
`buy_price * value / 100`</div></div><div style="text-align: center;"><div class="flow-arrow">↓ (No)</div><div class="flow-item-process" style="width: 180px; font-size: 12px;">Set Penalty = `value` (fixed amount)</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Create `wallet` transaction (credit = buy_price - penalty)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update `hub_reservation` (status=4, refund_penalty=penalty)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 201 Created (Empty Body)</div></div></div>

# PATCH /v2/core/hub/reservation/refund/undo

# Hub: Undo Reservation Refund

این اندپوینت برای لغو یک عملیات استرداد (Refund) که قبلاً انجام شده، استفاده می‌شود. فرآیند شامل پیدا کردن تراکنش بستانکاری مربوط به استرداد، بررسی کافی بودن موجودی کیف پول شعبه برای بازگرداندن آن مبلغ، ایجاد یک تراکنش بدهکاری جدید برای خنثی کردن تراکنش قبلی، و در نهایت بازگرداندن وضعیت رزرو به حالت اولیه (فعال) است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fhub%2Fre"><div class="endpoint-info"><div>**URL:** `/v2/core/hub/reservation/refund/undo`</div><div>**Method:** <span class="method-patch">PATCH</span></div><div>**Controller:** HubController@hubUndoRefund</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- شناسه شعبه (`branch`) به صورت خودکار از درخواست (احتمالاً توسط میدلور) استخراج شده و برای بررسی موجودی استفاده می‌شود.

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>**(الزامی)** شناسه (ID) رزروی که عملیات استرداد آن باید لغو شود. سیستم از این شناسه برای یافتن تراکنش استرداد مرتبط در جدول `wallet` استفاده می‌کند.</td></tr></tbody></table>

</div>## Logic Details

فرآیند لغو استرداد به صورت گام‌به‌گام زیر است:

<div class="api-docs" id="bkmrk-%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-%D8%A7%D8%B3%D8%AA%D8%B1%D8%AF%D8%A7%D8%AF">1. **یافتن تراکنش استرداد:**
    - سیستم در جدول `wallet` به دنبال تراکنشی می‌گردد که شرایط زیر را داشته باشد: 
        - `target_type == 'refund'`
        - `target == request('id')` (شناسه رزرو)
        - `credit > 0` (تراکنش بستانکاری که پول را به شعبه بازگردانده است)
    - اگر چنین تراکنشی یافت نشود، خطای **400** با پیام "استرداد یافت نشد" بازگردانده می‌شود.
2. **بررسی موجودی کیف پول (Critical Step):**
    - قبل از برداشت وجه، سیستم با فراخوانی متد `AccountingController::getCheckWallet` موجودی شعبه را بررسی می‌کند.
    - این متد چک می‌کند که آیا موجودی فعلی شعبه (با در نظر گرفتن حد اعتبار یا `credit_limit`) برای پوشش مبلغ استرداد شده (`$transaction->credit`) کافی است یا خیر.
    - اگر موجودی کافی نباشد، خطای **400** با پیام "موجودی کیف پول جهت برگشت این آیتم کافی نیست" بازگردانده می‌شود.
3. **ایجاد تراکنش بدهکاری (Debit Transaction):**
    - یک رکورد جدید در جدول `wallet` برای برداشت مبلغ استرداد شده از حساب شعبه ایجاد می‌شود:
    - **Mبلغ بدهکاری (debit):** برابر است با مبلغ `credit` تراکنش استرداد اولیه.
    - **توضیحات (description):** متنی مانند "برگشت استرداد \[شناسه تراکنش\]" ثبت می‌شود.
    - این رکورد بدهکاری، تراکنش بستانکاری قبلی را خنثی می‌کند.
4. **بازگردانی وضعیت رزرو:**
    - رکورد اصلی رزرو در جدول `hub_reservation` به‌روزرسانی می‌شود:
    - فیلد `refund_penalty` به `null` تغییر می‌کند.
    - فیلد `status` به مقدار **۱** (به معنای فعال/صادر شده) بازمی‌گردد.
    - فیلد `updated_at` به‌روز می‌شود.

</div>## Response Structure

### پاسخ موفق

<div class="api-docs" id="bkmrk-status-code%3A-201-cre">- **Status Code:** `201 Created`
- **Body:** Empty (بدون محتوا).

</div>### پاسخ‌های خطا

<div class="api-docs" id="bkmrk-status-code%3A-400-bad">- **Status Code:** `400 Bad Request`
- این خطا در سه حالت ممکن است رخ دهد:

</div>**۱. تراکنش استرداد یافت نشد:**

```json
{
    "error": {
        "code": 1000,
        "message": "استرداد یافت نشد"
    }
}
```

**۲. موجودی کیف پول ناکافی است:**

```json
{
    "error": {
        "code": 1000,
        "message": "موجودی کیف پول جهت برگشت این آیتم کافی نیست."
    },
    "meta": {
        "timestamp": 1733737200
    }
}
```

**۳. بروز خطای عمومی (Exception):**

```json
{
    "error": {
        "code": 1000,
        "message": "General exception message..."
    },
    "meta": {
        "timestamp": 1733737200
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28patch"><div class="flowchart"><div class="flow-item">Start Request (PATCH /.../refund/undo)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Find Refund Transaction in `wallet` table</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Transaction Found?</div><div style="position: relative;"><div class="flow-arrow-label-left">No</div><div class="flow-item-error" style="float: left; margin-left: -150px;">Return 400 - "استرداد یافت نشد"</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-decision">Check Wallet Balance (getCheckWallet)</div><div style="position: relative;"><div class="flow-arrow-label-left">No (Insufficient)</div><div class="flow-item-error" style="float: left; margin-left: -150px;">Return 400 - "موجودی ناکافی"</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">Create Reversal `Debit` Transaction in `wallet`</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update `hub_reservation` (status=1, refund_penalty=null)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 201 Created</div></div></div>

# POST /v2/flights/ticket/information/{type}

# L. Flight Ticket Information (Nira)

این اندپوینت برای استعلام اطلاعات دقیق بلیط‌های صادر شده از طریق سیستم تامین‌کننده **نیرا (Nira)** استفاده می‌شود. این سرویس قابلیت جستجو بر اساس "شماره بلیط" یا "PNR" را دارد و به صورت هوشمند وضعیت استرداد (Refund) بلیط را بررسی کرده و در صورت نیاز، جزئیات مالی استرداد (جریمه و مبلغ پرداختی) را از سیستم نیرا استخراج می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fflights%2Ftic"><div class="endpoint-info"><div>**URL:** `/v2/flights/ticket/information/{type}`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2BaseController@ticketInformation</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- دسترسی بر اساس تنظیمات شعبه و سطح دسترسی کاربر کنترل می‌شود.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">type</td><td dir="ltr">string</td><td>**(الزامی)** نوع وب‌سرویس. برای این مستند باید مقدار `nira` باشد.</td></tr></tbody></table>

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">service</td><td dir="ltr">string</td><td>**(الزامی)** کد یاتا (IATA) ایرلاین (مثلاً `W5`). جهت تعیین کانکشن استفاده می‌شود.</td></tr><tr><td dir="ltr">ticket\_number</td><td dir="ltr">string</td><td>**(اختیاری)** شماره بلیط کامل. در صورت وجود، اولویت با این فیلد است.</td></tr><tr><td dir="ltr">pnr</td><td dir="ltr">string</td><td>**(اختیاری)** شماره رزرو. در صورت عدم ارسال شماره بلیط، سیستم بر اساس PNR جستجو می‌کند.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش در کنترلر به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B9%D8%AA%D8%A8%D8%A7%D8%B1%D8%B3%D9%86%D8%AC%DB%8C-%D8%A7%D9%88%D9%84%DB%8C%D9%87%3A-%D8%A7%DA%AF">1. **اعتبارسنجی اولیه:**
    - اگر پارامتر `type` برابر با `nira` نباشد، خطای `409` بازگردانده می‌شود.
    - تنظیمات اتصال (Api Url, Username, Password) بر اساس شناسه شعبه کاربر یا شعبه پیش‌فرض (1) بارگذاری می‌شود.
2. **سناریوی ۱: جستجو با شماره بلیط (Ticket Number):**
    - متد `ticket_information` نیرا فراخوانی می‌شود.
    - اگر `StatusCode == 'R'` (استرداد شده) باشد: 
        - یک درخواست کامند (`command`) با مقدار `DMB + ticket_number` ارسال می‌شود.
        - پاسخ متنی با استفاده از Regex (متد `getRefundedTicketData`) پارس شده و فیلدهای `RefundedAmount` و `Penalty` استخراج می‌شوند.
3. **سناریوی ۲: جستجو با PNR:**
    - متد `reserve_information` نیرا فراخوانی می‌شود.
    - روی تمام بلیط‌های موجود در رزرو (آرایه `Tickets`) حلقه زده می‌شود.
    - برای هر بلیط، منطق "بررسی استرداد" (مشابه سناریوی ۱) اجرا می‌شود.
    - **باگ شناسایی شده:** در کد فعلی، هنگام استعلام استرداد در حلقه PNR، به اشتباه از `$request->ticket_number` (که خالی است) استفاده می‌شود، در حالی که باید از شماره بلیط جاری حلقه استفاده شود.

</div>## Response Structure

### پاسخ موفق (200 OK)

```json
{
    "items": [
        {
            "TicketNo": "096-23651478",
            "PassengerName": "TEST USER",
            "StatusCode": "R",  // وضعیت استرداد
            "RefundData": {     // داده‌های استخراج شده استرداد
                "RefundDate": "14DEC2025",
                "Penalty": 500000,
                "RefundedAmount": 4500000
            },
            // ... سایر اطلاعات بلیط
        }
    ],
    "meta": {
        "timestamp": 1733738000
    }
}
```

### پاسخ خطا

<div class="api-docs" id="bkmrk-409-conflict%3A-%D8%A7%DA%AF%D8%B1-%D8%B3%D8%B1">- **409 Conflict:** اگر سرویس (type) اشتباه باشد.
- **500 Server Error:** خطای اتصال به وب‌سرویس.

</div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28post%29"><div class="flowchart"><div class="flow-item">Start Request (POST)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Check Type == 'nira'</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">No</div><div class="flow-item-error" style="float: left; margin-left: -130px;">Return 409 Conflict</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-decision">Has Ticket Number?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center; width: 48%;"><div class="flow-arrow">↓ (No - Use PNR)</div><div class="flow-item-process">Call 'reserve_information'</div><div class="flow-arrow">↓</div><div class="flow-item-process">Loop through Tickets</div><div class="flow-arrow">↓</div><div class="flow-item-process">Extract Ticket Data</div></div><div style="text-align: center; width: 48%;"><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process">Call 'ticket_information'</div><div class="flow-arrow">↓</div><div class="flow-item-process">Extract Single Ticket</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is Ticket Refunded? ('R')</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -30px;">No</div><div class="flow-arrow" style="float: right; margin-right: -50px;">→ Skip</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #fff3cd; border-color: #ffeeba; color: #856404;">Send Command 'DMB'  
Parse Regex for Penalty</div><div class="flow-arrow">↓</div><div class="flow-item-process">Enrich Response Item</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return 200 OK (JSON)</div></div></div>

# POST /v2/flights/routes/update

# M. Update Airline Active Routes (Manual Trigger)

این اندپوینت برای **بروزرسانی دستی جدول مسیرهای فعال (Airline Active Routes)** استفاده می‌شود. سیستم با فراخوانی این سرویس، موجودی پروازها را برای ۷ روز آینده (از فردا) به تفکیک روزهای هفته بررسی کرده و در دیتابیس ذخیره می‌کند. این فرآیند با ارسال درخواست‌های متوالی به وب‌سرویس تامین‌کننده (Nira) انجام می‌شود.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fflights%2Frou"><div class="endpoint-info"><div>**URL:** `/v2/flights/routes/update`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2BaseController@updateAirlineActiveRoute</div><div>**Logic Handler:** CronController::updateAirlineActiveRoute</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- این سرویس عملیات سنگینی است و مستقیماً با وب‌سرویس‌های خارجی درگیر می‌شود.

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">airline</td><td dir="ltr">array/string</td><td>**(اختیاری)** کد IATA ایرلاین‌ها. در صورت ارسال، بروزرسانی فقط برای این ایرلاین(ها) انجام می‌شود (مثلاً `['W5', 'NV']`).</td></tr><tr><td dir="ltr">route\_id</td><td dir="ltr">integer</td><td>**(اختیاری)** شناسه مسیر (ID) در جدول `approved_flight_rate`. اگر ارسال شود، پردازش فقط برای این مسیر خاص انجام می‌گیرد.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش در `CronController` بسیار پیچیده است و شامل مراحل زیر می‌باشد:

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%DA%A9%D8%B3%D8%A7%D8%B2%DB%8C-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D9%82%D8%A8%D9%84%DB%8C%3A-">1. **پاکسازی وضعیت قبلی:** کلید ردیس `airline_active_route:cron:route_id` در ابتدای کار حذف می‌شود تا پردازش از نو آغاز شود.
2. **واکشی مسیرها (Routes):**
    - از جدول `approved_flight_rate` مسیرهایی که مبدا و مقصد غیرفارسی دارند انتخاب می‌شوند.
    - فیلتر بر اساس `route_id` (در صورت وجود در ورودی) اعمال می‌شود.
3. **واکشی ایرلاین‌ها:**
    - از جدول `application_interface` رکوردهایی با تایپ `api` و سرویس `nira` که فعال هستند (`status=1`) دریافت می‌شوند.
    - اگر پارامتر `airline` ارسال شده باشد، لیست ایرلاین‌ها محدود می‌شود.
4. **حلقه پردازش (هفتگی):**
    - برای ۷ روز آینده (شروع از فردا) یک بازه زمانی ایجاد می‌شود.
    - به ازای هر مسیر و هر روز، متد `NiraApi->sendRequestFlight` فراخوانی می‌شود.
    - اگر `flight['Data']['Information']` حاوی داده باشد، وضعیت آن روز (مثلاً `monday`) برابر با **1** و در غیر این صورت 0 در نظر گرفته می‌شود.
5. **بروزرسانی دیتابیس (Upsert):**
    - داده‌ها در جدول `airline_active_route` ذخیره می‌شوند.
    - سیستم بررسی می‌کند که آیا رکوردی با ترکیب (Airline + Origin + Destination) وجود دارد یا خیر (بدون توجه به جهت مسیر).
    - اگر موجود باشد **Update** و اگر نباشد **Insert** انجام می‌شود.
6. **محدودیت پردازش:** یک شرط `break` وجود دارد: اگر تعداد ایرلاین‌های یافت شده بیش از ۱ مورد باشد، حلقه مسیرها (Routes) پس از اولین اجرا متوقف می‌شود (جهت جلوگیری از Timeout در پردازش‌های انبوه).

</div>## Response Structure

### پاسخ موفق (200 OK)

```json
{
    "payload": {
        "Status": true,
        "Time": 1733738500
    },
    "meta": {
        "timestamp": 1733738500
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28post%29"><div class="flowchart"><div class="flow-item">Start Request (POST)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Redis::del(cron_key)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Routes (approved_flight_rate)</div><div class="flow-item-process">Fetch Airlines (Nira API configs)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Loop Routes</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd; border-color: #90caf9; color: #0d47a1;">**Loop Period (Next 7 Days)**  
1. Call NiraApi-&gt;sendRequestFlight  
2. Check Availability (1 or 0)  
3. Store in Memory ($checkedRoutes)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">DB Check (airline_active_route)</div><div style="display: flex; justify-content: space-between; width: 300px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↙ (Exists)</div><div class="flow-item-process">Update Row</div></div><div style="text-align: center;"><div class="flow-arrow">↘ (New)</div><div class="flow-item-process">Insert Row</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Count(Airlines) &gt; 1 ?</div><div style="position: relative;"><div class="flow-arrow-label-left" style="top: -20px; left: -30px;">Yes</div><div class="flow-item-error" style="float: left; margin-left: -130px;">Break Loop (Prevent Timeout)</div></div><div class="flow-arrow">↓ (No / Continue)</div><div class="flow-item-success">Return JSON Payload</div></div></div>

# POST /v2/flights/routes/min_price

# N. Flight Route Min Price (Cache)

این اندپوینت برای دریافت **حداقل قیمت پرواز** در یک مسیر خاص (مبدا و مقصد) استفاده می‌شود. داده‌های این سرویس مستقیماً از **Redis Cache** خوانده می‌شوند و دو حالت عملکرد دارد: دریافت قیمت برای یک تاریخ خاص، یا دریافت لیست تمام قیمت‌های موجود (Calendar View) برای آن مسیر.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fflights%2Frou"><div class="endpoint-info"><div>**URL:** `/v2/flights/routes/min_price`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2BaseController@getRouteFlightsMinPrice</div><div>**Storage:** Redis (Read-Only)</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- این سرویس بسیار سریع است زیرا هیچ درخواستی به تامین‌کنندگان خارجی ارسال نمی‌کند و فقط با ردیس در ارتباط است.

</div>## Request Body Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">origin</td><td dir="ltr">string</td><td>**(الزامی)** کد IATA فرودگاه مبدا (مثلاً `MHD`).</td></tr><tr><td dir="ltr">destination</td><td dir="ltr">string</td><td>**(الزامی)** کد IATA فرودگاه مقصد (مثلاً `THR`).</td></tr><tr><td dir="ltr">date</td><td dir="ltr">string</td><td>**(اختیاری)** تاریخ پرواز (فرمت YYYY-MM-DD). اگر ارسال شود، فقط قیمت همان روز برگردانده می‌شود. اگر ارسال نشود، لیست تمام تاریخ‌های موجود بازگردانده می‌شود.</td></tr></tbody></table>

</div>## Logic Details

منطق کنترلر بر اساس وجود یا عدم وجود پارامتر `date` به دو شاخه تقسیم می‌شود:

<div class="api-docs" id="bkmrk-%D8%AD%D8%A7%D9%84%D8%AA-%D8%AA%DA%A9-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-%28speci">1. **حالت تک تاریخ (Specific Date):**
    - کلید ردیس به صورت مستقیم ساخته می‌شود: `min_price:flights:{origin}:{destination}:{date}`.
    - اگر کلید موجود باشد، مقدار آن (قیمت) در قالب آبجکت `payload` بازگردانده می‌شود.
    - اگر موجود نباشد، پاسخ JSON با خطای داخلی `code: 404` بازگردانده می‌شود.
2. **حالت کلی (Calendar View):**
    - اگر پارامتر `date` ارسال نشود، سیستم تمام کلیدهای منطبق با الگوی `min_price:flights:{origin}:{destination}:*` را جستجو می‌کند.
    - **مدیریت پیشوند ردیس:** کد به طور هوشمند Prefix اتصال ردیس را دریافت کرده و هنگام پردازش کلیدها، آن را حذف می‌کند (`str_replace`) تا به نام کلید خالص برسد.
    - تاریخ از بخش پنجم کلید (اینکس 4 در explode) استخراج شده و به همراه قیمت در آرایه `items` قرار می‌گیرد.

</div>## Response Structure

### حالت ۱: پاسخ موفق (تاریخ مشخص)

```json
{
    "payload": {
        "origin": "MHD",
        "destination": "THR",
        "date": "2025-12-10",
        "min_price": 1500000
    },
    "meta": {
        "timestamp": 1733739000
    }
}
```

### حالت ۲: پاسخ موفق (لیست کلی)

```json
{
    "items": [
        {
            "origin": "MHD",
            "destination": "THR",
            "date": "2025-12-10",
            "min_price": 1500000
        },
        {
            "origin": "MHD",
            "destination": "THR",
            "date": "2025-12-11",
            "min_price": 1450000
        }
    ],
    "meta": {
        "timestamp": 1733739000
    }
}
```

### پاسخ خطا (یافت نشد)

فقط در حالت "تک تاریخ" رخ می‌دهد:

```json
{
    "error": {
        "code": 404,
        "message": "not found."
    },
    "meta": {
        "timestamp": 1733739000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28post%29"><div class="flowchart"><div class="flow-item">Start Request (POST)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is 'date' provided?</div><div style="display: flex; justify-content: space-between; width: 450px; margin: 0 auto;"><div style="text-align: center; width: 48%;"><div class="flow-arrow">↙ (Yes)</div><div class="flow-item-process">Redis::get(Specific Key)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Found?</div><div style="display: flex; justify-content: space-between;"><div><div class="flow-arrow">No</div><div class="flow-item-error">Return 404 Error</div></div><div><div class="flow-arrow">Yes</div><div class="flow-item-success">Return Single Payload</div></div></div></div><div style="text-align: center; width: 48%;"><div class="flow-arrow">↘ (No)</div><div class="flow-item-process">Redis::keys(Pattern *)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd; border-color: #90caf9; color: #0d47a1;">Loop Keys:  
1. Remove Prefix  
2. Extract Date  
3. Get Price</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Items List</div></div></div></div></div>

# GET /v2/core/application_interface

# O. List Application Interfaces (Core)

این اندپوینت برای دریافت لیست **رابط‌های نرم‌افزاری (Application Interfaces)** استفاده می‌شود. خروجی این سرویس لیستی از شعب (Offices) است که تنظیمات و دسترسی‌های مربوطه (مانند APIهای ایرلاین‌ها، دسترسی همکاران و...) به عنوان زیرمجموعه آن‌ها گروه‌بندی شده‌اند. همچنین امکان دریافت مانده حساب کیف پول هر شعبه نیز به صورت اختیاری وجود دارد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@listApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- داده‌ها بر اساس رکوردهای موجود در جداول `application_interface` و `offices` تجمیع می‌شوند.

</div>## Query Parameters (Filters)

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td dir="ltr">integer</td><td>**(اختیاری)** شناسه شعبه. در صورت ارسال، خروجی فقط شامل همین شعبه و تنظیمات مربوط به آن خواهد بود.</td></tr><tr><td dir="ltr">service</td><td dir="ltr">string</td><td>**(اختیاری)** نام سرویس (مثلاً `nira`). فقط رابط‌های مربوط به این سرویس بازگردانده می‌شوند.</td></tr><tr><td dir="ltr">type</td><td dir="ltr">string</td><td>**(اختیاری)** نوع رابط (مثلاً `api`).</td></tr><tr><td dir="ltr">status</td><td dir="ltr">integer</td><td>**(اختیاری)** وضعیت فعال/غیرفعال بودن رابط (`1` یا `0`).</td></tr><tr><td dir="ltr">balance</td><td dir="ltr">boolean</td><td>**(اختیاری)** اگر مقدار `true` یا `1` ارسال شود، سیستم مانده حساب کیف پول (Wallet) هر شعبه را محاسبه و در خروجی قرار می‌دهد.</td></tr></tbody></table>

</div>## Logic Details

فرآیند پردازش داده‌ها شامل مراحل زیر است:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%B1%D8%A7%D8%A8%D8%B7%E2%80%8C">1. **دریافت و فیلتر رابط‌ها:** ابتدا داده‌ها از جدول `application_interface` بر اساس فیلترهای ورودی (branch, service, type, status) دریافت می‌شوند.
2. **غنی‌سازی داده‌ها (Data Mapping):**
    - اگر `object_type` برابر با `colleague` باشد، اطلاعات همکار (نام، سریال، آفیس) از مدل `Colleague` استخراج و جایگزین فیلد `object` می‌شود.
    - فیلدهای رشته‌ای ساده (service, type, object\_type) به ساختار استاندارد آرایه‌ای `{id, title}` تبدیل می‌شوند.
3. **گروه‌بندی:** داده‌های پردازش شده بر اساس شناسه شعبه (`branch`) گروه‌بندی می‌شوند.
4. **تجمیع با شعب (Offices):**
    - لیست شعب از جدول `offices` دریافت می‌شود (اگر فیلتر `branch` باشد، فقط همان شعبه).
    - برای هر شعبه، رابط‌های مربوطه از مرحله قبل در فیلد `items` قرار می‌گیرند.
5. **محاسبه مانده (Balance):**
    - اگر پارامتر `balance=true` باشد، متد `AccountingController::getBalanceWallet` فراخوانی می‌شود.
    - این متد مجموع `credit` و `debit` را از جدول `wallet` برای آن شعبه (با شرط `operator_type='erp'`) محاسبه می‌کند.
    - وضعیت حساب (diagnosis) به صورت `creditor` (بستانکار)، `debtor` (بدهکار) یا `neutral` تعیین می‌شود.

</div>## Response Structure

### نمونه پاسخ موفق (JSON)

```json
{
    "items": [
        {
            "id": 1,
            "title_fa": "دفتر مرکزی",
            "title_en": "Headquarters",
            "brand_fa": "برند نمونه",
            "items": [
                {
                    "id": 105,
                    "branch": 1,
                    "service": { "id": "nira", "title": "nira" },
                    "type": { "id": "api", "title": "api" },
                    "object_type": { "id": "colleague", "title": "colleague" },
                    "object": {
                        "id": 50,
                        "first_name": "Ali",
                        "last_name": "Rezaei",
                        "serial": "12345"
                    },
                    "status": 1
                }
            ],
            "balance": {
                "credit": 1000000,
                "debit": 200000,
                "balance": 800000,
                "diagnosis": "creditor"
            }
        }
    ],
    "meta": {
        "timestamp": 1733745000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get%29-"><div class="flowchart"><div class="flow-item">Start Request (GET)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch application_interface (Apply Filters)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd; border-color: #90caf9; color: #0d47a1;">**Map Data Loop:**  
1. Expand 'colleague' object  
2. Format fields to {id, title}</div><div class="flow-arrow">↓</div><div class="flow-item-process">Group Data by 'branch'</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Offices (DB)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Need Balance?</div><div style="display: flex; justify-content: space-between; width: 350px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↙ (Yes)</div><div class="flow-item-process">Calc Wallet (Credit - Debit)</div></div><div style="text-align: center;"><div class="flow-arrow">↘ (No)</div><div class="flow-item-process">Skip</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Merged JSON</div></div></div>

# GET /v2/core/application_interface/{id}

# P. Show Application Interface (Single Item)

این اندپوینت برای دریافت **جزئیات کامل یک رابط نرم‌افزاری خاص** استفاده می‌شود. با ارسال شناسه (ID) رکورد، سیستم اطلاعات آن را از دیتابیس استخراج کرده و پس از استانداردسازی فیلدها و غنی‌سازی اطلاعات (مانند اطلاعات همکار)، خروجی را بازمی‌گرداند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@showApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- شناسه ارسالی باید در جدول `application_interface` موجود باشد.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td dir="ltr">integer</td><td>**(الزامی)** شناسه منحصر به فرد (Primary Key) رکورد در جدول رابط‌های نرم‌افزاری.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش این متد به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88-%D8%AF%D8%B1-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3%3A-%D8%A7%D8%A8">1. **جستجو در دیتابیس:** ابتدا با استفاده از `DB::table('application_interface')->find($id)` رکورد مورد نظر بازیابی می‌شود.
2. **بررسی نوع آبجکت (Object Expansion):**
    - اگر `object_type` برابر با `colleague` باشد، سیستم شناسه موجود در فیلد `object` را برداشته و اطلاعات همکار (نام، نام خانوادگی، سریال، دفتر و وضعیت) را از مدل `Colleague` جستجو می‌کند.
    - نتیجه جایگزین مقدار عددی فیلد `object` می‌شود.
3. **استانداردسازی فرمت (Data Formatting):**
    - فیلدهای `object_type`، `service` و `type` که به صورت رشته ساده هستند، به آرایه‌ای شامل `id` و `title` تبدیل می‌شوند تا ساختار پاسخ یکپارچه باشد (Front-end friendly).
4. **خروجی:** داده نهایی درون آبجکت `payload` قرار گرفته و ارسال می‌شود.

</div>## Response Structure

### نمونه پاسخ موفق (200 OK)

```json
{
    "payload": {
        "id": 105,
        "branch": 1,
        "service": {
            "id": "nira",
            "title": "nira"
        },
        "type": {
            "id": "api",
            "title": "api"
        },
        "object_type": {
            "id": "colleague",
            "title": "colleague"
        },
        "object": {
            "id": 50,
            "first_name": "Ali",
            "last_name": "Rezaei",
            "office": 1,
            "status": 1,
            "serial": "12345"
        },
        "status": 1,
        "created_at": "2024-01-01 12:00:00"
    },
    "meta": {
        "timestamp": 1733748000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28get-%7B"><div class="flowchart"><div class="flow-item">Start Request (GET {id})</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::find($id)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is object_type == 'colleague'?</div><div style="display: flex; justify-content: space-between; width: 350px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↙ (Yes)</div><div class="flow-item-process">Fetch Colleague Data  
&amp; Replace Object</div></div><div style="text-align: center;"><div class="flow-arrow">↘ (No)</div><div class="flow-item-process">Keep Original Object ID</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd; border-color: #90caf9; color: #0d47a1;">Format Fields:  
Convert Strings to {id, title} Structure</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Payload</div></div></div>

# DELETE /v2/core/application_interface/{id}

# Q. Delete Application Interface (Core)

این اندپوینت برای **حذف یک رابط نرم‌افزاری** از سیستم استفاده می‌شود. عملیات حذف به صورت مستقیم بر روی دیتابیس انجام شده و غیرقابل بازگشت است (Hard Delete).

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CoreController@deleteApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- دسترسی مدیریتی برای حذف رکوردها الزامی است.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td dir="ltr">integer</td><td>**(الزامی)** شناسه منحصر به فرد (Primary Key) رکورد که باید حذف شود.</td></tr></tbody></table>

</div>## Logic Details

منطق پردازش این متد بسیار ساده و مستقیم است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%D8%AF%D8%B3%D8%AA%D9%88%D8%B1-%D8%AD%D8%B0%D9%81%3A-%D9%85%D8%AA%D8%AF">1. **اجرای دستور حذف:** متد به صورت مستقیم کوئری `DELETE` را بر روی جدول `application_interface` با شرط `id` اجرا می‌کند.
2. **نتیجه عملیات:** خروجی تابع دیتابیس، تعداد ردیف‌های حذف شده است (معمولاً `1` در صورت وجود رکورد و `0` در صورت عدم وجود).
3. **پاسخ‌دهی:** تعداد ردیف‌های حذف شده به عنوان `payload` بازگردانده می‌شود.

</div>## Response Structure

### نمونه پاسخ موفق (JSON)

در این نمونه، عدد `1` نشان‌دهنده حذف موفقیت‌آمیز یک رکورد است.

```json
{
    "payload": 1,
    "meta": {
        "timestamp": 1733751000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28delet"><div class="flowchart"><div class="flow-item">Start Request (DELETE {id})</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::table('application_interface')-&gt;delete($id)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Deleted Count (int)</div></div></div>

# POST /v2/core/application_interface

# Store Application Interface (Create New)

این اندپوینت برای **ایجاد یک رابط نرم‌افزاری جدید** در سیستم استفاده می‌شود. نکته حائز اهمیت در این متد، نحوه ارسال پارامترهای دسته‌بندی است؛ فیلدهایی مانند نوع و سرویس باید به صورت آبجکت ارسال شوند تا سیستم بتواند عنوان (Title) آن‌ها را استخراج و ذخیره کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CoreController@storeApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Body Parameters

پارامترهای زیر باید در بدنه درخواست (Body) به صورت JSON ارسال شوند:

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td dir="ltr">integer</td><td>شناسه شعبه مربوطه.</td></tr><tr><td dir="ltr">type</td><td dir="ltr">object</td><td>**(ساختار خاص)** باید شامل کلید `title` باشد.   
مثال: `{"title": "api"}`</td></tr><tr><td dir="ltr">service</td><td dir="ltr">object</td><td>**(ساختار خاص)** باید شامل کلید `title` باشد.   
مثال: `{"title": "nira"}`</td></tr><tr><td dir="ltr">object\_type</td><td dir="ltr">object</td><td>**(ساختار خاص)** نوع موجودیت هدف. باید شامل کلید `title` باشد.   
مثال: `{"title": "colleague"}`</td></tr><tr><td dir="ltr">object</td><td dir="ltr">object | null</td><td>موجودیت هدف. باید شامل کلید `id` باشد. اگر ارسال نشود، `null` ذخیره می‌شود.   
مثال: `{"id": 50}`</td></tr><tr><td dir="ltr">url</td><td dir="ltr">string</td><td>آدرس URL رابط (در صورت وجود).</td></tr><tr><td dir="ltr">username</td><td dir="ltr">string</td><td>نام کاربری اتصال.</td></tr><tr><td dir="ltr">password</td><td dir="ltr">string</td><td>کلمه عبور اتصال.</td></tr><tr><td dir="ltr">data</td><td dir="ltr">string/text</td><td>سایر داده‌های پیکربندی (معمولاً JSON String یا کلید API).</td></tr><tr><td dir="ltr">priority</td><td dir="ltr">integer</td><td>اولویت رابط.</td></tr><tr><td dir="ltr">status</td><td dir="ltr">integer</td><td>وضعیت فعال (1) یا غیرفعال (0).</td></tr></tbody></table>

</div>## Logic Details

فرآیند ذخیره‌سازی به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7%3A-%D9%85%D9%82%D8%A7">1. **استخراج داده‌ها:** مقادیر از آبجکت‌های ورودی استخراج می‌شوند: 
    - `type['title']` → ذخیره در ستون `type`
    - `service['title']` → ذخیره در ستون `service`
    - `object_type['title']` → ذخیره در ستون `object_type`
    - `object['id']` → ذخیره در ستون `object` (اگر موجود نباشد Null).
2. **درج در دیتابیس:** رکورد جدید با دستور `insertGetId` ایجاد شده و شناسه (ID) تولید شده دریافت می‌شود.
3. **بازیابی:** بلافاصله پس از درج، رکورد کامل با استفاده از ID جدید از دیتابیس خوانده می‌شود (`find($id)`).
4. **خروجی:** رکورد ذخیره شده به عنوان پاسخ بازگردانده می‌شود.

</div>## Response Structure

### نمونه Body ارسالی (Request)

```json
{
    "branch": 1,
    "type": { "id": "api", "title": "api" },
    "service": { "id": "nira", "title": "nira" },
    "object_type": { "id": "colleague", "title": "colleague" },
    "object": { "id": 50, "name": "Ali..." },
    "url": "https://api.example.com",
    "username": "user1",
    "password": "pass123",
    "data": "API_KEY_STRING",
    "priority": 0,
    "status": 1
}
```

### نمونه پاسخ موفق (200 OK)

```json
{
    "payload": {
        "id": 106,
        "branch": 1,
        "type": "api",
        "service": "nira",
        "object_type": "colleague",
        "object": 50,
        "url": "https://api.example.com",
        "username": "user1",
        "password": "pass123",
        "data": "API_KEY_STRING",
        "priority": 0,
        "status": 1,
        "created_at": null,
        "updated_at": null
    },
    "meta": {
        "timestamp": 1733752000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28post-"><div class="flowchart"><div class="flow-item">Start Request (POST Data)</div><div class="flow-arrow">↓</div><div class="flow-item-process highlight">Extract Properties:  
type -&gt; title  
service -&gt; title  
object -&gt; id</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::insertGetId(...)</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::find($new_id)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return New Record</div></div></div>

# PUT /v2/core/application_interface/{id}

# Update Application Interface

این اندپوینت برای **ویرایش اطلاعات یک رابط نرم‌افزاری موجود** استفاده می‌شود. با ارسال شناسه رکورد و داده‌های جدید، سیستم رکورد را بروزرسانی می‌کند. همانند متد ثبت، پارامترهای دسته‌بندی باید به صورت آبجکت ارسال شوند تا سیستم بتواند مقادیر مورد نیاز (Title یا ID) را از آن‌ها استخراج کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface/{id}`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** CoreController@updateApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.
- شناسه ارسالی در URL باید معتبر باشد.

</div>## Path Parameters

<div class="api-docs" id="bkmrk-field-type-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td dir="ltr">integer</td><td>**(الزامی)** شناسه رکورد مورد نظر برای ویرایش.</td></tr></tbody></table>

</div>## Body Parameters

ساختار بادی درخواست مشابه متد ثبت (Store) است:

<div class="api-docs" id="bkmrk-field-type-descripti-1"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td dir="ltr">integer</td><td>شناسه شعبه.</td></tr><tr><td dir="ltr">type</td><td dir="ltr">object</td><td>باید شامل کلید `title` باشد. مقدار این کلید در دیتابیس ذخیره می‌شود.</td></tr><tr><td dir="ltr">service</td><td dir="ltr">object</td><td>باید شامل کلید `title` باشد.</td></tr><tr><td dir="ltr">object\_type</td><td dir="ltr">object</td><td>باید شامل کلید `title` باشد (مثلاً colleague).</td></tr><tr><td dir="ltr">object</td><td dir="ltr">object | null</td><td>باید شامل کلید `id` باشد. اگر نال باشد یا ارسال نشود، مقدار دیتابیس `NULL` می‌شود.</td></tr><tr><td dir="ltr">url, username, password, data</td><td dir="ltr">string</td><td>اطلاعات اتصال و تنظیمات (رشته ساده).</td></tr><tr><td dir="ltr">priority, status</td><td dir="ltr">integer</td><td>اولویت و وضعیت فعال بودن.</td></tr></tbody></table>

</div>## Logic Details

فرآیند بروزرسانی به شرح زیر است:

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%AF%D8%A7%D8%AF">1. **دریافت و استخراج داده‌ها:** مقادیر ورودی پردازش می‌شوند: 
    - برای فیلدهای `type`، `service` و `object_type` مقدار `['title']` برداشته می‌شود.
    - برای فیلد `object` مقدار `['id']` برداشته می‌شود (با استفاده از عملگر Null Coalescing).
2. **بروزرسانی دیتابیس:** دستور `UPDATE` بر روی جدول `application_interface` برای رکوردی که `id` آن برابر با پارامتر مسیر است اجرا می‌شود.
3. **بازیابی مجدد:** پس از اعمال تغییرات، رکورد بروزرسانی شده با `find($id)` فراخوانی می‌شود تا تغییرات منعکس شده و داده‌ها بازگردانده شوند.

</div>## Response Structure

### نمونه Body ارسالی (JSON)

```json
{
    "branch": 1,
    "type": { "id": "api", "title": "api" },
    "service": { "id": "parto", "title": "parto" },
    "object_type": { "id": "branch", "title": "branch" },
    "object": { "id": 2 },
    "url": "https://new-api.com",
    "username": "admin",
    "password": "newpass",
    "data": "{}",
    "priority": 5,
    "status": 1
}
```

### نمونه پاسخ موفق (200 OK)

```json
{
    "payload": {
        "id": 105,
        "branch": 1,
        "type": "api",
        "service": "parto",
        "object_type": "branch",
        "object": 2,
        "url": "https://new-api.com",
        "username": "admin",
        "password": "newpass",
        "data": "{}",
        "priority": 5,
        "status": 1,
        "created_at": "2024-01-01...",
        "updated_at": null
    },
    "meta": {
        "timestamp": 1733753000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28put-%7B"><div class="flowchart"><div class="flow-item">Start Request (PUT {id})</div><div class="flow-arrow">↓</div><div class="flow-item-process highlight">Extract Properties:  
type['title'], service['title']  
object['id'] ?? null</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::table(...)-&gt;where('id', $id)-&gt;update(...)</div><div class="flow-arrow">↓</div><div class="flow-item-process">DB::find($id)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Updated Record</div></div></div>

# GET /v2/core/application_interface_types

# List Application Interface Types

این اندپوینت لیست ثابت و از پیش تعریف‌شده‌ای از **انواع (Types)** قابل قبول برای رابط‌های نرم‌افزاری را بازمی‌گرداند. این لیست معمولاً برای پر کردن Dropdown‌ها در فرم‌های ایجاد یا ویرایش (اندپوینت‌های R و S) استفاده می‌شود تا کاربر یکی از مقادیر مجاز را انتخاب کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface_types`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@listApplicationInterfacTypes</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Logic Details

این متد هیچ پردازش دیتابیسی انجام نمی‌دهد و یک آرایه استاتیک از مقادیر زیر را برمی‌گرداند:

<div class="api-docs" id="bkmrk-id-%2F-title-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>ID / Title</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">recaptcha</td><td>سرویس کپچا گوگل یا مشابه.</td></tr><tr><td dir="ltr">sms</td><td>پنل‌های ارسال پیامک.</td></tr><tr><td dir="ltr">ami</td><td>رابط‌های Asterisk Manager Interface (VoIP).</td></tr><tr><td dir="ltr">ftp</td><td>پروتکل انتقال فایل.</td></tr><tr><td dir="ltr">support</td><td>سیستم‌های پشتیبانی/تیکتینگ.</td></tr><tr><td dir="ltr">api</td><td>سایر APIهای عمومی وب‌سرویس.</td></tr><tr><td dir="ltr">smtp</td><td>پروتکل ارسال ایمیل.</td></tr></tbody></table>

</div>## Response Structure

### نمونه پاسخ موفق (200 OK)

```json
{
    "items": [
        { "id": "recaptcha", "title": "recaptcha" },
        { "id": "sms", "title": "sms" },
        { "id": "ami", "title": "ami" },
        { "id": "ftp", "title": "ftp" },
        { "id": "support", "title": "support" },
        { "id": "api", "title": "api" },
        { "id": "smtp", "title": "smtp" }
    ],
    "meta": {
        "timestamp": 1733754000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-load"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Load Static Data Array  
(recaptcha, sms, api, ...)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>

# GET /v2/core/application_interface_services

# List Application Interface Services

این اندپوینت لیست ثابت و از پیش تعریف‌شده‌ای از **سرویس‌ها (Services)** قابل انتخاب برای رابط‌های نرم‌افزاری را بازمی‌گرداند. این مقادیر مشخص می‌کنند که یک رابط خاص (مثلاً یک API Key) مربوط به کدام سرویس‌دهنده یا پلتفرم خارجی است.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface_services`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@listApplicationInterfacServices</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Logic Details

این متد هیچ پردازش دیتابیسی انجام نمی‌دهد و یک آرایه استاتیک شامل لیست سرویس‌دهندگان پشتیبانی شده را برمی‌گرداند:

<div class="api-docs" id="bkmrk-id-%2F-title-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>ID / Title</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">airplus</td><td>وب‌سرویس خدمات پرواز ایرپلاس.</td></tr><tr><td dir="ltr">google</td><td>سرویس‌های گوگل (مانند Recaptcha).</td></tr><tr><td dir="ltr">ami</td><td>رابط مدیریت استریسک (Asterisk Manager Interface).</td></tr><tr><td dir="ltr">gmini</td><td>سرویس Gmini.</td></tr><tr><td dir="ltr">irnoti</td><td>سامانه پیامکی Irnoti.</td></tr><tr><td dir="ltr">issabel</td><td>سیستم تلفنی ایزابل (VoIP).</td></tr><tr><td dir="ltr">goftino</td><td>پلتفرم چت آنلاین گفتینو.</td></tr><tr><td dir="ltr">navasan</td><td>سرویس دریافت نرخ ارز.</td></tr><tr><td dir="ltr">ravis</td><td>وب‌سرویس گردشگری راویس.</td></tr><tr><td dir="ltr">sepehr</td><td>سیستم رزرواسیون سپهر.</td></tr><tr><td dir="ltr">nira</td><td>سیستم رزرواسیون نیرا سافت.</td></tr><tr><td dir="ltr">liara</td><td>سرویس ابری لیارا (Object Storage و ...).</td></tr><tr><td dir="ltr">tport</td><td>پلتفرم T-Port (گردشگری).</td></tr><tr><td dir="ltr">sepehr\_hotel</td><td>سرویس هتل سیستم سپهر.</td></tr><tr><td dir="ltr">jibit</td><td>خدمات بانکی و پرداخت جیبیت.</td></tr><tr><td dir="ltr">snapptrip\_hotel</td><td>سرویس هتل اسنپ‌تریپ.</td></tr></tbody></table>

</div>## Response Structure

### نمونه پاسخ موفق (200 OK)

```json
{
    "items": [
        { "id": "airplus", "title": "airplus" },
        { "id": "google", "title": "google" },
        { "id": "ami", "title": "ami" },
        { "id": "gmini", "title": "gmini" },
        { "id": "irnoti", "title": "irnoti" },
        { "id": "issabel", "title": "issabel" },
        { "id": "goftino", "title": "goftino" },
        { "id": "navasan", "title": "navasan" },
        { "id": "ravis", "title": "ravis" },
        { "id": "sepehr", "title": "sepehr" },
        { "id": "nira", "title": "nira" },
        { "id": "liara", "title": "liara" },
        { "id": "tport", "title": "tport" },
        { "id": "sepehr_hotel", "title": "sepehr_hotel" },
        { "id": "jibit", "title": "jibit" },
        { "id": "snapptrip_hotel", "title": "snapptrip_hotel" }
    ],
    "meta": {
        "timestamp": 1733754000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-load"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Load Static Service List  
(airplus, google, nira, ...)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>

# GET /v2/core/application_interface_object_types

# List Application Interface Object Types

این اندپوینت لیست ثابت انواع **موجودیت‌های (Object Types)** قابل اتصال به رابط‌های نرم‌افزاری را بازمی‌گرداند. این فیلد تعیین می‌کند که شناسه موجود در فیلد `object` به کدام جدول دیتابیس (مثلاً جدول همکاران) اشاره دارد.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface_object_types`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CoreController@listApplicationInterfacObjectType</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Logic Details

این متد در حال حاضر تنها یک مقدار ثابت را بازمی‌گرداند که نشان‌دهنده اتصال رابط به موجودیت «همکار» است:

<div class="api-docs" id="bkmrk-id-%2F-title-descripti"><table class="schema-table" dir="rtl"><thead><tr><th>ID / Title</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">colleague</td><td>نشان می‌دهد که این رابط نرم‌افزاری متعلق به یک همکار (Colleague) خاص است و فیلد `object` باید حاوی شناسه آن همکار باشد.</td></tr></tbody></table>

</div>## Response Structure

### نمونه پاسخ موفق (200 OK)

```json
{
    "items": [
        {
            "id": "colleague",
            "title": "colleague"
        }
    ],
    "meta": {
        "timestamp": 1733754000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-load"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Load Static Object Types  
(Currently only 'colleague')</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>

# PUT /v2/core/application_interface/status/{id}

# Update Application Interface Status

این اندپوینت به منظور تغییر سریع **وضعیت (Status)** یک رابط نرم‌افزاری استفاده می‌شود. برخلاف متد ویرایش کلی، این متد تنها فیلد `status` را در دیتابیس بروزرسانی می‌کند.

<div class="api-docs" id="bkmrk-"></div>## Request Overview

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcore%2Fapplic"><div class="endpoint-info"><div>**URL:** `/v2/core/application_interface/status/{id}`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** CoreController@updateStatusApplicationInterface</div><div>**Middleware:** authWithJwt</div></div></div>## Access Control

<div class="api-docs" id="bkmrk-%D9%86%DB%8C%D8%A7%D8%B2-%D8%A8%D9%87-%D8%AA%D9%88%DA%A9%D9%86-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87">- نیاز به توکن احراز هویت (JWT) دارد.

</div>## Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td>Integer</td><td>Path</td><td>شناسه یکتای رکورد در جدول `application_interface`.</td></tr><tr><td dir="ltr">status</td><td>String</td><td>Body</td><td>وضعیت جدید برای اعمال (مثلاً `active` یا `inactive`).</td></tr></tbody></table>

</div>## Logic Details

عملیات به صورت مستقیم روی دیتابیس انجام می‌شود:

<div class="api-docs" id="bkmrk-%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-">1. ابتدا با استفاده از `id` دریافتی، کوئری آپدیت روی جدول `application_interface` اجرا شده و فیلد `status` با مقدار ورودی جایگزین می‌شود.
2. بلافاصله پس از آپدیت، رکورد مورد نظر مجدداً از دیتابیس (با `find($id)`) بازیابی می‌شود تا آخرین وضعیت تایید شده برگردانده شود.

</div>## Response Structure

### نمونه پاسخ موفق (200 OK)

```json
{
    "payload": {
        "id": 15,
        "branch_id": 1,
        "type": "sms",
        "service": "irnoti",
        "object_type": "colleague",
        "object": 102,
        "settings": "{\"apiKey\":\"...\"}",
        "status": "inactive",
        "created_at": "2023-01-01 12:00:00",
        "updated_at": "2023-12-09 14:30:00"
    },
    "meta": {
        "timestamp": 1733754000
    }
}
```

<div class="api-docs" id="bkmrk--1"></div>## Flowchart

<div class="api-docs" id="bkmrk-start-request-%28id%2C-s"><div class="flowchart"><div class="flow-item">Start Request (ID, Status)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update 'status' in DB  
(Where ID = $id)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Updated Record  
(DB::find($id))</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON (Payload)</div></div></div>

# RESOURCE /v2/scrumboard/boards

<div class="api-docs" id="bkmrk-">  </div># List Scrum Boards

این اندپوینت لیست بوردهای اسکرام را بازیابی می‌کند. نتایج شامل بوردهایی است که کاربر جاری یا **سازنده (Owner)** آن‌هاست و یا به عنوان **عضو (Member)** به آن‌ها دعوت شده است.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/boards`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** BoardController@index</div><div>**Middleware:** authWithJwt</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>Query</td><td>شناسه شعبه (الزامی برای فیلتر اولیه).</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت فعال/غیرفعال.</td></tr></tbody></table>

</div>### Logic Details

سیستم پردازش‌های زیر را هنگام دریافت لیست انجام می‌دهد:

<div class="api-docs" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C%3A-%D8%AA%D9%86%D9%87%D8%A7-%D8%A8">- **فیلتر دسترسی:** تنها بوردهایی بازگردانده می‌شوند که `operator_id` برابر با کاربر جاری باشد **یا** شناسه کاربر در آرایه JSON ستون `members` وجود داشته باشد.
- **Hydration (تزریق داده‌ها):**
    - فیلد `members` (آرایه‌ای از IDها) با آبجکت کامل اطلاعات اپراتورها (نام، آواتار و...) جایگزین می‌شود.
    - لیست‌های بورد (Lists) و لیبل‌ها (Labels) از جداول مربوطه دریافت و به پاسخ اضافه می‌شوند.

</div>### Success Response

```json
{
    "items": [
        {
            "id": 10,
            "title": "پروژه توسعه",
            "operator_id": 55,
            "members": [
                { "id": 60, "first_name": "Ali", "last_name": "Rezaei", "avatar": "..." }
            ],
            "lists": [
                { "id": 1, "title": "درحال انجام", "priority": 2 }
            ],
            "labels": [
                { "id": 5, "title": "فوری" }
            ]
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Scrum Board

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

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/boards`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** BoardController@store</div><div>**Middleware:** authWithJwt</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">title</td><td>String</td><td>عنوان بورد (الزامی).</td></tr><tr><td dir="ltr">icon</td><td>String</td><td>آیکون یا ایموجی بورد (الزامی).</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>شناسه شعبه (الزامی).</td></tr><tr><td dir="ltr">sprint\_unit</td><td>String</td><td>(اختیاری) واحد زمان اسپرینت.</td></tr><tr><td dir="ltr">sprint\_duration</td><td>Integer</td><td>(اختیاری) مدت زمان اسپرینت.</td></tr></tbody></table>

</div>### Logic Details (Default Data)

پس از درج بورد، سیستم موارد زیر را به صورت خودکار ایجاد می‌کند:

<div class="api-docs" id="bkmrk-%D9%84%DB%8C%D8%B3%D8%AA%E2%80%8C%D9%87%D8%A7%DB%8C-%D9%BE%DB%8C%D8%B4%E2%80%8C%D9%81%D8%B1%D8%B6%3A-%D9%86%DB%8C"><div class="api-notice info" dir="rtl">**لیست‌های پیش‌فرض:**- **نیازمند بررسی** (رنگ: #ffdb6d، اولویت: 3)
- **درحال انجام** (رنگ: #4287f4، اولویت: 2)
- **انجام شده** (رنگ: #4caf50، اولویت: 1)

**لیبل‌های پیش‌فرض:** بسیار فوری، بالا، متوسط، پایین.</div></div>### Success Response

```json
{
    "payload": {
        "id": 12,
        "title": "New Board",
        "lists": [ ... ],
        "labels": [ ... ]
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Scrum Board

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

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/boards/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** BoardController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>شناسه بورد.</td></tr></tbody></table>

</div>### Logic Details

این متد چک می‌کند که آیا کاربر درخواست کننده (`operator`) یا سازنده بورد است و یا شناسه او در فیلد JSON `members` وجود دارد. در غیر این صورت (یا اگر بورد وجود نداشته باشد)، نتیجه خالی برمی‌گرداند.

### Success Response

```json
{
    "payload": {
        "id": 12,
        "title": "Board Title",
        "members": [...],
        "lists": [...],
        "labels": [...]
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Scrum Board

بروزرسانی اطلاعات بورد. نکته امنیتی مهم این است که برخلاف نمایش، **فقط سازنده بورد (Operator ID منطبق)** اجازه ویرایش را دارد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/boards/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** BoardController@update</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">title</td><td>String</td><td>عنوان جدید.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>توضیحات.</td></tr><tr><td dir="ltr">members</td><td>Array (IDs)</td><td>لیست ID اعضای جدید (به JSON تبدیل می‌شود).</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت (پیش‌فرض 1).</td></tr></tbody></table>

</div>### Error Response (Forbidden)

اگر کاربر جاری سازنده بورد نباشد:

```json
{
    "error": {
        "code": 1001,
        "message": "You do not have permission to edit this scrum board."
    },
    "meta": { "timestamp": 1733754000 }
}
```

### Success Response

```json
{
    "payload": { "id": 12, "title": "Updated Title", ... },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Scrum Board

حذف کامل بورد از دیتابیس. این عملیات نیز مانند ویرایش، تنها توسط **سازنده بورد** قابل انجام است.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/boards/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** BoardController@destroy</div></div></div>### Logic Details

ابتدا مالکیت بورد بررسی می‌شود. اگر کاربر جاری سازنده نباشد، خطای کد `1001` بازگردانده می‌شود. در صورت موفقیت، رکورد از جدول `scrumboard_boards` حذف می‌شود.

### Success Response

```json
{
    "payload": 1, // تعداد رکوردهای حذف شده
    "meta": {
        "timestamp": 1733754000
    }
}
```

# RESOURCE /v2/scrumboard/lists

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Deep Hydration Logic Flow

منطق غنی‌سازی عمیق (Deep Hydration) که در اکثر متدها (Index, Store, Show, Update) اجرا می‌شود تا ساختار درختی کامل بورد را بسازد:

<div class="api-docs" id="bkmrk-start-request-%28index"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Start Request (Index/Store/Show)</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch List(s) Record from DB</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Loop: For Each List**  
Fetch `scrumboard_cards`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Has Cards?</div><div style="position: relative;"><div class="flow-arrow-label-right" style="top: -20px; right: -50px;">No</div><div class="flow-item-process" style="float: right; margin-right: -160px; width: 140px; font-size: 11px; background: #f5f5f5; color: #999;">Skip Hydration  
Return Empty Cards []</div></div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #fff3e0;">**Start Nested Loop: For Each Card**</div><div style="display: flex; justify-content: space-between; margin-top: 20px; gap: 10px;"><div style="width: 48%;"><div class="flow-item-process" style="font-size: 11px;">**1. Decode Members**  
Fetch Operators (Avatar, Name)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="font-size: 11px;">**2. Decode Labels**  
Fetch Label Details</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="font-size: 11px;">**3. Sprints**  
Filter Sprints by JSON</div></div><div style="width: 48%;"><div class="flow-item-process" style="font-size: 11px;">**4. Comments**  
Fetch &amp; Hydrate Operator</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="font-size: 11px;">**5. Checklists**  
Decode Items &amp; Hydrate Operator</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="font-size: 11px;">**6. Media**  
Fetch Attachments</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">Merge All Data into Card Object</div><div class="flow-arrow">↓</div><div class="flow-item-process">Add Card to List Object</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Nested JSON Response</div></div></div>  
---

  </div># Get Board Lists

دریافت تمام لیست‌های (ستون‌های) مربوط به یک بورد خاص به همراه تمام کارت‌ها و جزئیات آن‌ها.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/lists`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ListController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه بوردی که لیست‌ها متعلق به آن هستند.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت لیست.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 1,
            "board_id": 10,
            "title": "To Do",
            "priority": 2,
            "cards": [
                {
                    "id": 101,
                    "title": "Task 1",
                    "members": [ { "id": 1, "first_name": "Ali", ... } ],
                    "labels": [ { "id": 5, "title": "Bug", "color": "red" } ],
                    "comments": [ ... ],
                    "checklists": [ ... ],
                    "attachments": [ ... ],
                    "sprints": [ ... ]
                }
            ]
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create List

ایجاد یک لیست (ستون) جدید در بورد. بلافاصله پس از ایجاد، رکورد بازیابی شده و فرآیند Hydration روی آن اجرا می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/lists`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** ListController@store</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>شناسه بورد والد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان لیست.</td></tr><tr><td dir="ltr">color</td><td>String</td><td>رنگ لیست (کد HEX).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 5,
        "title": "New Column",
        "cards": [],
        "created_at": "..."
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show List

دریافت اطلاعات یک لیست خاص با شناسه ID. این متد نیز تمامی کارت‌ها و وابستگی‌های آن‌ها را بارگذاری می‌کند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/lists/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ListController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>شناسه لیست (List ID).</td></tr></tbody></table>

---

  </div># Update List

ویرایش اطلاعات یک لیست.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/lists/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** ListController@update</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>شناسه بورد.</td><td>-</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان لیست.</td><td>-</td></tr><tr><td dir="ltr">color</td><td>String</td><td>رنگ لیست.</td><td>-</td></tr><tr><td dir="ltr">priority</td><td>Integer</td><td>اولویت نمایش (Sort Order).</td><td>0</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت لیست.</td><td>1</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 5,
        "title": "Edited Title",
        "priority": 10,
        "cards": [ ... ]
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Delete List

حذف کامل یک لیست از دیتابیس.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/lists/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** ListController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد ردیف‌های حذف شده
    "meta": {
        "timestamp": 1733754000
    }
}
```

# RESOURCE /v2/scrumboard/labels

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Label Logic Flow

منطق پردازش ساده برای مدیریت لیبل‌ها (برچسب‌ها) که مستقیماً با دیتابیس در تعامل است:

<div class="api-docs" id="bkmrk-start-request-%E2%86%93-requ"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Start Request</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Request Type?</div><div style="display: flex; justify-content: space-between; margin-top: 20px;"><div style="width: 30%;"><div class="flow-arrow-label-left" style="text-align: center;">GET (Index/Show)</div><div class="flow-item-process" style="background-color: #e3f2fd;">Fetch from `scrumboard_labels`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Data or False</div></div><div style="width: 30%;"><div class="flow-arrow-label-left" style="text-align: center;">POST/PUT</div><div class="flow-item-process" style="background-color: #fff3e0;">Insert/Update DB  
Set Timestamps</div><div class="flow-arrow">↓</div><div class="flow-item-process">Fetch Updated Record</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Payload</div></div><div style="width: 30%;"><div class="flow-arrow-label-right" style="text-align: center;">DELETE</div><div class="flow-item-process" style="background-color: #ffebee;">Delete by ID</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Count (0/1)</div></div></div></div></div>  
---

  </div># Get Board Labels

دریافت لیست تمامی لیبل‌های تعریف شده برای یک بورد خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/labels`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LabelController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه بوردی که لیبل‌ها متعلق به آن هستند.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت فعال/غیرفعال بودن.</td></tr></tbody></table>

</div>### Response Structure

نکته: اگر داده‌ای یافت نشود، مقدار `items` برابر با `false` خواهد بود.

```json
{
    "items": [
        {
            "id": 1,
            "board_id": 10,
            "title": "Bug Fix",
            "status": 1,
            "created_at": "2023-12-01 10:00:00",
            "updated_at": "2023-12-01 10:00:00"
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Label

ایجاد یک لیبل جدید برای بورد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/labels`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** LabelController@store</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>شناسه بورد والد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان لیبل.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 5,
        "board_id": 10,
        "title": "Urgent",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Label

دریافت جزئیات یک لیبل خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/labels/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LabelController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>شناسه لیبل.</td></tr></tbody></table>

</div>### Response

اگر لیبل یافت نشود، مقدار `payload` برابر با `false` خواهد بود.

```json
{
    "payload": {
        "id": 5,
        "board_id": 10,
        "title": "Urgent",
        "status": 1,
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Label

ویرایش نام، وضعیت یا انتقال لیبل به بورد دیگر.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/labels/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** LabelController@update</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>شناسه بورد.</td><td>-</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان جدید لیبل.</td><td>-</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت (1 فعال، 0 غیرفعال).</td><td>1</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 5,
        "board_id": 10,
        "title": "New Title",
        "status": 1,
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Label

حذف لیبل از دیتابیس.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/labels/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** LabelController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد ردیف‌های حذف شده
    "meta": {
        "timestamp": 1733754000
    }
}
```

# RESOURCE /v2/scrumboard/checklists

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Checklist Hydration Logic

در تمامی متدها (لیست، ایجاد، نمایش، ویرایش)، داده‌های خام دیتابیس قبل از ارسال به کلاینت پردازش می‌شوند. فیلد `checkitems` از رشته JSON به آرایه تبدیل شده و شناسه `operator` به آبجکت کامل اپراتور تبدیل می‌گردد.

<div class="api-docs" id="bkmrk-fetch-record%28s%29-from"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Fetch Record(s) from DB</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Step 1: Decode Checkitems**  
Input: JSON String (e.g., "[{...}]")  
Output: Array (or [] if null)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Step 2: Hydrate Operator**  
Input: Operator ID (Int)  
Action: Fetch from `operators` table  
Output: Object {id, name, avatar...}</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Hydrated JSON</div></div></div>  
---

  </div># Get Card Checklists

دریافت لیست تمامی چک‌لیست‌های متصل به یک کارت (Card) مشخص. آیتم‌های داخلی چک‌لیست و اطلاعات اپراتور مسئول به صورت خودکار بارگذاری می‌شوند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/checklists`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ChecklistController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه کارتی که چک‌لیست‌ها متعلق به آن هستند.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت (مثلاً 1 فعال).</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 1,
            "card_id": 101,
            "title": "QA Testing",
            "checkitems": [
                { "text": "Test Login", "done": true },
                { "text": "Test Logout", "done": false }
            ],
            "operator": {
                "id": 5,
                "first_name": "Ali",
                "last_name": "Rezaei",
                "avatar": "path/to/img.jpg"
                ...
            },
            "duration": "2h",
            "status": 1,
            "created_at": "..."
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Checklist

ایجاد یک چک‌لیست جدید. کلاینت می‌تواند آرایه‌ای از آیتم‌ها (`checkitems`) را ارسال کند که در سمت سرور به JSON تبدیل و ذخیره می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/checklists`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** ChecklistController@store</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>شناسه کارت والد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان کلی چک‌لیست.</td></tr><tr><td dir="ltr">checkitems</td><td>Array</td><td>آرایه‌ای از آبجکت‌ها (آیتم‌های تیک‌دار).</td></tr><tr><td dir="ltr">operator</td><td>Integer</td><td>شناسه اپراتور مسئول (اختیاری).</td></tr><tr><td dir="ltr">duration</td><td>String</td><td>مدت زمان تخمینی/صرف شده (اختیاری).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 15,
        "title": "Deployment Steps",
        "checkitems": [ ... ], // آرایه دیکد شده
        "operator": { ... },   // آبجکت اپراتور (اگر ارسال شده باشد)
        "created_at": "..."
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Checklist

دریافت جزئیات یک چک‌لیست خاص با شناسه ID.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/checklists/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** ChecklistController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>شناسه چک‌لیست.</td></tr></tbody></table>

</div>### Response

اگر یافت نشود، `payload: false` برمی‌گرداند.

```json
{
    "payload": {
        "id": 15,
        "title": "Deployment Steps",
        "checkitems": [],
        "operator": null,
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Checklist

ویرایش اطلاعات چک‌لیست. این متد معمولاً برای تغییر عنوان، وضعیت، تغییر مسئول (`operator`) یا به‌روزرسانی محتوای آیتم‌های داخلی (`checkitems`) استفاده می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/checklists/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** ChecklistController@update</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>شناسه کارت.</td><td>-</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان چک‌لیست.</td><td>-</td></tr><tr><td dir="ltr">checkitems</td><td>Array</td><td>آرایه کامل آیتم‌ها (جایگزین قبلی می‌شود).</td><td>null</td></tr><tr><td dir="ltr">operator</td><td>Integer</td><td>شناسه اپراتور جدید.</td><td>null</td></tr><tr><td dir="ltr">duration</td><td>String</td><td>مدت زمان.</td><td>null</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت.</td><td>1</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 15,
        "title": "Updated Title",
        "checkitems": [ ... ],
        "operator": { ... },
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Checklist

حذف چک‌لیست از دیتابیس.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/checklists/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** ChecklistController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد ردیف‌های حذف شده
    "meta": {
        "timestamp": 1733754000
    }
}
```

# RESOURCE /v2/scrumboard/comments

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Comment Operator Hydration

در تمامی متدها (لیست، ثبت، نمایش و ویرایش)، سیستم پس از دریافت اطلاعات کامنت از جدول `scrumboard\_comments`، به صورت دستی اطلاعات اپراتور (نویسنده کامنت) را از جدول `operators` استخراج کرده و به پاسخ اضافه می‌کند.

<div class="api-docs" id="bkmrk-fetch-comment%28s%29-%E2%86%93-e"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Fetch Comment(s)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Enrich with Operator**  
Input: operator_id  
Query: SELECT id, first_name, last_name, avatar FROM operators WHERE id = ?  
Result: Attach object to `operator` field</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>  
---

  </div># Get Card Comments

دریافت لیست نظرات ثبت شده برای یک کارت (Card). این متد امکان فیلتر کردن بر اساس اپراتور و وضعیت را نیز دارد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/comments`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CommentController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه کارتی که نظرات مربوط به آن است.</td></tr><tr><td dir="ltr">operator\_id</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر نظرات یک اپراتور خاص.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت نظر.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 10,
            "card_id": 55,
            "operator_id": 3,
            "message": "This needs to be fixed ASAP.",
            "ip_address": "192.168.1.5",
            "status": 1,
            "created_at": "2024-01-01 12:00:00",
            "updated_at": "2024-01-01 12:00:00",
            "operator": {
                "id": 3,
                "first_name": "John",
                "last_name": "Doe",
                "first_name_en": "John",
                "last_name_en": "Doe",
                "avatar": "path/to/avatar.jpg"
            }
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Comment

ثبت یک نظر جدید برای یک کارت. آدرس IP کاربر به صورت خودکار توسط سیستم (`getIP()`) ذخیره می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/comments`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CommentController@store</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>شناسه کارت مربوطه.</td></tr><tr><td dir="ltr">operator\_id</td><td>Integer</td><td>شناسه اپراتوری که نظر را ثبت می‌کند.</td></tr><tr><td dir="ltr">message</td><td>String</td><td>متن نظر.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 12,
        "card_id": 55,
        "operator_id": 3,
        "ip_address": "127.0.0.1",
        "message": "Task completed.",
        "created_at": "...",
        "updated_at": "...",
        "operator": {
            "id": 3,
            "first_name": "John",
            "last_name": "Doe",
            ...
        }
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Comment

مشاهده جزئیات یک نظر خاص با استفاده از شناسه آن.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/comments/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CommentController@show</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>شناسه نظر.</td></tr></tbody></table>

</div>### Response

در صورت عدم یافتن رکورد، مقدار `payload: false` بازگردانده می‌شود.

```json
{
    "payload": {
        "id": 10,
        "message": "Specific comment details...",
        "operator": { ... },
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Comment

ویرایش متن، وضعیت و یا حتی انتقال نظر به کارت/اپراتور دیگر.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/comments/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** CommentController@update</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td dir="ltr">card\_id</td><td>Integer</td><td>شناسه کارت (جهت جابجایی نظر).</td><td>-</td></tr><tr><td dir="ltr">operator\_id</td><td>Integer</td><td>شناسه اپراتور (جهت تغییر نویسنده).</td><td>-</td></tr><tr><td dir="ltr">message</td><td>String</td><td>متن اصلاح شده نظر.</td><td>-</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت نمایش نظر.</td><td>1</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 10,
        "message": "Updated message text",
        "status": 1,
        "updated_at": "...",
        "operator": { ... }
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Comment

حذف دائمی یک نظر از دیتابیس.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/comments/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CommentController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد ردیف‌های حذف شده
    "meta": {
        "timestamp": 1733754000
    }
}
```

# RESOURCE /v2/scrumboard/cards

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Card Deep Hydration Logic

کارت‌ها قلب سیستم اسکرام هستند. در تمامی متدها (Index, Store, Show, Update)، پس از دریافت اطلاعات خام کارت، یک فرآیند سنگین برای بارگذاری تمام وابستگی‌ها اجرا می‌شود. این فرآیند شامل دیکد کردن JSONها و کوئری‌های متعدد به جداول مختلف است.

<div class="api-docs" id="bkmrk-fetch-card%28s%29-data-%E2%86%93"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Fetch Card(s) Data</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Step 1: JSON Fields**  
Decode `members` &amp; `labels`  
Fetch Operators &amp; Label Objects via IDs</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Step 2: Sub-Entities**  
1. Get Comments (+ Hydrate Operators)  
2. Get Checklists (+ Hydrate Operators + Decode Items)  
3. Get Attachments (Media table)  
4. Get Sprints (JSON Search)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Fully Hydrated Card</div></div></div>  
---

  </div># Get Cards List

دریافت لیست کارت‌های موجود در یک لیست (ستون) خاص. تمامی اطلاعات وابسته (اعضا، لیبل‌ها، پیوست‌ها و...) به همراه کارت برگردانده می‌شوند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/cards`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CardController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">list\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه لیست (ستون) والد.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر وضعیت کارت.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 101,
            "list_id": 5,
            "title": "Fix Login Bug",
            "description": "User cannot login with email...",
            "members": [
                { "id": 1, "first_name": "Ali", "avatar": "..." }
            ],
            "labels": [
                { "id": 2, "title": "Bug", "color": "#ff0000" }
            ],
            "comments": [ ... ], // لیست کامل نظرات با اطلاعات نویسنده
            "checklists": [ ... ], // لیست چک‌لیست‌ها با آیتم‌ها
            "attachments": [ ... ], // مدیاهای متصل
            "sprints": [ ... ], // اسپرینت‌های مرتبط
            "created_at": "..."
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Card

ایجاد یک کارت جدید ساده. پس از ایجاد، کارت بلافاصله بازیابی شده و ساختار کامل (با آرایه‌های خالی برای وابستگی‌ها) برگردانده می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/cards`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** CardController@store</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">list\_id</td><td>Integer</td><td>شناسه لیست مقصد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان کارت.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 102,
        "title": "New Feature",
        "members": [],
        "labels": [],
        "comments": [],
        "checklists": [],
        "attachments": [],
        "sprints": [],
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Card Details

دریافت جزئیات کامل یک کارت با شناسه مشخص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/cards/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CardController@show</div></div></div>### Response

اگر کارت پیدا نشود، `payload: false` برمی‌گرداند.

```json
{
    "payload": {
        "id": 101,
        "title": "Fix Login Bug",
        "members": [ ... ],
        "checklists": [
            {
                "id": 5,
                "title": "QA",
                "checkitems": [{ "text": "Test A", "done": true }],
                "operator": { ... }
            }
        ],
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Card &amp; Sub-Resources

این متد علاوه بر آپدیت فیلدهای اصلی کارت، قابلیت **مدیریت تو‌در‌توی (Nested Management)** برای `comments` و `checklists` را نیز دارد. شما می‌توانید همزمان با ویرایش کارت، یک کامنت یا چک‌لیست را ایجاد، ویرایش یا حذف کنید.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/cards/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** CardController@update</div></div></div>### Basic Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">list\_id</td><td>Integer</td><td>جابجایی کارت به لیست دیگر.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>تغییر عنوان.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>توضیحات تکمیلی.</td></tr><tr><td dir="ltr">due\_date</td><td>Date/String</td><td>تاریخ سررسید.</td></tr><tr><td dir="ltr">members</td><td>Array\[Int\]</td><td>لیست جدید شناسه‌های اعضا (جایگزین کل لیست قبلی می‌شود).</td></tr><tr><td dir="ltr">labels</td><td>Array\[Int\]</td><td>لیست جدید شناسه‌های لیبل‌ها.</td></tr><tr><td dir="ltr">done\_at</td><td>DateTime</td><td>زمان انجام شدن.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت (پیش‌فرض 1).</td></tr></tbody></table>

</div>### Advanced Parameters (Nested Actions)

ارسال این پارامترها اختیاری است و برای عملیات سریع روی زیرمجموعه‌ها استفاده می‌شود.

#### 1. مدیریت کامنت (پارامتر `comment`)

<div class="api-docs" id="bkmrk-action-payload-struc"><table class="schema-table" dir="rtl"><thead><tr><th>Action</th><th>Payload Structure</th></tr></thead><tbody><tr><td>**Create**</td><td dir="ltr">`{ "card_id": 1, "operator_id": 2, "message": "text" }`  
(بدون ارسال id)</td></tr><tr><td>**Delete**</td><td dir="ltr">`{ "id": 10, "delete": true }`</td></tr></tbody></table>

</div>#### 2. مدیریت چک‌لیست (پارامتر `checklist`)

<div class="api-docs" id="bkmrk-action-payload-struc-1"><table class="schema-table" dir="rtl"><thead><tr><th>Action</th><th>Payload Structure</th></tr></thead><tbody><tr><td>**Create**</td><td dir="ltr">`{ "card_id": 1, "title": "Checklist 1", "checkitems": [...] }`  
(بدون ارسال id)</td></tr><tr><td>**Update**</td><td dir="ltr">`{ "id": 5, "card_id": 1, "title": "New Title", "checkitems": [...] }`</td></tr><tr><td>**Delete**</td><td dir="ltr">`{ "id": 5, "delete": true }`</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 101,
        // کارت آپدیت شده به همراه تمام تغییرات اعمال شده در کامنت‌ها/چک‌لیست‌ها
        "comments": [ ... ], 
        "checklists": [ ... ],
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Card

حذف کارت از دیتابیس. توجه: طبق کد فعلی، وابستگی‌ها (مثل کامنت‌ها) به صورت Cascade در کد PHP حذف نمی‌شوند مگر اینکه در سطح دیتابیس تنظیم شده باشد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/cards/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** CardController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // 1: موفق، 0: ناموفق
    "meta": { "timestamp": 1733754000 }
}
```

# RESOURCE /v2/scrumboard/sprints

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Sprint Hydration Logic

اسپرینت‌ها ظروف نگهداری کارت‌ها در بازه‌های زمانی مشخص هستند. منطق Hydration در اینجا بسیار سنگین است زیرا با دریافت اسپرینت، سیستم به صورت خودکار تمام کارت‌های داخل آن را واکشی کرده و فرآیند "Card Deep Hydration" را برای تک تک آن‌ها اجرا می‌کند.

<div class="api-docs" id="bkmrk-fetch-sprint-data-%E2%86%93-"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Fetch Sprint Data</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Step 1: Reports**  
Fetch `scrumboard_sprint_reports`  
+ Hydrate Operator for each report</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Step 2: Cards Hydration (Heavy)**  
Decode `cards` JSON (Array of IDs)  
Loop through Cards &amp; Apply **Card Deep Hydration**:  
(Members, Labels, Comments, Checklists, Attachments)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Sprint with Full Nested Data</div></div></div>  
---

  </div># Get Sprints List

دریافت لیست اسپرینت‌های یک بورد. هر آیتم در لیست شامل آرایه‌ای کامل از کارت‌های داخل آن اسپرینت است.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprints`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SprintController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه بورد مربوطه.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت اسپرینت.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 10,
            "board_id": 1,
            "title": "Sprint #4 - Login Refactor",
            "goal": "Improve security",
            "duration": "2 weeks",
            "status": 1,
            "created_at": "...",
            // گزارش‌های اسپرینت
            "reports": [
                { "id": 1, "note": "Daily meeting...", "operator": { ... } }
            ],
            // کارت‌های داخل اسپرینت (کاملاً Hydrate شده)
            "cards": [
                {
                    "id": 101,
                    "title": "Login API",
                    "members": [...],
                    "labels": [...],
                    "comments": [...],
                    "checklists": [...],
                    "attachments": [...]
                }
            ]
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Sprint

ایجاد یک اسپرینت جدید. می‌توان در لحظه ایجاد، لیست کارت‌های مرتبط را نیز تعیین کرد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprints`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** SprintController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">board\_id</td><td>Integer</td><td>(الزامی) شناسه بورد والد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(الزامی) عنوان اسپرینت.</td></tr><tr><td dir="ltr">duration</td><td>String</td><td>(الزامی) مدت زمان (مثلاً "2 weeks").</td></tr><tr><td dir="ltr">goal</td><td>String</td><td>(اختیاری) هدف اسپرینت.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>(اختیاری) توضیحات تکمیلی.</td></tr><tr><td dir="ltr">color</td><td>String</td><td>(اختیاری) کد رنگ برای نمایش در UI.</td></tr><tr><td dir="ltr">cards</td><td>Array\[Int\]</td><td>(اختیاری) آرایه‌ای از شناسه کارت‌ها جهت اتصال به این اسپرینت. مثال: `[101, 102]`</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 11,
        "title": "New Sprint",
        "cards": [], // یا شامل آبجکت‌های کارت اگر در ورودی ارسال شده باشد
        "reports": [],
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Sprint Details

مشاهده جزئیات یک اسپرینت خاص به همراه تمامی وابستگی‌ها (کارت‌ها و گزارش‌ها).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprints/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SprintController@show</div></div>---

  </div># Update Sprint

به‌روزرسانی اطلاعات اسپرینت. مهم: اگر پارامتر `cards` ارسال شود، لیست کارت‌های اسپرینت با لیست جدید **جایگزین** می‌شود (Sync).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprints/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** SprintController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">title</td><td>String</td><td>عنوان اسپرینت.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>وضعیت (مثلاً 1: فعال، 0: آرشیو).</td></tr><tr><td dir="ltr">cards</td><td>Array\[Int\]</td><td>لیست جدید شناسه کارت‌ها. مثال: `[101, 105, 109]`</td></tr><tr><td dir="ltr">...</td><td>...</td><td>سایر فیلدهای مشابه متد Create (goal, color, duration, etc).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 11,
        "title": "Updated Title",
        "cards": [ ... ], // لیست جدید کارت‌ها با جزئیات کامل
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Delete Sprint

حذف اسپرینت از دیتابیس. این عملیات کارت‌ها را حذف نمی‌کند، بلکه فقط ارتباط اسپرینت را از بین می‌برد (مگر اینکه در دیتابیس تنظیمات دیگری اعمال شده باشد).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprints/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** SprintController@destroy</div></div></div>### Response

```json
{
    "payload": 1, // تعداد رکوردهای حذف شده
    "meta": { "timestamp": 1733754000 }
}
```

# RESOURCE /v2/scrumboard/sprint_reports

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Sprint Report Hydration Logic

گزارش‌های اسپرینت، زیرمجموعه‌ای از اسپرینت‌ها هستند و منطق Hydration ساده‌ای دارند. در تمام متدها، پس از دریافت داده‌های گزارش، سیستم فقط اطلاعات اپراتور (نویسنده) را از جدول `operators` بارگذاری می‌کند.

<div class="api-docs" id="bkmrk-fetch-sprint-report%28"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Fetch Sprint Report(s)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Hydrate Operator**  
If `operator` field exists, fetch the full operator object from the `operators` table.</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Report with Operator Details</div></div></div>  
---

  </div># Get Sprint Reports List

دریافت لیست گزارش‌های ثبت شده برای یک اسپرینت مشخص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprint_reports`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SprintReportController@index</div></div></div>### Parameters

<div class="api-docs" id="bkmrk-parameter-type-locat"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Location</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">sprint\_id</td><td>Integer</td><td>Query</td><td>(الزامی) شناسه اسپرینت والد.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>Query</td><td>(اختیاری) فیلتر بر اساس وضعیت گزارش.</td></tr></tbody></table>

</div>### Response Structure

در صورت عدم وجود گزارش، مقدار `items: false` برگردانده می‌شود.

```json
{
    "items": [
        {
            "id": 1,
            "sprint_id": 10,
            "title": "Daily Stand-up - Day 3",
            "description": "Discussed blocker on API endpoint...",
            "issued_at": "2025-12-08 09:00:00",
            "status": 1,
            "operator": {
                "id": 5,
                "first_name": "Reza",
                "last_name": "Ahmadi",
                "avatar": "..."
            },
            "created_at": "..."
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Sprint Report

ایجاد یک گزارش جدید برای یک اسپرینت. شناسه اپراتور (نویسنده) به صورت خودکار از توکن کاربر احراز هویت شده (`authWithJwt`) استخراج و ثبت می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-1"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprint_reports`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** SprintReportController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">sprint\_id</td><td>Integer</td><td>(الزامی) شناسه اسپرینت والد.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(الزامی) عنوان گزارش.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>(الزامی) متن گزارش.</td></tr><tr><td dir="ltr">issued\_at</td><td>DateTime</td><td>(الزامی) تاریخ و زمان صدور گزارش (مثلاً زمان جلسه).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 2,
        "sprint_id": 10,
        "title": "Sprint Review Notes",
        ...
        "operator": {
            "id": 1, 
            "first_name": "Admin",
            ...
        }
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Sprint Report Details

مشاهده جزئیات یک گزارش خاص با شناسه آن.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-2"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprint_reports/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SprintReportController@show</div></div></div>### Response

در صورت پیدا نشدن گزارش، `payload: false` برمی‌گردد.

```json
{
    "payload": {
        "id": 1,
        "title": "Daily Stand-up - Day 3",
        "operator": { ... },
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Sprint Report

به‌روزرسانی اطلاعات یک گزارش. توجه داشته باشید که نویسنده (operator) قابل تغییر نیست.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-3"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprint_reports/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** SprintReportController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">sprint\_id</td><td>Integer</td><td>انتقال گزارش به اسپرینت دیگر.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>تغییر عنوان.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>تغییر متن گزارش.</td></tr><tr><td dir="ltr">issued\_at</td><td>DateTime</td><td>تغییر زمان صدور.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>تغییر وضعیت (پیش‌فرض 1).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 1,
        "title": "Updated report title",
        ...
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Sprint Report

حذف یک گزارش از سیستم.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fscrumboard%2F-4"><div class="endpoint-info"><div>**URL:** `/v2/scrumboard/sprint_reports/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** SprintReportController@destroy</div></div></div>### Response

```json
{
    "payload": 1, // 1 برای موفقیت، 0 برای عدم موفقیت
    "meta": { "timestamp": 1733754000 }
}
```

# RESOURCE /v2/cartable/letters

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Letter &amp; Workflow Logic

ماژول کارتابل دارای یک منطق گردش کار (Workflow) است. زمانی که یک نامه ایجاد می‌شود، سیستم به صورت خودکار اولین مرحله (Step) آن فرآیند را ایجاد می‌کند. همچنین زمانی که وضعیت یک مرحله به "انجام شده" تغییر می‌کند، سیستم به صورت هوشمند مرحله بعدی را تشخیص داده و ایجاد می‌کند.

<div class="api-docs" id="bkmrk-create-letter-reques"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Create Letter Request</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Step 1: Initialization**  
Generate Serial ID  
Insert Letter Record</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Step 2: Workflow Trigger**  
Fetch `cartable_subjects` (Process Config)  
Decode `steps` JSON  
**Insert First Step** into `cartable_letter_steps`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Letter Resource</div></div></div>  
---

  </div># Get Letters List

دریافت لیست نامه‌های موجود در کارتابل. این متد از یک سیستم صفحه‌بندی (Pagination) خاص استفاده می‌کند که پارامترهای `length` و `start` را به شماره صفحه لاراول تبدیل می‌کند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LetterController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">paginate\[length\]</td><td>Integer</td><td>(اختیاری) تعداد آیتم در هر صفحه (پیش‌فرض: 30).</td></tr><tr><td dir="ltr">paginate\[start\]</td><td>Integer</td><td>(اختیاری) آفست شروع دیتا (برای محاسبه صفحه فعلی).</td></tr><tr><td dir="ltr">requester\_id</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس شناسه درخواست‌کننده.</td></tr><tr><td dir="ltr">subject</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس شناسه موضوع نامه.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس وضعیت کلی نامه.</td></tr></tbody></table>

</div>### Response Structure

خروجی توسط `LetterResource` فرمت‌دهی می‌شود.

```json
{
    "items": [
        {
            "id": 50,
            "serial": "LET-1402-001",
            "subject": { "id": 1, "title": "Leave Request" },
            "requester": { "id": 10, "name": "Ali..." },
            "current_step": { "id": 205, "status": "pending" },
            "created_at": "..."
        }
    ],
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Letter

ایجاد یک نامه جدید.   
**نکته مهم:** پس از ایجاد نامه، سیستم به جدول `cartable\_subjects` مراجعه کرده و لیست مراحل تعریف شده برای آن موضوع (`subject`) را می‌خواند و به صورت خودکار **اولین مرحله** را در جدول `cartable\_letter\_steps` برای این نامه ثبت می‌کند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle-1"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** LetterController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه (برای تولید شماره سریال).</td></tr><tr><td dir="ltr">subject</td><td>Integer</td><td>(الزامی) شناسه موضوع نامه (تعیین کننده مراحل گردش کار).</td></tr><tr><td dir="ltr">recipient</td><td>String</td><td>(اختیاری) گیرنده نامه.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(اختیاری) عنوان نامه.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>(اختیاری) توضیحات نامه.</td></tr><tr><td dir="ltr">operators</td><td>Array\[Int\]</td><td>(اختیاری) لیست اپراتورهای دخیل (به صورت JSON ذخیره می‌شود).</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 51,
        "serial": "100-2569",
        "subject": 5,
        "steps": [ ... ] // شامل اولین مرحله ایجاد شده
    },
    "meta": { "timestamp": 1733754000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Letter Details

مشاهده جزئیات کامل یک نامه. اگر نامه حذف شده باشد (`deleted\_at` داشته باشد) یا وجود نداشته باشد، خطای 404 برمی‌گرداند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle-2"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** LetterController@show</div></div>---

  </div># Update Letter

ویرایش اطلاعات عمومی نامه (عنوان، گیرنده، توضیحات و...). این متد مراحل گردش کار را تغییر نمی‌دهد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle-3"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** LetterController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">title</td><td>String</td><td>عنوان نامه.</td></tr><tr><td dir="ltr">subject</td><td>Integer</td><td>تغییر موضوع نامه.</td></tr><tr><td dir="ltr">operators</td><td>Array\[Int\]</td><td>لیست اپراتورها.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>توضیحات.</td></tr><tr><td dir="ltr">recipient</td><td>String</td><td>گیرنده.</td></tr></tbody></table>

---

  </div># Delete Letter

حذف نرم (Soft Delete) نامه. فیلد `deleted_at` با زمان فعلی مقداردهی می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle-4"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** LetterController@destroy</div></div>---

  </div># Update Step &amp; Advance Workflow

**توجه:** این متد در کنترلر وجود دارد اما روت آن در اسنیپت ارائه شده نیست (احتمالاً روت سفارشی دارد).   
این متد برای تغییر وضعیت یک **مرحله (Step)** استفاده می‌شود.   
**منطق هوشمند:** اگر وضعیت ارسالی برابر با `3` (انجام شده) باشد، سیستم به صورت خودکار مرحله بعدی فرآیند را از تنظیمات موضوع (`subject steps`) پیدا کرده و آن را ایجاد می‌کند.

<div class="api-docs" id="bkmrk-method-url%3A-%28custom-"><div class="endpoint-info"><div>**Method URL:** `(Custom Route) /v2/cartable/steps/{id}`</div><div>**Target ID:** شناسه مرحله (cartable_letter_steps.id)</div><div>**Controller:** LetterController@updateLetterStep</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-3"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>Integer</td><td>(الزامی) وضعیت جدید مرحله (مثلاً 3 برای اتمام).</td></tr><tr><td dir="ltr">description</td><td>String</td><td>(اختیاری) توضیحات یا دستور انجام کار روی مرحله.</td></tr></tbody></table>

<div class="flowchart"><div class="flow-item">Update Step Request</div><div class="flow-arrow">↓</div><div class="flow-item-process">Update `status` &amp; `description`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Status == 3 ?</div><div class="flow-arrow-right">YES →</div><div class="flow-item-process" style="background-color: #c8e6c9; margin-left: 20px;">Find Current Step Index  
Check for Next Step in Config  
**Insert Next Step**</div></div></div>

# RESOURCE /v2/cartable/subjects

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Subject &amp; Workflow Configuration Logic

این کنترلر وظیفه مدیریت "موضوعات نامه" را بر عهده دارد. اهمیت اصلی این بخش در فیلد `steps` است.   
فیلد `steps` یک آرایه JSON است که "نقشه راه" یا "گردش کار" (Workflow) یک نامه را تعریف می‌کند. وقتی نامه‌ای با موضوع خاصی ایجاد می‌شود، سیستم از این تنظیمات برای تولید مراحل (`cartable\_letter\_steps`) استفاده می‌کند.

<div class="api-docs" id="bkmrk-admin-define-subject"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Admin Define Subject</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Define Steps (JSON)**  
Example: [Step1: Secretary, Step2: Manager, Step3: Archive]</div><div class="flow-arrow">↓</div><div class="flow-item-success">Saved in `cartable_subjects`</div><div class="flow-arrow" style="border-left: 2px dashed #999; height: 30px; width: 0; margin: 0 auto;">  
</div><div class="flow-item" style="border: 1px dashed #999; color: #666; font-size: 0.8em;">Used by LetterController</div></div></div>  
---

  </div># List Subjects

دریافت لیست تمام موضوعات تعریف شده. برخلاف نامه‌ها، در اینجا فعلاً صفحه‌بندی (Pagination) وجود ندارد و همه رکوردها بر اساس فیلترها بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subjects`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SubjectController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">department</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس دپارتمان.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس وضعیت فعال/غیرفعال بودن موضوع.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 1,
            "branch": 101,
            "department": 5,
            "title": "درخواست مرخصی",
            "steps": "[{\"order\":1, \"role\":\"manager\"}, ...]", // JSON String
            "created_at": "...",
            "updated_at": "..."
        }
    ],
    "meta": { "timestamp": 1733758000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Subject &amp; Workflow

تعریف یک موضوع جدید به همراه مراحل گردش کار آن.   
**نکته فنی:** آرایه `steps` به صورت `json\_encode` شده در دیتابیس ذخیره می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-1"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subjects`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** SubjectController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه.</td></tr><tr><td dir="ltr">department</td><td>Integer</td><td>شناسه دپارتمان مربوطه.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان موضوع (مثلاً: "درخواست وام").</td></tr><tr><td dir="ltr">steps</td><td>Array</td><td>آرایه مراحل گردش کار (JSON). این ساختار برای تولید خودکار مراحل حیاتی است.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 12,
        "title": "New Workflow",
        "steps": "[\"step1\", \"step2\"]",
        "created_at": "..."
    },
    "meta": { "timestamp": 1733758000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Subject Details

مشاهده جزئیات یک موضوع.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-2"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subjects/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SubjectController@show</div></div>---

  </div># Update Subject

ویرایش تنظیمات موضوع.   
**نکته:** طبق کد، فیلد `branch` در متد Update قابل ویرایش نیست (فقط department, title, steps).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-3"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subjects/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** SubjectController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">department</td><td>Integer</td><td>دپارتمان.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان موضوع.</td></tr><tr><td dir="ltr">steps</td><td>Array</td><td>بازتعریف مراحل گردش کار (این تغییر روی نامه‌های قدیمی تاثیر نمی‌گذارد، فقط نامه‌های جدید).</td></tr></tbody></table>

---

  </div># Delete Subject

حذف فیزیکی (Hard Delete) یک موضوع از دیتابیس.   
<span style="color: red;">هشدار:</span> این عملیات Soft Delete نیست و رکورد کاملاً پاک می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-4"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subjects/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** SubjectController@destroy</div></div></div>

# RESOURCE /v2/cartable/subject_steps

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Subject Steps Management Logic

این کنترلر وظیفه مدیریت **مراحل پیش‌فرض (Step Templates)** را بر عهده دارد.   
این جدول (`cartable\_subject\_steps`) به عنوان یک بانک اطلاعاتی از مراحل عمل می‌کند که شامل یک عنوان (`title`) و یک اپراتور مسئول (`operator`) است. این مراحل مستقل هستند اما می‌توانند هنگام تعریف گردش کار (Workflow) در بخش Subjectها مورد استفاده قرار گیرند.

<div class="api-docs" id="bkmrk-admin-define-step-te"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Admin Define Step Template</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Assign Operator &amp; Title**  
(e.g. Operator: 105, Title: "Financial Review")</div><div class="flow-arrow">↓</div><div class="flow-item-success">Saved in `cartable_subject_steps`</div><div class="flow-arrow" style="border-left: 2px dashed #999; height: 30px; width: 0; margin: 0 auto;">  
</div><div class="flow-item" style="border: 1px dashed #999; color: #666; font-size: 0.8em;">Selectable in Subject Workflow Config</div></div></div>  
---

  </div># List Step Definitions

دریافت لیست مراحل تعریف شده. این لیست بر اساس تاریخ ایجاد (نزولی) مرتب شده است.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subject_steps`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SubjectStepsController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس شناسه اپراتور مسئول.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس وضعیت مرحله (اگرچه در متد Store فیلد status ست نمی‌شود، اما در Index قابل فیلتر است).</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 10,
            "operator": 105,
            "title": "بررسی مالی",
            "created_at": "...",
            "updated_at": "..."
        }
    ],
    "meta": { "timestamp": 1733760000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Step Definition

ایجاد یک تعریف مرحله جدید (Template).

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-1"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subject_steps`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** SubjectStepsController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>(الزامی) شناسه اپراتوری که مسئول انجام این مرحله است.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(الزامی) عنوان مرحله (مثلاً: "تایید نهایی").</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 15,
        "operator": 105,
        "title": "تایید نهایی",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733760000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Step Details

مشاهده جزئیات یک مرحله خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-2"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subject_steps/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** SubjectStepsController@show</div></div></div>### Response Structure

```json
{
    "payload": {
        "id": 15,
        "operator": 105,
        "title": "تایید نهایی",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733760000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Step Definition

ویرایش اطلاعات اپراتور یا عنوان مرحله.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-3"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subject_steps/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** SubjectStepsController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>شناسه اپراتور جدید.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان جدید مرحله.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 15,
        "operator": 200, // Updated
        "title": "تایید مدیریت ارشد", // Updated
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733760000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Step Definition

حذف فیزیکی (Hard Delete) یک مرحله از دیتابیس.   
<span style="color: red;">هشدار:</span> اگر این مرحله در Subjectها استفاده شده باشد، ممکن است باعث ناهماهنگی در تنظیمات شود (بسته به منطق فرانت‌اند). در اینجا حذف کامل انجام می‌شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fsu-4"><div class="endpoint-info"><div>**URL:** `/v2/cartable/subject_steps/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** SubjectStepsController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد رکوردهای حذف شده
    "meta": { "timestamp": 1733760000 }
}
```

# RESOURCE /v2/cartable/recipients

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Department Recipients Management Logic

این کنترلر وظیفه مدیریت لیست "گیرندگان" (`recipients`) را در سطح دپارتمان‌ها بر عهده دارد.   
این جدول (`cartable\_department\_recipients`) عناوین شغلی یا واحدهایی را نگهداری می‌کند که در یک شعبه و دپارتمان خاص مجاز به دریافت نامه هستند (مانند: "دبیرخانه"، "مدیریت مالی" و ...).

<div class="api-docs" id="bkmrk-define-recipient-tit"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Define Recipient Title</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Link to Branch &amp; Department**  
(e.g. Branch: 100, Dept: 5, Title: "Secretariat")</div><div class="flow-arrow">↓</div><div class="flow-item-success">Saved in `cartable_department_recipients`</div></div></div>  
---

  </div># List Recipients

دریافت لیست تمام گیرندگان تعریف شده. این لیست بر اساس تاریخ ایجاد (نزولی) مرتب شده است.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fre"><div class="endpoint-info"><div>**URL:** `/v2/cartable/recipients`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** RecipientController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">department</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس شناسه دپارتمان.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس وضعیت فعال/غیرفعال بودن (در صورتی که در دیتابیس این فیلد پر شده باشد).</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 10,
            "department": 5,
            "branch": 101,
            "title": "دبیرخانه مرکزی",
            "created_at": "...",
            "updated_at": "..."
        }
    ],
    "meta": { "timestamp": 1733762000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Recipient

تعریف یک گیرنده جدید برای یک شعبه و دپارتمان خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fre-1"><div class="endpoint-info"><div>**URL:** `/v2/cartable/recipients`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** RecipientController@store</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">department</td><td>Integer</td><td>(الزامی) شناسه دپارتمان مربوطه.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(الزامی) عنوان گیرنده (مثلاً: "حسابداری").</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 12,
        "department": 5,
        "branch": 101,
        "title": "حسابداری",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733762000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Recipient Details

مشاهده جزئیات یک گیرنده خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fre-2"><div class="endpoint-info"><div>**URL:** `/v2/cartable/recipients/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** RecipientController@show</div></div></div>### Response Structure

```json
{
    "payload": {
        "id": 12,
        "department": 5,
        "branch": 101,
        "title": "حسابداری",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733762000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Recipient

ویرایش اطلاعات گیرنده.   
**نکته مهم:** طبق منطق کد، فیلد `branch` در متد Update قابل تغییر نیست و فقط `department` و `title` بروزرسانی می‌شوند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fre-3"><div class="endpoint-info"><div>**URL:** `/v2/cartable/recipients/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** RecipientController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">department</td><td>Integer</td><td>شناسه دپارتمان جدید.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان جدید.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 12,
        "department": 6, // Changed
        "branch": 101,
        "title": "حسابداری ارشد", // Changed
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733762000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Recipient

حذف فیزیکی (Hard Delete) یک گیرنده از سیستم.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fre-4"><div class="endpoint-info"><div>**URL:** `/v2/cartable/recipients/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** RecipientController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // تعداد رکوردهای حذف شده
    "meta": { "timestamp": 1733762000 }
}
```

# RESOURCE /v2/cartable/operator_role

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Operator Role &amp; Signature Logic

این کنترلر برای انتساب یک "عنوان" (`title`) به یک "اپراتور" (`operator`) و مهم‌تر از آن، ذخیره \*\*تصویر امضای دیجیتال\*\* (`signature\_file`) استفاده می‌شود.   
فایل‌های آپلود شده در فضای ذخیره‌سازی ابری (دیسک `liara`) با ساختار پوشه‌بندی بر اساس شناسه شعبه (`branch`) و تاریخ ذخیره می‌شوند.

<div class="api-docs" id="bkmrk-select-operator-%E2%86%93-de"><div class="endpoint-section"><div class="flowchart"><div class="flow-item">Select Operator</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Define Title &amp; Upload Signature**  
(Image validation: max 2MB, jpeg/png...)</div><div class="flow-arrow">↓</div><div class="flow-item-success">File Uploaded to Cloud  
Record Saved in DB</div></div></div>  
---

  </div># List Operator Roles

دریافت لیست نقش‌ها و امضاهای تعریف شده.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fop"><div class="endpoint-info"><div>**URL:** `/v2/cartable/operator_role`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** OperatorRoleController@index</div></div></div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس شناسه اپراتور.</td></tr><tr><td dir="ltr">status</td><td>Integer</td><td>(اختیاری) فیلتر بر اساس وضعیت.</td></tr></tbody></table>

</div>### Response Structure

```json
{
    "items": [
        {
            "id": 5,
            "operator": 105,
            "title": "مدیر فنی",
            "signature_file": "uploads/100/cartable/signatures/2024/12/example.png",
            "created_at": "...",
            "updated_at": "..."
        }
    ],
    "meta": { "timestamp": 1733764000 }
}
```

<div class="api-docs" id="bkmrk--1">---

  </div># Create Role &amp; Upload Signature

تعریف نقش جدید و آپلود تصویر امضا.   
<span style="color: orange;">نکته مهم:</span> این درخواست باید به صورت `multipart/form-data` ارسال شود.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fop-1"><div class="endpoint-info"><div>**URL:** `/v2/cartable/operator_role`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** OperatorRoleController@store</div></div></div>### Body Parameters (Multipart)

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>(الزامی) شناسه اپراتور صاحب امضا.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>(الزامی) عنوان شغلی (مثلاً "رئیس هیئت مدیره").</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه (جهت ساخت مسیر ذخیره‌سازی فایل).</td></tr><tr><td dir="ltr">signature\_file</td><td>File</td><td>(اختیاری) فایل تصویر امضا. فرمت‌های مجاز: jpeg, png, jpg, gif, svg. حداکثر حجم: 2MB.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 8,
        "operator": 105,
        "title": "رئیس هیئت مدیره",
        "signature_file": "uploads/101/cartable/signatures/2025/12/sig-123.png",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733764000 }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div># Show Role Details

مشاهده جزئیات یک نقش خاص.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fop-2"><div class="endpoint-info"><div>**URL:** `/v2/cartable/operator_role/{id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** OperatorRoleController@show</div></div></div>### Response Structure

```json
{
    "payload": {
        "id": 8,
        "operator": 105,
        "title": "رئیس هیئت مدیره",
        "signature_file": "uploads/101/cartable/signatures/2025/12/sig-123.png",
        "created_at": "...",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733764000 }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div># Update Role &amp; Signature

ویرایش عنوان یا جایگزینی فایل امضا.   
اگر فایل جدیدی ارسال شود، آپلود شده و مسیر آن در دیتابیس جایگزین می‌شود.   
*توجه: در لاراول برای ارسال فایل در متد PUT معمولاً باید از `\_method: PUT` در یک درخواست POST استفاده کنید.*

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fop-3"><div class="endpoint-info"><div>**URL:** `/v2/cartable/operator_role/{id}`</div><div>**Method:** <span class="method-put">PUT</span> / <span class="method-patch">PATCH</span></div><div>**Controller:** OperatorRoleController@update</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">operator</td><td>Integer</td><td>شناسه اپراتور.</td></tr><tr><td dir="ltr">title</td><td>String</td><td>عنوان شغلی جدید.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>شناسه شعبه (الزامی اگر فایل ارسال شود).</td></tr><tr><td dir="ltr">signature\_file</td><td>File</td><td>(اختیاری) تصویر امضای جدید برای جایگزینی.</td></tr></tbody></table>

</div>### Success Response

```json
{
    "payload": {
        "id": 8,
        "operator": 105,
        "title": "عنوان ویرایش شده",
        "signature_file": "uploads/101/cartable/signatures/2025/12/new-sig.png",
        "updated_at": "..."
    },
    "meta": { "timestamp": 1733764000 }
}
```

<div class="api-docs" id="bkmrk--4">---

  </div># Delete Role

حذف نقش و امضا.   
**پاکسازی فایل:** این متد علاوه بر حذف رکورد از دیتابیس، فایل امضای مربوطه را نیز از فضای ابری (Liara) پاک می‌کند.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fop-4"><div class="endpoint-info"><div>**URL:** `/v2/cartable/operator_role/{id}`</div><div>**Method:** <span class="method-delete">DELETE</span></div><div>**Controller:** OperatorRoleController@destroy</div></div></div>### Success Response

```json
{
    "payload": 1, // 1 for success
    "meta": { "timestamp": 1733764000 }
}
```

# PUT /v2/cartable/letters/step/{id}

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Workflow Progression Logic

این متد یک مرحله خاص (`cartable\_letter\_steps`) از یک نامه را بروزرسانی می‌کند.   
**جادوی گردش کار:** اگر وضعیت ارسالی برابر با <span style="color: green; font-weight: bold;">3 (انجام شده)</span> باشد، سیستم به صورت خودکار پیکربندی "موضوع نامه" (`subject-&gt;steps`) را بررسی کرده و مرحله بعدی را در دیتابیس ایجاد می‌کند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Update Letter Step

تغییر وضعیت یک مرحله، ثبت توضیحات و (در صورت اتمام) ارجاع خودکار به مرحله بعد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fcartable%2Fle"><div class="endpoint-info"><div>**URL:** `/v2/cartable/letters/step/{id}`</div><div>**Method:** <span class="method-put">PUT</span></div><div>**Controller:** LetterController@updateLetterStep</div></div></div>### URL Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td>Integer</td><td>(الزامی) شناسه رکورد مرحله در جدول `cartable_letter_steps`.</td></tr></tbody></table>

</div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>Integer</td><td>(الزامی) وضعیت جدید.   
`3` = انجام شده (Done) -&gt; **محرک مرحله بعدی**.  
سایر اعداد = صرفاً بروزرسانی وضعیت.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>(اختیاری) توضیحات یا هامش روی این مرحله.</td></tr></tbody></table>

</div>### Logic Details (Backend)

<div class="api-docs" id="bkmrk-%D8%AB%D8%A8%D8%AA-%D8%B2%D9%85%D8%A7%D9%86-%D8%A7%D8%AA%D9%85%D8%A7%D9%85%3A-%D8%A7%DA%AF%D8%B1-">- **ثبت زمان اتمام:** اگر `status == 3` باشد، فیلد `done_at` با زمان فعلی پر می‌شود.
- **منطق ایجاد مرحله بعد:**
    1. سیستم `letter\_id` را پیدا کرده و از روی آن `subject\_id` را می‌یابد.
    2. آرایه JSON مراحل (`steps`) را از جدول `cartable\_subjects` می‌خواند.
    3. موقعیت فعلی را در آرایه پیدا کرده و اگر ایندکس بعدی (`index + 1`) وجود داشته باشد، یک رکورد جدید با وضعیت پیش‌فرض برای مرحله بعد ایجاد می‌کند.

</div>### Success Response

```json
{
    "payload": 1, // نشان دهنده موفقیت آمیز بودن عملیات (تعداد ردیف تغییر کرده)
    "meta": {
        "timestamp": 1733765000
    }
}
```

<div class="api-docs" id="bkmrk--2">---

</div>### Visual Logic Flow

<div class="api-docs" id="bkmrk-update-step-status-%E2%86%93"><div class="flowchart"><div class="flow-item">Update Step Status</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7;">**Check: Is Status == 3?**</div><div class="flow-arrow">↓ (Yes)</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Fetch Workflow Config**  
(Find current step index in Subject's JSON)</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Next Step Exists?**  
Insert new record into `cartable_letter_steps`</div></div></div>

# POST /v2/ai/chat/completions

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## AI Financial Analysis Logic

این اندپوینت داده‌های خام مالی (شامل خرید، فروش، سود و زیان) را دریافت کرده و آن را به یک گزارش متنی/بصری تبدیل می‌کند.   
**نقش سیستم:** سیستم به عنوان یک مدیر مالی هوشمند برای نرم‌افزار *AirPlus* عمل می‌کند.   
**فرمت خروجی:** پرامپت سیستم به گونه‌ای تنظیم شده که خروجی نهایی صرفاً کد `HTML Body` باشد تا در فرانت‌اند مستقیماً داخل یک تگ `div` نمایش داده شود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Generate AI Report

ارسال داده‌های JSON مالی و دریافت تحلیل متنی به همراه جداول HTML.   
در این فرآیند نام آژانس (`branch`) از دیتابیس خوانده شده و در متن گزارش استفاده می‌شود تا تحلیل شخصی‌سازی شده باشد.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fai%2Fchat%2Fcom"><div class="endpoint-info"><div>**URL:** `/v2/ai/chat/completions`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** MainController@getReport</div></div></div>### Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">message</td><td>JSON/Array</td><td>(الزامی) داده‌های خام گزارش عملکرد (لیست خرید، فروش، سود، زیان و نوع خدمات).</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه/آژانس (جهت واکشی نام آژانس و درج در گزارش).</td></tr><tr><td dir="ltr">model</td><td>String</td><td>(اختیاری) مدل هوش مصنوعی.   
پیش‌فرض: `google/gemini-2.0-flash-001`.</td></tr><tr><td dir="ltr">prompt</td><td>String</td><td>(اختیاری) دستورالعمل سفارشی برای تحلیلگر. در صورت عدم ارسال، از پرامپت پیش‌فرض (تحلیل 5 مرحله‌ای AirPlus) استفاده می‌شود.</td></tr></tbody></table>

</div>### Default Prompt Structure

اگر پارامتر `prompt` ارسال نشود، سیستم دستورالعمل زیر را به AI می‌دهد:

<div class="api-docs" id="bkmrk-%D8%AE%D9%84%D8%A7%D8%B5%D9%87-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA%3A-%DA%A9%D9%84-%D9%81%D8%B1%D9%88%D8%B4">- **خلاصه وضعیت:** کل فروش، هزینه و سود/زیان.
- **تحلیل مقایسه‌ای:** مقایسه بین محصولات (پرواز، هتل، قطار و...).
- **روندها:** شناسایی رشد یا افت ناگهانی.
- **جدول آمار:** ارائه جدول HTML خلاصه.
- **توصیه مدیریتی:** پیشنهاد کاربردی برای بهبود سود (واحد پول: ریال).

</div>### Success Response Example

```json
{
    // خروجی بسته به کلاینت AI متفاوت است اما محتوا HTML است
    "content": "
```

### گزارش عملکرد آژانس تعطیلات رویایی

```
```

بر اساس داده‌های دریافتی...

```json
..."
}
```

<div class="api-docs" id="bkmrk--3">---

</div>### Visual Logic Flow

<div class="api-docs" id="bkmrk-receive-financial-js"><div class="flowchart"><div class="flow-item">Receive Financial JSON</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e0f7fa;">**Fetch Branch Info**  
(Get 'title_fa' from DB)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fff3e0;">**Build Contextual Prompt**  
(Inject Branch Name + JSON Data + HTML formatting rules)</div><div class="flow-arrow">↓</div><div class="flow-item">Send to LLM (Gemini/GPT)</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Return HTML Report**</div></div></div>

# POST /v2/trade/reference/type

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Global Reference Search

این متد به عنوان یک جستجوگر چندمنظوره (Poly-morphic Search) عمل می‌کند. فرانت‌اند با ارسال پارامتر `goal` مشخص می‌کند که به دنبال چه نوع داده‌ای است (مثلاً مسافر، حساب بانکی، یا ایرلاین) و سیستم با جستجو در جداول مربوطه، لیست استاندارد شده‌ای شامل `id` و `text` برمی‌گرداند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Search Entities (Autocomplete)

جستجو و واکشی اطلاعات برای ورودی‌های Select و Autocomplete در سراسر سیستم.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Ftrade%2Frefer"><div class="endpoint-info"><div>**URL:** `/v2/trade/reference/type`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** BaseController@tradeReferenceType</div><div>**Middleware:** authWithJwt</div></div></div>### Request Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">goal</td><td>String/Array</td><td>(الزامی) شناسه نوع موجودیت مورد جستجو. می‌تواند یک عدد (مثل `1000`) یا یک رشته (مثل `pay_type`) یا ترکیبی جدا شده با کاما باشد.</td></tr><tr><td dir="ltr">q</td><td>String</td><td>(اختیاری) عبارت جستجو (متن یا شناسه عددی).</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی - معمولاً در هدر یا بادی) شناسه شعبه فعال کاربر.</td></tr><tr><td dir="ltr">separator</td><td>Boolean</td><td>(اختیاری) اگر ارسال شود، به ابتدای `id` در خروجی، نوع موجودیت اضافه می‌شود (مثلاً `account-123` به جای `123`). مناسب برای جستجوهای ترکیبی.</td></tr><tr><td dir="ltr">extra</td><td>JSON/Array</td><td>(اختیاری) فیلترهای تکمیلی. مثلاً برای حساب‌ها (goal=1000) نوع پرداخت (`type_pay`: cash, pos, check) را مشخص می‌کند.</td></tr></tbody></table>

</div>### Goal Codes (Magic Numbers) &amp; Logic

<div class="api-docs" id="bkmrk-goal-code-target-ent"><table class="schema-table" dir="rtl"><thead><tr><th>Goal Code</th><th>Target Entity</th><th>Search Logic / Filters</th></tr></thead><tbody><tr><td>1000</td><td>حساب‌ها (Accounts)</td><td>جستجو در `accounting_accounts`.   
پشتیبانی از فیلتر `extra['type_pay']` برای نمایش حساب‌های خاص (نقد، پوز، آنلاین، چک).</td></tr><tr><td>1100</td><td>ایرلاین‌ها (Airlines)</td><td>جستجو بر اساس نام فارسی، انگلیسی یا کد IATA.</td></tr><tr><td>1200</td><td>هتل‌ها (Hotels)</td><td>جستجو در نام هتل و نام استان (State).</td></tr><tr><td>1300</td><td>پرسنل (Personnel)</td><td>جستجو در کاربران (`users`). اعمال محدودیت دسترسی بر اساس سطح دسترسی اپراتور.</td></tr><tr><td>1500</td><td>تنخواه‌گردان‌ها</td><td>کاربرانی که فیلد `imprest` آن‌ها مقدار دارد.</td></tr><tr><td>1600</td><td>اعلامیه‌ها</td><td>جدول `announcements`.</td></tr><tr><td>2000</td><td>بانک‌ها</td><td>جدول `accounting_banks` (به جز نوع داخلی).</td></tr><tr><td>3000</td><td>حوالجات</td><td>حساب‌های بانکی متصل به بانک با شناسه 32.</td></tr><tr><td>4000</td><td>اقساط</td><td>حساب‌های بانکی متصل به بانک با شناسه 33.</td></tr><tr><td>5000, 5001</td><td>رفرنس/فاکتور</td><td>جستجو در شماره سریال فاکتور (`factors`) یا نام مشتری مرتبط.</td></tr><tr><td>8000, 8100</td><td>همکاران (Suppliers)</td><td>جدول `colleagues` با وضعیت فعال.</td></tr><tr><td>8200</td><td>متعهدین (Commitments)</td><td>جدول `colleagues` (معمولاً بدهکاران/بستانکاران خاص).</td></tr><tr><td>9000</td><td>مسافرین (Customers)</td><td>جستجو در نام، نام خانوادگی (فارسی/انگلیسی) جدول `customers`.</td></tr><tr><td>pay\_type</td><td>روش‌های پرداخت</td><td>لیست استاتیک بر اساس `extra['type']` (دریافت یا پرداخت). شامل: نقد، پوز، چک، پایا، ساتنا و...</td></tr><tr><td>type\_functor</td><td>طرف حساب</td><td>لیست انواع موجودیت‌های مجاز برای تراکنش (مسافر، همکار، پرسنل و...).</td></tr><tr><td>status</td><td>وضعیت‌ها</td><td>لیست استاتیک وضعیت (پایان یافته، در حال بررسی و...).</td></tr><tr><td>gateways</td><td>درگاه‌ها</td><td>لیست درگاه‌های پرداخت فعال شعبه.</td></tr></tbody></table>

</div>### Success Response Example

```json
{
    "total_count": 10,
    "incomplete_results": false,
    "results": [
        {
            "id": 105,
            "text": "1205 - هتل داریوش (کیش)"
        },
        {
            "id": "account-50", // در صورت فعال بودن separator
            "text": "1050 - صندوق ریالی (Main Branch)"
        }
    ],
    "pagination": {
        "more": false
    }
}
```

# POST /v2/base/reference

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Base System References

این اندپوینت دقیقاً همان موتور جستجوی مرکزی (Global Search) است که در ماژول Trade استفاده می‌شد، اما با آدرس `base` برای استفاده‌های عمومی در کل سیستم (مانند فرم‌های تنظیمات، داشبورد و غیره) در دسترس قرار گرفته است.   
وظیفه آن تبدیل کدهای موجودیت (Goals) به لیست‌های استاندارد برای Dropdownها است.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># General Reference Lookup

جستجوی تمامی موجودیت‌های پایه سیستم (کاربران، شیفت‌ها، دپارتمان‌ها، حساب‌ها و...) برای استفاده در کامپوننت‌های Select2.

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fbase%2Frefere"><div class="endpoint-info"><div>**URL:** `/v2/base/reference`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** BaseController@tradeReferenceType</div><div>**Middleware:** authWithJwt</div></div></div>### Request Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">goal</td><td>String/Array</td><td>(الزامی) شناسه نوع داده مورد نیاز. (لیست کامل در جدول پایین).</td></tr><tr><td dir="ltr">q</td><td>String</td><td>(اختیاری) عبارت متنی یا عددی برای جستجو.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>(الزامی) شناسه شعبه فعال.</td></tr><tr><td dir="ltr">separator</td><td>Boolean</td><td>(اختیاری) افزودن پیشوند متنی به ID خروجی (مثلاً `office-1`).</td></tr><tr><td dir="ltr">details</td><td>Boolean</td><td>(اختیاری) برای برخی موجودیت‌ها مثل `shift_works` جزئیات کامل رکورد را در خروجی برمی‌گرداند.</td></tr></tbody></table>

</div>### Reference Codes (Goal Dictionary)

<div class="api-docs" id="bkmrk-code-entity-notes-10"><table class="schema-table" dir="rtl"><thead><tr><th>Code</th><th>Entity</th><th>Notes</th></tr></thead><tbody><tr><td>1000</td><td>حساب‌های حسابداری</td><td>فیلتر بر اساس `extra['type_pay']` (نقد، بانک، چک).</td></tr><tr><td>1300</td><td>پرسنل</td><td>کاربران سیستم. دسترسی محدود به سطح دسترسی اپراتور.</td></tr><tr><td>1500</td><td>تنخواه‌گردان</td><td>کاربران دارای فیلد `imprest`.</td></tr><tr><td>1600</td><td>اعلامیه‌ها</td><td>جستجو در عنوان و شرکت صادرکننده.</td></tr><tr><td>2000</td><td>بانک‌ها</td><td>لیست بانک‌های تعریف شده (بجز نوع داخلی).</td></tr><tr><td>5000</td><td>فاکتور/رفرنس</td><td>جستجو در سریال فاکتور یا نام مشتری.</td></tr><tr><td>8000/8100</td><td>همکاران</td><td>تامین‌کنندگان (Suppliers).</td></tr><tr><td>9000</td><td>مشتریان/مسافرین</td><td>جستجو در نام فارسی و انگلیسی.</td></tr><tr><td>office</td><td>دفاتر/شعب</td><td>لیست دفاتر فعال (`offices`).</td></tr><tr><td>gateways</td><td>درگاه‌های پرداخت</td><td>درگاه‌های متصل به شعبه (`gateways`).</td></tr><tr><td>shift\_works</td><td>شیفت‌های کاری</td><td>لیست شیفت‌ها. اگر `details=true` باشد، کل آبجکت برمی‌گردد.</td></tr><tr><td>office\_departments</td><td>دپارتمان‌ها</td><td>لیست دپارتمان‌های شعبه (فروش، مالی و...).</td></tr><tr><td>status</td><td>وضعیت‌ها</td><td>لیست استاتیک (پایان یافته، در حال بررسی، ...).</td></tr></tbody></table>

</div>### Example Request (Shift Works)

```json
{
    "goal": "shift_works",
    "branch": 1,
    "details": true
}
```

### Success Response

```json
{
    "total_count": 5,
    "incomplete_results": false,
    "results": [
        {
            "id": 1,
            "text": "شیفت صبح",
            "query": {
                "id": 1,
                "title": "شیفت صبح",
                "start_time": "08:00",
                "end_time": "16:00"
            }
        }
    ],
    "pagination": {
        "more": false
    }
}
```

<div class="api-docs" id="bkmrk--2">---

</div>### Technical Logic Flow

<div class="api-docs" id="bkmrk-request%3A-goal-%2B-quer"><div class="flowchart"><div class="flow-item">Request: Goal + Query</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7;">**Switch Case (Goal)**  
Select Table (User, Account, Office...)</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Apply Filters**  
(Branch check, Search 'q', Extra params)</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Format Output**  
{ id: ..., text: ... }</div></div></div>

# POST /v2/config

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## System Configuration &amp; Tenant Init

این متد حیاتی‌ترین اندپوینت برای راه‌اندازی اولیه اپلیکیشن (Bootstrapping) است.   
سیستم بر اساس هدر **Domain** تشخیص می‌دهد که کاربر مربوط به کدام شعبه (Office) است و تنظیمات ظاهری، مالی، حقوقی و ماژول‌های فعال آن شعبه را برمی‌گرداند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Get Office Configuration

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fconfig-meth"><div class="endpoint-info"><div>**URL:** `/v2/config`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V2BaseController@config</div><div>**Auth:** Public (No Auth Middleware Required usually, relies on Domain Header)</div></div></div>### Headers (Required)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Domain</td><td>آدرس دامنه‌ای که اپلیکیشن روی آن اجرا می‌شود (مثلاً `crm.example.com`).   
<small>سیستم `www.` و پورت (`:8080`) را به صورت خودکار حذف می‌کند تا دامنه پایه را پیدا کند.</small></td></tr></tbody></table>

  </div>### Response Structure (JSON)

خروجی شامل چندین بخش اصلی است:

<div class="api-docs" id="bkmrk-key-block-descriptio"><table class="schema-table" dir="rtl"><thead><tr><th>Key Block</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">office\_id</td><td>شناسه شعبه + 1000 (جهت Obfuscation).</td></tr><tr><td dir="ltr">title / brand</td><td>عنوان‌ها و نام برند به فارسی و انگلیسی.</td></tr><tr><td dir="ltr">communicational</td><td>اطلاعات تماس (تلفن، موبایل، ایمیل، آدرس، کد پستی و لوکیشن).</td></tr><tr><td dir="ltr">design</td><td>تنظیمات ظاهری شامل: - `logo/paper_logo/favicon`: لینک تصاویر.
- `base_color/theme/style`: تنظیمات CSS و رنگ‌بندی.
- `login_text`: متن نمایش داده شده در صفحه ورود.

</td></tr><tr><td dir="ltr">support\_online</td><td>کدهای مربوط به ابزارهای چت آنلاین (مثل Raychat یا Crisp).</td></tr><tr><td dir="ltr">details.financial\_debt</td><td><span style="color: red;">Boolean</span>  
آیا شعبه بدهی پرداخت نشده بابت پشتیبانی دارد؟ (بررسی جدول `airplus_bills`).</td></tr><tr><td dir="ltr">details.official</td><td>قوانین هاردکد شده سیستم برای مرخصی‌ها (Calendar Limit): - `leave`: قوانین مرخصی روزانه/ساعتی.
- `license`: قوانین مجوزها.

</td></tr><tr><td dir="ltr">details.config</td><td>تنظیمات داینامیک خوانده شده از جدول `office_config` (بصورت Key-Value).</td></tr></tbody></table>

</div>### Example Response

```json
{
    "office_id": 1001,
    "title": {
        "fa": "آژانس مسافرتی نمونه",
        "en": "Sample Travel Agency"
    },
    "brand": {
        "fa": "برند نمونه",
        "en": "Sample Brand"
    },
    "short_domain": "smpl",
    "expiration": "2026-01-01",
    "communicational": {
        "phone": "02188888888",
        "mobile": "09120000000",
        "email": "info@example.com",
        "site": "example.com",
        "address": {
            "fa": "تهران، خیابان ولیعصر...",
            "en": "Tehran, Valiasr St..."
        }
    },
    "design": {
        "login_text": "Welcome to Automation",
        "logo": "https://liara.../logo.png",
        "base_color": "#3b82f6",
        "theme": "light"
    },
    "services": ["flight", "hotel", "train"],
    "details": {
        "financial_debt": false,
        "financial": {
            "value_added": 9 
        },
        "official": {
            "calendar_limit": {
                "leave": {
                    "1": { "overtime": -60, "substitute": true },
                    "2": { "overtime": -60, "substitute": false }
                }
            }
        },
        "config": {
            "allow_negative_stock": "0",
            "print_footer_text": "Thanks for your purchase"
        }
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic Flow

<div class="api-docs" id="bkmrk-request-header%3A-doma"><div class="flowchart"><div class="flow-item">Request Header: Domain</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Clean Domain**  
Remove 'www.' and ':port'</div><div class="flow-arrow">↓</div><div class="flow-item-process" style="background-color: #fffde7;">**DB Query (offices)**  
WHERE json_contains(domain, $baseDomain)</div><div class="flow-arrow">↓</div><div class="flow-item-condition">Found?</div><div class="flow-arrow" style="transform: rotate(-45deg); display: inline-block;">↙ No</div><div class="flow-arrow" style="transform: rotate(45deg); display: inline-block;">Yes ↘</div><div style="display: flex; justify-content: space-between; margin-top: 10px;"><div class="flow-item-error" style="width: 45%;">**Return 400**  
"The office is not defined"</div><div class="flow-item-process" style="width: 45%; background-color: #e8f5e9;">**Fetch Details**  
1. VAT Info  
2. Office Configs  
3. Check Expired Bills</div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Complete JSON</div></div></div>

# GET /v2/config

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Initial System Configuration

این متد نقطه شروع (Entry Point) اپلیکیشن است. فرانت‌اند قبل از هر کاری باید این اندپوینت را صدا بزند.   
سیستم با بررسی هدر `Domain`، شعبه (Office) مورد نظر را پیدا کرده و تمام تنظیمات حیاتی شامل رنگ‌بندی، لوگو، اطلاعات تماس، قوانین مالی و وضعیت بدهی را برمی‌گرداند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Get Tenant Config

<div class="api-docs" id="bkmrk-url%3A-%2Fv2%2Fconfig-meth"><div class="endpoint-info"><div>**URL:** `/v2/config`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V2BaseController@config</div><div>**Auth:** Public (نیاز به توکن ندارد)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Domain</td><td>آدرس دامنه‌ای که کاربر با آن سایت را باز کرده است.   
<small>مثال: `crm.travel-agency.com` یا `localhost:3000`</small>   
*سیستم به صورت خودکار `www.` و پورت را حذف می‌کند.*</td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-field-block-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Field Block</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">office\_id</td><td>شناسه واقعی شعبه + 1000 (جهت امنیت و عدم نمایش ID اصلی).</td></tr><tr><td dir="ltr">design</td><td>تنظیمات UI/UX: - `theme/style/base_color`: برای تنظیم متغیرهای CSS.
- `logo/favicon`: لینک فایل‌ها.
- `login_text`: متن خوش‌آمدگویی صفحه لاگین.

</td></tr><tr><td dir="ltr">details.financial\_debt</td><td><span style="color: red; font-weight: bold;">مهم:</span> (Boolean)   
اگر `true` باشد، یعنی شعبه فاکتور پرداخت نشده "پشتیبانی" (Support Bill) دارد و دسترسی باید محدود شود.</td></tr><tr><td dir="ltr">details.official</td><td>قوانین هاردکد شده (Hardcoded) مربوط به سیستم حضور و غیاب: - `leave`: قوانین کسر اضافه کار و جایگزین برای مرخصی‌ها.
- `license`: قوانین مربوط به مرخصی ساعتی/مجوزها.

</td></tr><tr><td dir="ltr">details.config</td><td>تنظیمات داینامیک تعریف شده در جدول `office_config`.   
خروجی به صورت Key-Value است (مثلاً `allow_negative_stock: "1"`).</td></tr></tbody></table>

</div>### Example Response

```json
{
    "office_id": 1005,
    "title": {
        "fa": "آژانس مسافرتی آسمان",
        "en": "Aseman Travel Agency"
    },
    "brand": {
        "fa": "آسمان",
        "en": "Aseman"
    },
    "short_domain": "aseman",
    "expiration": "2025-12-29",
    "communicational": {
        "phone": "021-44444444",
        "mobile": "09121234567",
        "address": {
            "fa": "تهران، میدان آزادی...",
            "en": "Tehran, Azadi Sq..."
        },
        "location": "35.6892,51.3890"
    },
    "design": {
        "login_text": "به سیستم اتوماسیون خوش آمدید",
        "logo": "https://storage.../logo.png",
        "base_color": "#ff5722",
        "theme": "dark"
    },
    "support_online": {
        "service": "raychat",
        "code": "e45-fgh-789"
    },
    "details": {
        "financial_debt": false,
        "financial": {
            "value_added": 9
        },
        "official": {
            "calendar_limit": {
                "leave": {
                    "1": { "overtime": -60, "substitute": true },
                    "2": { "overtime": -60, "substitute": false }
                }
            }
        },
        "config": {
            "print_header": "1",
            "currency_default": "IRR"
        }
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Logic Flow

<div class="api-docs" id="bkmrk-request-%28header%3A-dom"><div class="flowchart"><div class="flow-item">Request (Header: Domain)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Domain Parsing**  
Remove 'www.', Remove Port  
Extract Base Domain</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Exists in DB?   
<small>(table: offices)</small></div><div style="display: flex; justify-content: space-between; width: 100%;"><div style="width: 40%;"><div class="flow-arrow" style="transform: rotate(-20deg);">No ↙</div><div class="flow-item-error">**400 Bad Request**  
"The office is not defined"</div></div><div style="width: 40%;"><div class="flow-arrow" style="transform: rotate(20deg);">Yes ↘</div><div class="flow-item-process" style="background-color: #e8f5e9;">**Load Data**  
1. Configs (office_config)  
2. VAT (accounting_titles)  
3. Debt Check (airplus_bills)  
4. Redis Cache Update</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Config</div></div></div>

# GET /b2c/v1/config

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## B2C Frontend Configuration

این اندپوینت مخصوص وب‌سایت‌های فروش آنلاین (White-label B2C) است.   
سیستم با بررسی هدر `Domain`، آدرس سایت مشتری را در ستون `b2c_domains` جستجو می‌کند و تنظیمات ظاهری، بنرها، مجوزهای نماد اعتماد و پیکربندی ماژول‌های فروش را برمی‌گرداند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Get B2C Config

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fconfig-"><div class="endpoint-info"><div>**URL:** `/b2c/v1/config`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1BaseController@config</div><div>**Auth:** Public (Public Access)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Domain</td><td>دامنه سایت فروش B2C (مثلاً `booking.agency.com`).   
<small>سیستم `www.` و پورت را حذف کرده و در `offices.b2c_domains` جستجو می‌کند.</small></td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-block-descriptio"><table class="schema-table" dir="rtl"><thead><tr><th>Key Block</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">erp\_domain</td><td>دامنه اصلی پنل مدیریت (ERP) که کارمندان از آن استفاده می‌کنند (جهت ارجاع لینک‌های ادمین).</td></tr><tr><td dir="ltr">communicational.certificates</td><td>لیست مجوزها (نماد اعتماد، ساماندهی و ...).   
سیستم به صورت خودکار تگ `<a><img .../></a>` را برای نمایش تصویر مجوز تولید می‌کند.</td></tr><tr><td dir="ltr">design.advertisement</td><td>لیست بنرهای تبلیغاتی فعال (Status 1 or 3) برای نمایش در اسلایدر یا بخش‌های تبلیغاتی سایت.</td></tr><tr><td dir="ltr">hub</td><td><span style="color: #007bff; font-weight: bold;">تنظیمات اختصاصی فروشگاه (Hub):</span>- `theme/palette`: چیدمان و رنگ‌بندی قالب سایت.
- `modules`: ماژول‌های فعال (پرواز، هتل، بیمه و ...).
- `features`: ویژگی‌های خاص فعال شده برای این مشتری.
- `authentication`: تنظیمات نحوه لاگین/ثبت‌نام کاربران (OTP، ایمیل و ...).

</td></tr></tbody></table>

</div>### Example Response

```json
{
    "office_id": 1025,
    "title": { "fa": "سفر آنلاین", "en": "Safar Online" },
    "brand": { "fa": "سفر24", "en": "Safar24" },
    "short_domain": "s24",
    "erp_domain": "admin.safar24.ir",
    "communicational": {
        "phone": "02199999999",
        "whatsapp": "989120000000",
        "social_media": { "instagram": "safar24_inst" },
        "certificates": [
            {
                "title": "Enamad",
                "content": "<a href="https://docs.airplus.app/..."><img src="https://storage.../enamad.png"></img></a>"
            }
        ]
    },
    "design": {
        "logo": "https://storage.../logo_b2c.png",
        "theme": "minimal-light-1",
        "advertisement": [
            {
                "id": 55,
                "title": "تور نوروزی استانبول",
                "image": "https://storage.../banner1.jpg",
                "link": "/tours/istanbul"
            }
        ]
    },
    "hub": {
        "services": ["flight", "hotel"],
        "theme": "minimal",
        "palette": "1",
        "slogan": "سفری خاطره‌انگیز با ما",
        "modules": {
            "flight_domestic": true,
            "train": false
        },
        "authentication": "otp_mobile"
    },
    "cache": {
        "update": "Tue, 09 Dec 2025 10:30:00 +0330"
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-header%3A-doma"><div class="flowchart"><div class="flow-item">Request Header: Domain</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Parse Domain**  
Normalize URL (Remove www)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Search DB   
<small>WHERE json\_contains(b2c\_domains, $domain)</small></div><div class="flow-arrow">↓ (Found)</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Fetch Aggregated Data**  
1. `advertisement` (Active banners)  
2. `certificates` (Trust logos)  
3. `hub_config` (Site settings)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return B2C Config JSON</div></div></div>

# POST /b2c/v1/auth/otp

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## B2C Customer Authentication (OTP)

این اندپوینت وظیفه مدیریت ورود و ثبت‌نام کاربران در وب‌سایت‌های فروش (B2C) را بر عهده دارد.   
سیستم از مکانیزم **رمز یکبار مصرف (OTP)** استفاده می‌کند. نکته کلیدی این است که اگر شماره موبایل در سیستم وجود نداشته باشد، کاربر به صورت **خودکار (Auto-Register)** ثبت‌نام شده و شناسه مسافر (`passenger\_id`) ایجاد می‌گردد.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Request OTP / Login

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fauth%2Fot"><div class="endpoint-info"><div>**URL:** `/b2c/v1/auth/otp`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1UserController@authOtp</div><div>**Auth:** Public (Guest Access)</div></div></div>### Body Parameters (الزامی)

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">mobile</td><td>String</td><td>شماره موبایل کاربر.   
<small>سیستم فرمت را بررسی کرده و در صورت نیاز عدد '0' را به ابتدای آن اضافه می‌کند.</small></td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>شناسه شعبه فعال (Office ID).   
<small>در صورت ثبت‌نام جدید، کاربر به این شعبه منتسب می‌شود.</small></td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-stat"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>بولین (true/false) که موفقیت عملیات ارسال پیامک را نشان می‌دهد.</td></tr><tr><td dir="ltr">data.passenger\_id</td><td>شناسه منحصر به فرد کاربر (Customer ID) در دیتابیس. چه کاربر جدید باشد چه قدیمی، این شناسه برگردانده می‌شود.</td></tr><tr><td dir="ltr">message</td><td>پیام متنی وضعیت عملیات جهت نمایش به کاربر.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (201 Created):**

```json
{
    "status": true,
    "time": 1702134567,
    "data": {
        "passenger_id": 5421
    },
    "message": "OTP با موفقیت ارسال گردید."
}
```

**۲. خطای محدودیت ارسال (422 Unprocessable Entity - Code 1204):**

```json
{
    "status": false,
    "code": 1204,
    "message": "تعداد درخواست های کاربر بیش از حد مجاز بوده است. لطفا 3 ساعت دیگر اقدام نمائید."
}
```

**۳. خطای پنل پیامک (422 Unprocessable Entity - Code 1202):**

```json
{
    "status": false,
    "code": 1202,
    "message": "پیامک OTP ارسال نشد. مشکلی رخ داده است. لطفا دوباره تلاش کنید",
    "trace": { ... }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28mobile-%2B-br"><div class="flowchart"><div class="flow-item">Request (Mobile + Branch)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Normalize Mobile**  
Ensure starts with '0'  
Convert Persian nums to English</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Exist in `customers`?</div><div style="display: flex; justify-content: space-between; width: 300px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #fff3cd;">**Auto Register**  
INSERT INTO customers  
Branch: [$branch]</div></div><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">Select ID</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Generate OTP**  
`StaticController::generateOtp`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Send SMS**  
`StaticController::sendNotification`  
<small>Log to SnailJob Queue</small></div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON (Passenger ID)</div></div></div>

# POST /b2c/v1/auth/submit

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## B2C Verify OTP &amp; Login

این اندپوینت مرحله نهایی احراز هویت است.   
کلاینت کد دریافتی (OTP) را به همراه شناسه مسافر ارسال می‌کند. سیستم کد را در جدول `otp_requests` اعتبارسنجی کرده (بررسی انقضا و عدم استفاده قبلی) و در صورت صحت، وضعیت کد را به "استفاده شده" تغییر می‌دهد و یک **JWT Token** با اعتبار ۷ روز برای کاربر صادر می‌کند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Submit OTP

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fauth%2Fsu"><div class="endpoint-info"><div>**URL:** `/b2c/v1/auth/submit`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1UserController@authSubmit</div><div>**Auth:** Public (Guest Access)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Domain</td><td>دامنه سایت فروش (جهت ثبت در Payload توکن به عنوان `iss` و `aud`).</td></tr></tbody></table>

</div>### Body Parameters (الزامی)

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">passenger\_id</td><td>Integer</td><td>شناسه مسافر (دریافت شده در پاسخ مرحله قبل).</td></tr><tr><td dir="ltr">otp</td><td>String/Int</td><td>کد یکبار مصرف دریافتی در پیامک.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>شناسه شعبه (جهت ثبت در کلیم `brn` توکن).</td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-stat"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>وضعیت عملیات (true).</td></tr><tr><td dir="ltr">access\_token</td><td>توکن احراز هویت (JWT) که باید در هدر `Authorization: Bearer ...` درخواست‌های بعدی ارسال شود.   
<small>مدت اعتبار: ۶۰۴۸۰۰ ثانیه (یک هفته).</small></td></tr><tr><td dir="ltr">user</td><td>آبجکت حاوی اطلاعات پروفایل کاربر (نام، نام خانوادگی، موبایل و کدملی).</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (Login Success):**

```json
{
    "status": true,
    "user": {
        "uuid": 5421,
        "from": "user",
        "role": "passenger",
        "type": "passenger",
        "level": 1,
        "data": {
            "title": {
                "fa": "محمد محمدی",
                "en": "Mohammad Mohammadi"
            },
            "sex": "male",
            "first_name": "Mohammad",
            "last_name": "Mohammadi",
            "first_name_fa": "محمد",
            "last_name_fa": "محمدی",
            "mobile": "09121234567",
            "national_code": "0012345678"
        }
    },
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
```

**۲. خطای نامعتبر بودن کد (422 Unprocessable Entity - Code 1209):**

```json
{
    "status": false,
    "code": 1209,
    "message": "رمز یکبار مصرف وارد شده معتبر نمی باشد."
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28id-%2B-otp%29-%E2%86%93"><div class="flowchart"><div class="flow-item">Request (ID + OTP)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Validate OTP**  
<small>Check table `otp\_requests`</small>  
Match ID &amp; Code AND  
Not Expired AND Not Used</div><div style="display: flex; justify-content: space-between; width: 350px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Invalid</div><div class="flow-item-process" style="background-color: #f8d7da;">**Error 1209**  
Return 422 JSON</div></div><div style="text-align: center;"><div class="flow-arrow">↓ Valid</div><div class="flow-item-process" style="background-color: #e3f2fd;">**Update OTP**  
Set `is_used = 1`</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Fetch User Info**  
SELECT from `customers`  
Detect User Agent (Device)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Generate JWT**  
Claims: uuid, branch, ip, domain  
Exp: +7 Days</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Token + User Data</div></div></div>

# POST /b2c/v1/auth/basic

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## B2C Colleague Authentication (Basic)

این اندپوینت جهت ورود **همکاران و آژانس‌های طرف قرارداد (Colleagues)** طراحی شده است.   
سیستم با دریافت نام کاربری و رمز عبور، اعتبار حساب را در جدول `colleague_auth` بررسی کرده و در صورت فعال بودن حساب و عدم انقضای قرارداد، یک توکن JWT صادر می‌کند. همچنین جزئیات سقف اعتبار و اطلاعات تماس همکار نیز بازگردانده می‌شود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Colleague Login

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fauth%2Fba"><div class="endpoint-info"><div>**URL:** `/b2c/v1/auth/basic`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1UserController@authBasic</div><div>**Auth:** Public (Guest Access)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Domain</td><td>دامنه سایت جهت ثبت در توکن (iss/aud).</td></tr></tbody></table>

</div>### Body Parameters (الزامی)

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">username</td><td>String</td><td>نام کاربری همکار.</td></tr><tr><td dir="ltr">password</td><td>String</td><td>رمز عبور.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>شناسه شعبه فعال (Office ID).</td></tr></tbody></table>

  </div>### Response Structure (Success)

<div class="api-docs" id="bkmrk-key-description-acce"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">access\_token</td><td>توکن دسترسی JWT با طول عمر ۷ روز.</td></tr><tr><td dir="ltr">user.uuid</td><td>شناسه رکورد همکار در جدول `colleagues`.</td></tr><tr><td dir="ltr">user.role</td><td>نقش همکار: - `pledger` (اگر type=1 باشد - متعهد)
- `partner` (سایر موارد - همکار)

</td></tr><tr><td dir="ltr">user.ceiling</td><td>اطلاعات سقف اعتباری: - `amount`: مبلغ اعتبار
- `deadline`: مهلت تسویه (ماهانه)

</td></tr><tr><td dir="ltr">user.data</td><td>آبجکت جزئیات شامل نام آژانس، لوگو، تلفن و کدملی صاحب حساب.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (Login Success):**

```json
{
    "user": {
        "uuid": 105,
        "from": "colleague",
        "type": "agency",
        "role": "partner",
        "data": {
            "id": 88,
            "title": {
                "fa": "آژانس مسافرتی آسمان",
                "en": "Aseman Travel Agency"
            },
            "subtitle": {
                "fa": "user123 - علی رضایی",
                "en": "Aseman Travel Agency"
            },
            "additional": false,
            "avatar": "logo_105.jpg",
            "sex": "male",
            "first_name": "Ali",
            "last_name": "Rezaei",
            "first_name_fa": "علی",
            "last_name_fa": "رضایی",
            "mobile": "09121111111",
            "phone": "02188888888",
            "national_code": "0055555555"
        },
        "level": 2,
        "ceiling": {
            "amount": 50000000,
            "deadline": 1
        },
        "expired_at": null
    },
    "access_token": "eyJ0eXAiOiJjb2xsZWFndWUiLCJhbGciOiJIUzI1NiJ9..."
}
```

**۲. خطا - اطلاعات نادرست (Invalid Credentials):**

```json
{
    "error": {
        "type": "username",
        "message": "اطلاعات وارد شده همخوانی ندارد."
    }
}
```

**۳. خطا - حساب غیرفعال (Inactive Account):**

```json
{
    "error": {
        "type": "username",
        "message": "وضعیت شما بصورت غیرفعال می باشد."
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28user-%2B-pass"><div class="flowchart"><div class="flow-item">Request (User + Pass + Branch)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Query DB (`colleague\_auth`)**  
Match Username &amp; Branch  
Check `expired_at` &gt; Now (or Null)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Record Found?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Verify Password**  
`Hash::check(input, db_hash)`</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error: Invalid Info</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Status == 1?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes (Active)</div><div class="flow-item-process">**Logging**  
Dispatch `SystemLog` to `snailJob` queue (10min delay)</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No (Inactive)</div><div class="flow-item-process" style="background-color: #f8d7da;">Error: Inactive Status</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Generate JWT**  
Type: `colleague`  
Claims: uuid, brn, uip, brw</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return User Data + Token</div></div></div>

# POST /b2c/v1/get_country

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Nationalities List

این اندپوینت لیست کشورهای فعال که دارای عنوان «ملیت» (Nationality) تعریف شده هستند را باز می‌گرداند.   
از این داده‌ها معمولاً در فرم‌های ورود اطلاعات مسافر جهت انتخاب **ملیت/تابعیت** (مثلاً: ایرانی، آلمانی و...) استفاده می‌شود. لیست خروجی تنها شامل رکوردهایی است که وضعیت آن‌ها فعال (`status = 1`) باشد.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Country/Nationality Data

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fget_cou"><div class="endpoint-info"><div>**URL:** `/b2c/v1/get_country`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1UserController@getCitizen</div><div>**Auth:** Public (Guest Access)</div></div></div>### Parameters

این درخواست نیاز به پارامتر ورودی (Body Parameters) ندارد.

<div class="api-docs" id="bkmrk--2">  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>Boolean</td><td>وضعیت درخواست (true).</td></tr><tr><td dir="ltr">data.countries</td><td>Array</td><td>آرایه‌ای از آبجکت‌های کشور/ملیت.</td></tr><tr><td dir="ltr">↳ id</td><td>Integer</td><td>شناسه عددی کشور در دیتابیس.</td></tr><tr><td dir="ltr">↳ iso</td><td>String</td><td>کد دو حرفی ایزو کشور (مثلاً IR, US).</td></tr><tr><td dir="ltr">↳ fa\_nationality</td><td>String</td><td>عنوان فارسی ملیت (مثلاً: ایرانی).</td></tr><tr><td dir="ltr">↳ en\_nationality</td><td>String</td><td>عنوان انگلیسی ملیت (مثلاً: Iranian).</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (200 OK):**

```json
{
    "status": true,
    "time": 1702138899,
    "data": {
        "countries": [
            {
                "id": 105,
                "iso": "IR",
                "fa_nationality": "ایرانی",
                "en_nationality": "Iranian"
            },
            {
                "id": 220,
                "iso": "TR",
                "fa_nationality": "ترک",
                "en_nationality": "Turkish"
            },
            {
                "id": 68,
                "iso": "DE",
                "fa_nationality": "آلمانی",
                "en_nationality": "German"
            }
        ]
    }
}
```

**۲. خطای سرور (400 Bad Request):**

```json
{
    "status": false,
    "error": "Database connection error..."
}
```

<div class="api-docs" id="bkmrk--3">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-received-%E2%86%93-q"><div class="flowchart"><div class="flow-item">Request Received</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Query Database (Countries)**  
`WHERE status = 1`  
`AND fa_nationality IS NOT NULL`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Select Columns**  
id, iso, fa_nationality, en_nationality</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON Response</div></div></div>

# GET /b2c/v1/user/passengers

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get User Passengers List

این اندپوینت لیست تمامی مسافران مرتبط با حساب کاربری فعلی را باز می‌گرداند.   
سیستم لیست مسافران را از دو منبع استخراج می‌کند:

<div class="api-docs" id="bkmrk-%D8%B3%D8%A7%D8%A8%D9%82%D9%87-%D8%AE%D8%B1%DB%8C%D8%AF-%28history%29"><div class="endpoint-section">1. **سابقه خرید (History):** افرادی که کاربر لاگین شده قبلاً در فاکتورهای خود (`factors`) برای آن‌ها بلیط (`factor\_items`) تهیه کرده است (به جز خود کاربر).
2. **زیرمجموعه‌ها (Relationship):** افرادی که در پروفایل کاربر به عنوان همراه یا خانواده تعریف شده‌اند (`relationship = user\_id`).

</div></div><div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Passengers

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fuser%2Fpa"><div class="endpoint-info"><div>**URL:** `/b2c/v1/user/passengers`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1UserController@passengersList</div><div>**Auth:** Required (Bearer Token)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Authorization</td><td>توکن احراز هویت (Bearer Token).</td></tr><tr><td dir="ltr">Domain</td><td>دامنه سایت.</td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>Boolean</td><td>وضعیت درخواست.</td></tr><tr><td dir="ltr">time</td><td>Timestamp</td><td>زمان سرور.</td></tr><tr><td dir="ltr">data</td><td>Array\[Object\]</td><td>لیست کامل آبجکت‌های مسافران (از جدول `customers`).</td></tr><tr><td dir="ltr">↳ id</td><td>Integer</td><td>شناسه مسافر.</td></tr><tr><td dir="ltr">↳ first\_name / last\_name</td><td>String</td><td>نام و نام خانوادگی.</td></tr><tr><td dir="ltr">↳ national\_code</td><td>String</td><td>کد ملی.</td></tr><tr><td dir="ltr">↳ passport\_number</td><td>String</td><td>شماره پاسپورت (در صورت وجود).</td></tr><tr><td dir="ltr">↳ gender</td><td>Boolean/Int</td><td>جنسیت (معمولاً 1: مرد، 0: زن).</td></tr><tr><td dir="ltr">↳ birthday</td><td>String</td><td>تاریخ تولد.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (200 OK):**

```json
{
    "status": true,
    "time": 1702145000,
    "data": [
        {
            "id": 501,
            "first_name": "Saeed",
            "last_name": "Mohammadi",
            "first_name_fa": "سعید",
            "last_name_fa": "محمدی",
            "national_code": "0077777777",
            "gender": 1,
            "birthday": "1990-05-20",
            "passport_number": "A12345678",
            "relationship": 105,
            "created_at": "2023-01-01 12:00:00"
        },
        {
            "id": 890,
            "first_name": "Sara",
            "last_name": "Tehrani",
            "first_name_fa": "سارا",
            "last_name_fa": "تهرانی",
            "national_code": "0088888888",
            "gender": 0,
            "birthday": "1995-10-10",
            "passport_number": null,
            "relationship": null, 
            "note": "Fetched from history"
        }
    ]
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28get%29-%E2%86%93-iden"><div class="flowchart"><div class="flow-item">Request (GET)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Identify User**  
Get `operator->id` from JWT</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Query 1: Purchase History**  
Find `customer_id` in `factor_items` where:  
1. Invoice owner is User (`factors.customer == id`)  
2. Passenger is NOT User (`customer_id != id`)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Query 2: Fetch Customers**  
`SELECT * FROM customers WHERE:`  
Condition A: `relationship == user_id`  
**OR**  
Condition B: `id IN [History IDs]`</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return List</div></div></div>

# GET /b2c/v1/office/users

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Office/Company Users

این اندپوینت جهت دریافت لیست تمامی کاربران (Users) تعریف شده در زیرمجموعه یک شرکت یا آژانس (Colleague) استفاده می‌شود.   
<span style="color: #d9534f; font-weight: bold;">نکته مهم:</span> دسترسی به این متد محدود است و تنها کاربرانی که سطح دسترسی آن‌ها **مدیریت (Management)** باشد مجاز به فراخوانی آن هستند. در غیر این صورت خطای 406 بازگردانده می‌شود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Sub-Users

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Foffice%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/office/users`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1UserController@usersOffice</div><div>**Auth:** Required (Bearer Token - Management Only)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Authorization</td><td>توکن احراز هویت (Bearer Token) کاربر مدیر.</td></tr></tbody></table>

  </div>### Response Structure (Success)

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">items</td><td>Array</td><td>لیست کاربران زیرمجموعه.</td></tr><tr><td dir="ltr">↳ id</td><td>Integer</td><td>شناسه کاربر.</td></tr><tr><td dir="ltr">↳ username</td><td>String</td><td>نام کاربری جهت ورود.</td></tr><tr><td dir="ltr">↳ category</td><td>String</td><td>دسته‌بندی نقش کاربر (مثلاً management, sales).</td></tr><tr><td dir="ltr">↳ level</td><td>Integer</td><td>سطح دسترسی عددی.</td></tr><tr><td dir="ltr">↳ ceiling</td><td>Object</td><td>تنظیمات سقف اعتبار (amount: مبلغ, deadline: مهلت).</td></tr><tr><td dir="ltr">↳ status</td><td>Integer</td><td>وضعیت کاربر (1: فعال، 0: غیرفعال).</td></tr><tr><td dir="ltr">meta.timestamp</td><td>Timestamp</td><td>زمان تولید پاسخ.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (مدیر):**

```json
{
    "items": [
        {
            "id": 10,
            "colleague": 55, 
            "username": "admin_agency",
            "category": "management",
            "level": 1,
            "ceiling": {
                "amount": false,
                "deadline": false
            },
            "expired_at": false,
            "created_at": "2023-01-01 10:00:00",
            "updated_at": "2023-05-20 14:30:00",
            "status": 1
        },
        {
            "id": 12,
            "colleague": 55,
            "username": "sales_counter",
            "category": "sales",
            "level": 2,
            "ceiling": {
                "amount": 5000000,
                "deadline": 1
            },
            "expired_at": "2024-12-29",
            "created_at": "2023-02-15 09:00:00",
            "updated_at": "2023-02-15 09:00:00",
            "status": 1
        }
    ],
    "meta": {
        "timestamp": 1702150000
    }
}
```

**۲. خطا - عدم دسترسی (کاربر عادی):**

```json
{
    "error": {
        "code": 1000,
        "message": "دسترسی فقط برای سطح مدیریت"
    },
    "meta": {
        "timestamp": 1702150005
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-received-%E2%86%93-u"><div class="flowchart"><div class="flow-item">Request Received</div><div class="flow-arrow">↓</div><div class="flow-item-decision">User Category == 'management'?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Fetch Users**  
Query `colleague_auth`  
Where `colleague` == Operator's Colleague ID</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error 406  
"Access Denied"</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Map &amp; Format Data**  
Format Dates, Check Ceilings, Enrich Colleague Info (if applicable)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return JSON</div></div></div>

# GET /b2c/v1/cost-center/list/form

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Cost Centers List (Form)

این اندپوینت جهت دریافت لیست مراکز هزینه (Cost Centers) برای فرم‌ها استفاده می‌شود.   
<span style="color: #d9534f; font-weight: bold;">محدودیت دسترسی:</span> این سرویس صرفاً مخصوص کاربران B2B (همکاران) است که شرکت آن‌ها در سطح **هلدینگ (Category 7)** تعریف شده باشد. هلدینگ‌ها از این طریق لیست زیرمجموعه‌های خود (که فیلد relationship آن‌ها برابر با شناسه هلدینگ است) را دریافت می‌کنند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Cost Centers

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fcost-ce"><div class="endpoint-info"><div>**URL:** `/b2c/v1/cost-center/list/form`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1UserController@getCostCenterForRequest</div><div>**Auth:** Required (Bearer Token)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Authorization</td><td>توکن احراز هویت (Bearer Token).</td></tr></tbody></table>

  </div>### Response Structure (Success)

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">items</td><td>Array</td><td>لیست مراکز هزینه (زیرمجموعه‌های هلدینگ).</td></tr><tr><td dir="ltr">↳ id</td><td>Integer</td><td>شناسه مرکز هزینه (Colleague ID).</td></tr><tr><td dir="ltr">↳ title</td><td>Object</td><td>عنوان نمایشی مرکز هزینه.</td></tr><tr><td dir="ltr">↳ title.fa</td><td>String</td><td>عنوان فارسی (ترکیب نام آفیس + نام و نام خانوادگی مسئول).</td></tr><tr><td dir="ltr">↳ title.en</td><td>String</td><td>عنوان انگلیسی آفیس.</td></tr><tr><td dir="ltr">meta.timestamp</td><td>Timestamp</td><td>زمان تولید پاسخ.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز (کاربر هلدینگ):**

```json
{
    "items": [
        {
            "id": 205,
            "title": {
                "fa": "دپارتمان مالی - علی رضایی",
                "en": "Finance Department"
            }
        },
        {
            "id": 208,
            "title": {
                "fa": "شعبه شمال تهران", 
                "en": "North Branch"
            }
        }
    ],
    "meta": {
        "timestamp": 1702155000
    }
}
```

**۲. خطا - عدم دسترسی (کاربر معمولی یا شرکت غیر هلدینگ):**

```json
{
    "error": {
        "code": 1000,
        "message": "دسترسی فقط برای همکاران سطح هلدینگ امکانپذیر است."
    },
    "meta": {
        "timestamp": 1702155005
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-received-%E2%86%93-g"><div class="flowchart"><div class="flow-item">Request Received</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Group check:  
is 'colleague' OR 'b2b'?</div><div style="display: flex; justify-content: space-between; width: 500px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Check Company Type**  
Get operator's colleague info.  
Condition: `category == 7` (Holding)</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error 406  
"Access Denied"</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is Holding (Cat 7)?</div><div style="display: flex; justify-content: space-between; width: 500px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Fetch Children**  
`SELECT * FROM colleagues`  
WHERE `relationship == holding_id`</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error 406  
"Not a Holding Company"</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Format Output**  
Generate FA Title: Office + (First Name + Last Name)</div><div class="flow-arrow">↓</div><div class="flow-item-success">Return Items</div></div></div>

# GET /b2c/v1/credit-card

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Credit Card Detail

این اندپوینت برای دریافت اطلاعات یک کارت اعتباری خاص استفاده می‌شود.   
<span style="color: #0275d8; font-weight: bold;">ویژگی امنیتی (Security Masking):</span> سیستم به طور خودکار بررسی می‌کند که آیا کاربر درخواست‌دهنده (`operator`) همان مالک کارت (`passenger\_id`) است یا خیر:

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D9%85%D8%A7%D9%84%DA%A9-%D8%A8%D8%A7%D8%B4%D8%AF%3A-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9"><div class="endpoint-section">- **اگر مالک باشد:** اطلاعات کامل شامل `cvv2`، تاریخ انقضا و شماره کامل کارت نمایش داده می‌شود.
- **اگر مالک نباشد:** شماره کارت به صورت ماسک شده (مثلاً `6037\*\*\*\*1234`) برمی‌گردد و فیلدهای حساس (`cvv2`, `expire\_date`) حذف می‌شوند.

</div></div><div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Credit Card

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fcredit-"><div class="endpoint-info"><div>**URL:** `/b2c/v1/credit-card`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** CreditCards@showCreditCard</div><div>**Auth:** Required (Bearer Token)</div></div></div>### Headers (الزامی)

<div class="api-docs" id="bkmrk-header-name-descript"><table class="schema-table" dir="rtl"><thead><tr><th>Header Name</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">Authorization</td><td>توکن احراز هویت (Bearer Token).</td></tr></tbody></table>

</div>### Query Parameters (فیلترها)

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">id</td><td>Integer</td><td>(اختیاری) جستجو بر اساس شناسه کارت در سیستم.</td></tr><tr><td dir="ltr">passenger\_id</td><td>Integer</td><td>(اختیاری) جستجو بر اساس شناسه مسافر (مالک کارت).</td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">payload.id</td><td>Integer</td><td>شناسه رکورد کارت.</td></tr><tr><td dir="ltr">payload.status</td><td>Integer</td><td>وضعیت کارت (معمولاً 1 فعال).</td></tr><tr><td dir="ltr">payload.card.number</td><td>String</td><td>شماره کارت (کامل یا ماسک شده بسته به مالکیت).</td></tr><tr><td dir="ltr">payload.card.withdrawal\_limit</td><td>Integer/Bool</td><td>سقف برداشت (در صورت وجود).</td></tr><tr><td dir="ltr">payload.card.blocked\_amount</td><td>Integer/Bool</td><td>مبلغ بلوکه شده.</td></tr><tr><td dir="ltr" style="color: red;">payload.card.cvv2</td><td>String</td><td><span style="color: red;">(شرطی)</span> فقط اگر کاربر لاگین شده مالک کارت باشد ارسال می‌شود.</td></tr><tr><td dir="ltr" style="color: red;">payload.card.expire\_date</td><td>String</td><td><span style="color: red;">(شرطی)</span> فقط اگر کاربر لاگین شده مالک کارت باشد ارسال می‌شود.</td></tr><tr><td dir="ltr">payload.passenger</td><td>Object</td><td>اطلاعات صاحب کارت.</td></tr></tbody></table>

</div>### Example Responses

**۱. موفقیت آمیز - مالک کارت (نمایش کامل):**

```json
{
    "payload": {
        "id": 101,
        "status": 1,
        "card": {
            "number": "6037991122334455",
            "withdrawal_limit": 50000000,
            "blocked_amount": 0,
            "cvv2": "1234",           // موجود است
            "expire_date": "04/05"    // موجود است
        },
        "passenger": {
            "id": 55,
            "first_name": "علی",
            "last_name": "محمدی"
        }
    },
    "meta": {
        "timestamp": 1702160000
    }
}
```

**۲. موفقیت آمیز - غیر مالک (اطلاعات امنیتی مخفی):**

```json
{
    "payload": {
        "id": 101,
        "status": 1,
        "card": {
            "number": "6037********4455", // ماسک شده
            "withdrawal_limit": false,
            "blocked_amount": false
            // فیلدهای cvv2 و expire_date وجود ندارند
        },
        "passenger": {
            "id": 55,
            "first_name": "علی",
            "last_name": "محمدی"
        }
    },
    "meta": {
        "timestamp": 1702160010
    }
}
```

**۳. خطا - یافت نشد:**

```json
{
    "error": {
        "code": 1000,
        "message": "کارت اعتباری پیدا نشد."
    },
    "meta": {
        "timestamp": 1702160020
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28get%29-%E2%86%93-data"><div class="flowchart"><div class="flow-item">Request (GET)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Database Query**  
Join `credit_cards` with `customers`  
Filter by `id` OR `passenger_id` from Request</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Record Found?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Check Ownership**  
`request->operator->id == card->passenger_id`</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error 422  
"Card Not Found"</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is Owner?</div><div style="display: flex; justify-content: space-between; width: 500px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-success">**Return Full Data**  
Include `cvv2`, `expire_date`  
Real Card Number</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #fff3cd;">**Return Safe Data**  
Exclude Sensitive Fields  
Mask Number: `1234********5678`</div></div></div></div></div>

# POST /b2c/v1/media/upload/s3

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Upload Media to S3

این اندپوینت جهت آپلود فایل‌ها (تصاویر، اسناد و ...) بر روی فضای ذخیره‌سازی ابری (S3 Compatible / Liara) استفاده می‌شود.   
<span style="color: #0275d8; font-weight: bold;">انعطاف‌پذیری:</span> کلاینت می‌تواند محدودیت‌های حجم، فرمت فایل و مسیر ذخیره‌سازی را در درخواست تعیین کند. در غیر این صورت، مقادیر پیش‌فرض سیستم اعمال می‌شوند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Upload File

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fmedia%2Fu"><div class="endpoint-info"><div>**URL:** `/b2c/v1/media/upload/s3`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1S3Controller@uploadFile</div><div>**Content-Type:** multipart/form-data</div></div></div>### Body Parameters (Multipart/Form-Data)

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th><th>Default / Note</th></tr></thead><tbody><tr><td dir="ltr">file</td><td>File</td><td><span style="color: red;">Yes</span></td><td>فایل مورد نظر برای آپلود.</td><td>-</td></tr><tr><td dir="ltr">branch</td><td>String</td><td>No\*</td><td>شناسه برنچ (برای ساختار پوشه‌بندی پیش‌فرض).</td><td>در مسیر پیش‌فرض استفاده می‌شود.</td></tr><tr><td dir="ltr">type</td><td>String</td><td>No\*</td><td>نوع فایل (مثلاً avatar, ticket) برای پوشه‌بندی.</td><td>در مسیر پیش‌فرض استفاده می‌شود.</td></tr><tr><td dir="ltr">name</td><td>String</td><td>No</td><td>نام دلخواه برای ذخیره فایل (بدون پسوند).</td><td>Timestamp + Random ID</td></tr><tr><td dir="ltr">size</td><td>Integer</td><td>No</td><td>حداکثر حجم مجاز به کیلوبایت.</td><td>`5120` (5MB)</td></tr><tr><td dir="ltr">mimes</td><td>String</td><td>No</td><td>فرمت‌های مجاز (با کاما جدا شوند).</td><td>`jpeg,png,jpg,gif,pdf,webp`</td></tr><tr><td dir="ltr">path</td><td>String</td><td>No</td><td>مسیر کامل ذخیره‌سازی (Folder Path).</td><td>`b2c/uploads/{branch}/{type}/{Y}/{m}/`</td></tr></tbody></table>

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">status</td><td>Boolean</td><td>وضعیت عملیات (true موفق، false ناموفق).</td></tr><tr><td dir="ltr">file</td><td>String</td><td>مسیر کامل (Key) فایل آپلود شده در باکت S3.</td></tr></tbody></table>

</div>### Example Responses

**۱. آپلود موفق (Success - 201):**

```json
{
    "status": true,
    "file": "b2c/uploads/1001/avatar/2023/12/custom-name.jpg"
}
```

**۲. خطای اعتبارسنجی (Validation Error - 422):**

```json
{
    "message": "The file must be a file of type: jpeg, png, jpg, gif, pdf, webp.",
    "errors": {
        "file": [
            "The file must be a file of type: jpeg, png, jpg, gif, pdf, webp."
        ]
    }
}
```

**۳. خطای سرور/آپلود (Error - 400):**

```json
{
    "status": false,
    "time": 1702165000
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28multipart%29-"><div class="flowchart"><div class="flow-item">Request (Multipart)</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Config Initialization**  
Set Size: input OR 5120KB  
Set Mimes: input OR defaults  
Set Path: input OR `b2c/uploads/...`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Validation Passed?</div><div style="display: flex; justify-content: space-between; width: 400px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Generate Filename**  
If `name` exists → Use it  
Else → `Y-m-d-His-microtime`</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Error 422  
Validation Exception</div></div></div><div class="flow-arrow">↓</div><div class="flow-item-process">**Storage Put**  
Disk: 'liara'  
`put(path + filename, contents)`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Upload Success?</div><div style="display: flex; justify-content: space-between; width: 500px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-success">Return 201  
{"status": true, "file": "path..."}</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-process" style="background-color: #f8d7da;">Return 400  
{"status": false}</div></div></div></div></div>

# GET /b2c/v1/base/data

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Base Data (Cities, Trains, Configs)

این اندپوینت مرکزی برای دریافت داده‌های پایه (Base Data) و لیست‌های انتخابی (Select Options) در سیستم استفاده می‌شود.   
<span style="color: #0275d8; font-weight: bold;">عملکرد داینامیک:</span> خروجی این سرویس کاملاً وابسته به پارامتر ورودی `subject` است. این متد برای پر کردن لیست شهرها، ایستگاه‌های قطار، فرودگاه‌ها و تنظیمات هتل کاربرد دارد.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Base Data

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fbase%2Fda"><div class="endpoint-info"><div>**URL:** `/b2c/v1/base/data`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1BaseController@baseData</div><div>**Auth:** Public / Optional</div></div></div>### Query Parameters (پارامترهای ورودی)

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">subject</td><td>String</td><td><span style="color: red;">Yes\*</span></td><td>نوع داده درخواستی. مقادیر مجاز: - `cities` (لیست شهرها)
- `train_companies` (شرکت‌های ریلی)
- `train_types` (انواع قطار: ۴ ستاره و...)
- `train_stations` (ایستگاه‌های قطار)
- `room_rate` (انواع نرخ اتاق هتل)
- `room_view` (انواع چشم‌انداز اتاق)
- `airports` (لیست فرودگاه‌ها)

</td></tr><tr><td dir="ltr">search</td><td>String</td><td>No</td><td>عبارت جستجو (برای شهرها، ایستگاه‌ها و شرکت‌ها).</td></tr><tr><td dir="ltr">state</td><td>Integer</td><td>No</td><td>شناسه استان (جهت فیلتر کردن شهرها یا ایستگاه‌ها بر اساس استان).</td></tr><tr><td dir="ltr">type</td><td>String</td><td>No</td><td>فقط برای `subject=cities` کاربرد دارد.  
اگر مقدار `train` ارسال شود، فقط شهرهایی که دارای ایستگاه قطار هستند بازگردانده می‌شوند.</td></tr><tr><td dir="ltr">action</td><td>String</td><td>No</td><td>حالت خاص: اگر مقدار `airports` باشد، فرمت خروجی جیسون تغییر می‌کند.</td></tr><tr><td dir="ltr">route</td><td>Integer</td><td>No</td><td>فقط برای فرودگاه‌ها (1 یا 2) - جهت سورت یا فیلتر خاص (در کد فعلی منطق مشابه است).</td></tr></tbody></table>

  </div>### Response Structure

ساختار پاسخ بسته به `subject` متفاوت است، اما به طور کلی در فرمت زیر است:

```json
{
    "items": [ ...Array of Data... ],
    "meta": {
        "timestamp": 1702123456
    }
}
```

### Examples per Subject (مثال‌ها بر اساس موضوع)

**۱. شهرها (subject=cities):**

```json
{
    "items": [
        {
            "id": 10,
            "title": { "fa": "تهران", "en": "Tehran" },
            "category": {
                "id": 5,
                "title": { "fa": "تهران", "en": "Tehran" } // نام استان
            }
        }
    ],
    "meta": { ... }
}
```

**۲. انواع قطار (subject=train\_types):**

```json
{
    "items": [
        {
            "id": 1,
            "title": { "fa": "4 ستاره اتوبوسی", "en": false },
            "details": {
                "type": "bus",
                "star": 4
            }
        }
    ],
    "meta": { ... }
}
```

**۳. نرخ/دید اتاق (subject=room\_rate / room\_view):**

```json
{
    "items": [
        {
            "title": { "fa": "برد", "en": "Board" },
            "status": "1",
            "selected": "0"
        }
    ],
    "meta": { ... }
}
```

**۴. حالت خاص فرودگاه (action=airports):**

اگر پارامتر `action=airports` ارسال شود، ساختار پاسخ متفاوت است:

```json
{
    "status": true,
    "time": 1702123456,
    "data": {
        "titles": [
            {
                "id": 1,
                "iata": "MHD",
                "title": "Mashhad",
                "title_fa": "مشهد",
                "country": 1,
                "group_by": "ایران",
                "state": "خراسان رضوی"
                ...
            }
        ]
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-%28get%29-%E2%86%93-chec"><div class="flowchart"><div class="flow-item">Request (GET)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">Check `subject`?</div><div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;"><div style="border: 1px solid #ddd; padding: 5px; border-radius: 4px; width: 140px;">**cities**  
<span style="font-size: 10px;">If type='train' → Filter IDs from `train\_stations`</span>  
<span style="font-size: 10px;">Search/State Filter</span></div><div style="border: 1px solid #ddd; padding: 5px; border-radius: 4px; width: 140px;">**train\_\***  
<span style="font-size: 10px;">Query `train\_companies`, `types`, or `stations`</span></div><div style="border: 1px solid #ddd; padding: 5px; border-radius: 4px; width: 140px;">**room\_\***  
<span style="font-size: 10px;">Decode JSON from `hotel\_titles` table</span></div><div style="border: 1px solid #ddd; padding: 5px; border-radius: 4px; width: 140px;">**airports**  
<span style="font-size: 10px;">Join Countries &amp; States</span></div></div><div class="flow-arrow">↓</div><div class="flow-item-decision">Is `action` == 'airports'?</div><div style="display: flex; justify-content: space-between; width: 500px; margin: 0 auto;"><div style="text-align: center;"><div class="flow-arrow">↓ Yes</div><div class="flow-item-process">**Custom Format**  
{"status": true, "data": ...}</div></div><div style="text-align: center;"><div class="flow-arrow">↓ No</div><div class="flow-item-success">**Standard Format**  
{"items": [...], "meta": ...}</div></div></div></div></div>

# GET /b2c/v1/base/accommodations/list

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Search Accommodations &amp; Cities

این اندپوینت برای "Autocomplete" یا جستجوی اولیه در بخش هتل استفاده می‌شود.   
ورودی کاربر را گرفته و به صورت همزمان در لیست **شهرها** و **اقامتگاه‌ها** (هتل، آپارتمان و...) جستجو می‌کند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Get Accommodation List

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fbase%2Fac"><div class="endpoint-info"><div>**URL:** `/b2c/v1/base/accommodations/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1BaseController@accommodationList</div><div>**Auth:** Optional (Public)</div></div></div>### Query Parameters (پارامترهای ورودی)

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">search</td><td>String</td><td>No</td><td>عبارت مورد نظر برای جستجو.   
<span style="font-size: 0.9em; color: gray;">در نام شهر، نام هتل، آدرس، نام استان و نام کشور (فارسی و انگلیسی) جستجو می‌شود.</span></td></tr></tbody></table>

  </div>### Business Logic (منطق تجاری)

<div class="api-docs" id="bkmrk-%D8%AC%D8%B3%D8%AA%D8%AC%D9%88%DB%8C-%D8%B4%D9%87%D8%B1%D9%87%D8%A7-%28cities"><div class="logic-box" dir="rtl">1. **جستجوی شهرها (Cities Search):**
    - جدول `cities` با جداول `states` و `countries` جوین می‌شود.
    - اگر `search` ارسال شده باشد، در نام‌های فارسی و انگلیسی (شهر، استان، کشور) جستجو می‌کند.
    - نتایج بر اساس `priority` مرتب شده و حداکثر ۳۰ مورد بازگردانده می‌شود.
2. **جستجوی اقامتگاه‌ها (Accommodations Search):**
    - **شرط اجرا:** تنها در صورتی اجرا می‌شود که پارامتر `search` خالی نباشد.
    - **فیلتر:** فقط اقامتگاه‌های فعال (`status=1`).
    - **دامنه جستجو:** عنوان (فارسی/انگلیسی)، آدرس، و نام مکان‌های جغرافیایی مرتبط.
    - **بهینه‌سازی (Redis):** ابتدا شناسه (ID) هتل‌ها پیدا می‌شود. سپس سیستم چک می‌کند آیا اطلاعات کامل هتل در **Redis** (کلید: `accommodations:{id}`) وجود دارد یا خیر. 
        - اگر در کش نباشد: تابع `StaticController::getAccommodation` فراخوانی شده و نتیجه در Redis ذخیره می‌شود.
        - اگر در کش باشد: اطلاعات از Redis خوانده می‌شود.
    - **ترجمه نوع:** نوع اقامتگاه (مثلا hotel) توسط تابع `getTitleType` به فارسی (هتل) تبدیل می‌شود.

</div>---

  </div>### Response Structure

```json
{
    "status": true,
    "time": 1702123456,
    "data": {
        "cities": [ ...Array of Cities OR false... ],
        "accommodation": [ ...Array of Hotels OR false... ]
    }
}
```

#### 1. ساختار آبجکت شهر (Inside `cities` array)

```json
{
    "id": 10,
    "title": {
        "fa": "تهران",
        "en": "Tehran"
    },
    "country": {
        "fa": "ایران",
        "en": "Iran"
    },
    "state": {
        "fa": "تهران",
        "en": "Tehran"
    },
    "category": {
        "title": "شهر",
        "subtitle": false
    }
}
```

#### 2. ساختار آبجکت اقامتگاه (Inside `accommodation` array)

```json
{
    "id": 505,
    "en_title": "Espinas Palace",
    "fa_title": "هتل اسپیناس پالاس",
    "type": "هتل",  // Translated value
    "category": {
        "title": "هتل",
        "subtitle": false
    },
    "order": 1,
    // ... سایر فیلدهای بازگردانده شده از Redis/StaticController ...
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Accommodation Types Mapping

مقادیر فیلد `type` در خروجی اقامتگاه‌ها به صورت خودکار به فارسی ترجمه می‌شوند:

<div class="api-docs" id="bkmrk-database-value-%28key%29"><table class="schema-table" dir="rtl"><thead><tr><th>Database Value (key)</th><th>Output Value (fa)</th></tr></thead><tbody><tr><td dir="ltr">hotel</td><td>هتل</td></tr><tr><td dir="ltr">apartment</td><td>آپارتمان</td></tr><tr><td dir="ltr">suite</td><td>سویت</td></tr><tr><td dir="ltr">house</td><td>خانه</td></tr><tr><td dir="ltr">villa</td><td>ویلا</td></tr><tr><td dir="ltr">traditional</td><td>بومگردی</td></tr><tr><td dir="ltr">motel</td><td>موتل</td></tr><tr><td dir="ltr">guesthouse</td><td>میهمانسرا</td></tr></tbody></table>

</div>

# GET /b2c/v1/base/suggestions/items/{type}

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Suggestions &amp; Featured Items

این اندپوینت لیستی از آیتم‌های پیشنهادی (Featured) را برای نمایش در صفحه اصلی یا لندینگ پیج‌ها بازمی‌گرداند.   
<span style="color: #d9534f; font-weight: bold;">نکته مهم:</span> طبق کد فعلی، داده‌های این سرویس به صورت **Hardcoded** (ثابت) در کنترلر تعریف شده‌اند و برای شعب خاص (Branch IDs: 2, 4) شخصی‌سازی شده‌اند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Fetch Suggestion Items

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fbase%2Fsu"><div class="endpoint-info"><div>**URL:** `/b2c/v1/base/suggestions/items/{type}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1BaseController@suggestionsItemsIndex</div><div>**Auth:** Optional (Public)</div></div></div>### Parameters (پارامترها)

<div class="api-docs" id="bkmrk-type-parameter-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Type</th><th>Parameter</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>Path</td><td dir="ltr">{type}</td><td><span style="color: red;">Yes</span></td><td>نوع پیشنهاد مورد نظر: - `tour` (پکیج‌های تور)
- `train` (بلیط قطار)
- `aircraft` (بلیط هواپیما)
- `accommodation` (هتل و اقامتگاه)

</td></tr><tr><td>Query</td><td dir="ltr">branch</td><td><span style="color: red;">Yes\*</span></td><td>شناسه شعبه/سایت.   
<span style="font-size: 0.9em; color: gray;">(در کد فعلی فقط برای مقادیر **2** و **4** خروجی تعریف شده است).</span></td></tr></tbody></table>

  </div>### Response Structure

```json
{
    "items": [
        // ... Array of Suggestion Objects ...
    ],
    "meta": {
        "timestamp": 1702123456
    }
}
```

### Item Object Examples (نمونه خروجی آیتم‌ها)

ساختار آبجکت‌های داخل آرایه `items` بسته به `type` درخواستی متفاوت است:

#### ۱. نوع تور (type = tour)

```json
{
    "type": "tour",
    "vehicle": "train", // یا aircraft
    "cover": "https://storage.../image.jpg",
    "link": "/search/route-accommodation?origin=37&destination=396...", // لینک فرانت‌اند
    "title": {
        "fa": "تور چهارشب فول برد",
        "en": "4 nights full boards"
    },
    "accommodation": {
        "title": {
            "fa": "هتل آوازه مشهد",
            "en": "Avazeh Hotel"
        },
        "star": 2
    },
    "origin": "اصفهان",
    "destination": "مشهد",
    "price": 96000000,
    "nights": 4
}
```

#### ۲. نوع بلیط (type = train / aircraft)

```json
{
    "type": "ticket",
    "vehicle": "aircraft", // یا train
    "link": "/search/flights?origin=IFN&destination=MHD",
    "title": { // ممکن است در برخی موارد ارسال نشود
         "fa": "بلیت مشهد قطار",
         "en": "Mashhad ticket"
    },
    "origin": "اصفهان",
    "destination": "مشهد",
    "price": 33990000
}
```

#### ۳. نوع اقامتگاه (type = accommodation)

```json
{
    "type": "accommodation",
    "cover": "https://storage.../hotel.jpg",
    "link": "/search/accommodations?destination=335&action=accommodation",
    "title": {
        "fa": "اتاق دوتخته فول برد",
        "en": ""
    },
    "accommodation": {
        "title": {
            "fa": "هتل آوازه مشهد",
            "en": "Avazeh Hotel"
        },
        "star": 2
    },
    "destination": "مشهد",
    "price": 12000000
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Logic &amp; Constraints

<div class="api-docs" id="bkmrk-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%DA%AF%DB%8C-%D8%A8%D9%87-branch%3A-%D8%B3"><div class="logic-box" dir="rtl">- **وابستگی به Branch:** سیستم ابتدا پارامتر `branch` را چک می‌کند. اگر شعبه تعریف نشده باشد (چیزی غیر از 2 یا 4)، آرایه `items` خالی برمی‌گردد.
- **لینک‌دهی (Links):** فیلد `link` حاوی مسیر نسبی (Relative Path) برای هدایت کاربر در سمت فرانت‌اند است (مثلاً به صفحه جستجو با فیلترهای از پیش تعیین شده).
- **داده‌های استاتیک:** قیمت‌ها، تصاویر و عناوین در حال حاضر مستقیماً در کد PHP نوشته شده‌اند و از دیتابیس خوانده نمی‌شوند.

</div></div>

# GET /b2c/v1/trade/list

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## User Trade History (List Orders)

این اندپوینت لیست تمامی سفارشات و فاکتورهای کاربر (B2C) یا همکار (B2B) را بازمی‌گرداند.   
اطلاعات مالی، وضعیت پرداخت و جزئیات محصول (پرواز، هتل و...) برای هر سفارش محاسبه و ارسال می‌شود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># List User Trades

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Ftrade%2Fl"><div class="endpoint-info"><div>**URL:** `/b2c/v1/trade/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** V1TradeController@listTrade</div><div>**Middleware:** `authWithJwt` (Required)</div></div>  </div>### Business Logic (منطق تجاری)

<div class="api-docs" id="bkmrk-%D8%B4%D9%86%D8%A7%D8%B3%D8%A7%DB%8C%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1-%28user-"><div class="logic-box" dir="rtl">1. **شناسایی کاربر (User Identification):**
    - بر اساس توکن JWT، نوع کاربر (`group`) و اطلاعات کاربر (`operator`) استخراج می‌شود.
    - اگر گروه کاربر `b2b` یا `colleague` باشد، فیلتر بر اساس `colleague_auth` انجام می‌شود.
    - در غیر این صورت (مسافر عادی)، فیلتر بر اساس ستون `customer` انجام می‌شود.
2. **فیلتر سفارشات (Query Factors):**
    - سفارشاتی که وضعیت (status) آن‌ها **2** یا **5** باشد، از لیست حذف می‌شوند (`whereNotIn`).
    - لیست بر اساس شناسه (ID) به صورت نزولی (جدیدترین به قدیمی‌ترین) مرتب می‌شود.
3. **غنی‌سازی داده‌ها (Data Enrichment Loop):**   
    برای هر فاکتور یافت شده عملیات زیر انجام می‌شود: 
    - **عنوان (Redis):** تلاش برای دریافت عنوان فارسی سفارش از کلید Redis: `reference:{id}:information:title:fa`.
    - **نوع محصول:** استخراج `product` و `byproduct` از جدول `factor_items`.
    - **محاسبات مالی:** اگر عنوان در Redis موجود باشد، تابع `ApiTradeController::financial` فراخوانی شده تا وضعیت مالی دقیق (فروش، جریمه، پرداختی و مانده) محاسبه شود.

</div>---

  </div>### Response Structure

```json
{
    "status": true,
    "time": 1702123456,
    "data": [
        {
            "method": "flight",       // نوع محصول (flight, train, hotel, ...)
            "submethod": false,       // زیر مجموعه (در صورت وجود)
            "title": "پرواز تهران به مشهد - ماهان", // عنوان فارسی (از کش)
            "details": {
                // تمام فیلدهای سفارش (id, created_at, status, ...)
                "id": 10250,
                "status": 1,
                "created_at": "2024-01-01 12:00:00",
                "financial": {
                    "sum_sale": 15000000,         // مبلغ کل فروش
                    "sum_return_sale": 0,         // مبلغ استرداد شده به مشتری
                    "sum_penalty_sale": 0,        // جریمه کنسلی کسر شده
                    "sum_receive": 15000000,      // مبلغ دریافت شده از مشتری
                    "balance_received": 0         // مانده حساب (بدهی/بستانکاری)
                }
            }
        },
        {
            "method": "hotel",
            "submethod": false,
            "title": 10249, // حالت Fallback: اگر عنوان در کش نباشد، ID برمی‌گردد
            "details": false // در صورت نبودن در کش، جزئیات مالی محاسبه نمی‌شود
        }
    ]
}
```

### Financial Fields Description

<div class="api-docs" id="bkmrk-field-description-su"><table class="schema-table" dir="rtl"><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">sum\_sale</td><td>مبلغ نهایی فاکتور (قیمت محصول).</td></tr><tr><td dir="ltr">sum\_receive</td><td>مجموع مبالغی که کاربر تاکنون برای این سفارش پرداخت کرده است.</td></tr><tr><td dir="ltr">sum\_return\_sale</td><td>مبلغی که بابت استرداد (کنسلی) باید به کاربر برگشت داده شود (یا شده است).</td></tr><tr><td dir="ltr">sum\_penalty\_sale</td><td>مبلغ جریمه کنسلی.</td></tr><tr><td dir="ltr">balance\_received</td><td>تراز مالی سفارش (اگر 0 باشد یعنی تسویه شده، مثبت یا منفی نشان‌دهنده بدهی یا بستانکاری است).</td></tr></tbody></table>

</div>

# POST /b2c/v1/trade/store

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Store Trade / Payment Initiation

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

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Submit Order &amp; Pay

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Ftrade%2Fs"><div class="endpoint-info"><div>**URL:** `/b2c/v1/trade/store`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1TradeController@storeTradeBeforePay</div><div>**Middleware:** `authWithJwt` (Required)</div></div>  </div>### Request Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>Yes</td><td>شناسه شعبه (Branch ID).</td></tr><tr><td dir="ltr">group</td><td>String</td><td>Yes</td><td>نوع گروه کاربری: `b2c` (مسافر) یا `colleague` (همکار/B2B).</td></tr><tr><td dir="ltr">requests\[pay\]</td><td>Object</td><td>Yes</td><td>آبجکت تنظیمات پرداخت (جزئیات در جدول زیر).</td></tr><tr><td dir="ltr">...product\_data</td><td>Mixed</td><td>Yes</td><td>سایر اطلاعات مربوط به محصول (لیست مسافران، شناسه پرواز/هتل و...) که برای رزرو ضروری است.   
<span style="font-size: 0.9em; color: gray;">(این اطلاعات مستقیماً به سرویس رزرو ارسال می‌شود).</span></td></tr></tbody></table>

</div>#### ساختار `requests\[pay\]`

<div class="api-docs" id="bkmrk-key-type-description"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">amount</td><td>Integer</td><td>مبلغ قابل پرداخت (ریال).</td></tr><tr><td dir="ltr">drive</td><td>String</td><td>روش پرداخت: - `wallet` : پرداخت از اعتبار حساب.
- `online` (یا هر مقدار دیگر): پرداخت از درگاه بانکی.

</td></tr><tr><td dir="ltr">return</td><td>String</td><td>لینک بازگشت (Callback URL) پس از پرداخت موفق در درگاه بانکی.</td></tr></tbody></table>

---

  </div>### Business Logic Scenarios

#### ۱. سناریوی تغییر قیمت/وضعیت (Price/Status Changed)

قبل از هر اقدامی، متد `statusItemProgress` اجرا می‌شود. اگر قیمت یا ظرفیت محصول تغییر کرده باشد:

<div class="api-docs" id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D9%85%D8%AA%D9%88%D9%82%D9%81-%D9%85%DB%8C%E2%80%8C%D8%B4%D9%88%D8%AF."><div class="logic-box" dir="rtl">- عملیات متوقف می‌شود.
- کد وضعیت **201** برمی‌گردد.
- در پاسخ، `status_data['changed'] = true` خواهد بود. کلاینت باید قیمت جدید را به کاربر نمایش دهد.

</div></div>#### ۲. پرداخت با کیف پول (Wallet Payment)

اگر `drive = 'wallet'` باشد:

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%85%D9%88%D8%AC%D9%88%D8%AF%DB%8C%3A-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-"><div class="logic-box" dir="rtl">- **بررسی موجودی:** سیستم فرمول زیر را چک می‌کند:  
    `(Wallet Credit - Wallet Debit) + Ceiling Amount >= Pay Amount`
- **خطای موجودی:** اگر موجودی کافی نباشد، خطای **402** بازگردانده می‌شود.
- **موفقیت:** مبلغ از حساب کسر شده، رکورد در جدول `wallet` ثبت می‌شود و خرید نهایی می‌شود (Reference ID تولید می‌شود).

</div></div>#### ۳. پرداخت آنلاین (Online Gateway)

اگر `drive != 'wallet'` باشد:

<div class="api-docs" id="bkmrk-%D9%85%D8%AA%D8%AF-paymentinvoiceop"><div class="logic-box" dir="rtl">- متد `paymentInvoiceOperation` اجرا می‌شود.
- یک رکورد در جدول `payment_gateway` با یک Slug یکتا ایجاد می‌شود.
- لینک پرداخت به فرمت `https://{short_domain}/p/{slug}` تولید و برگردانده می‌شود.
- یک رزرو موقت (Temporary Reservation) ایجاد می‌شود.

</div>---

  </div>### Response Examples

#### حالت اول: تولید موفق لینک پرداخت (Online)

<div class="api-docs" id="bkmrk-status%3A-201-created"><div class="endpoint-info" style="background-color: #dff0d8; border-color: #d6e9c6; color: #3c763d;">Status: 201 Created</div></div>```json
{
    "payload": {
        "payment_link": "https://airplus.app/p/a1b2c3d4",
        "temporary_id": 12345, // شناسه رزرو موقت
        "status_data": {
            "changed": false // یعنی قیمت تغییر نکرده است
        }
    },
    "meta": {
        "timestamp": 1702123456
    }
}
```

#### حالت دوم: خرید موفق با کیف پول (Wallet)

<div class="api-docs" id="bkmrk-status%3A-201-created-1"><div class="endpoint-info" style="background-color: #dff0d8; border-color: #d6e9c6; color: #3c763d;">Status: 201 Created</div></div>```json
{
    "payload": {
        "payment_id": 9876,
        "reference_id": 500120 // شماره فاکتور نهایی (رزرو قطعی)
    },
    "meta": {
        "timestamp": 1702123456
    }
}
```

#### حالت سوم: تغییر قیمت (نیاز به تایید کاربر)

<div class="api-docs" id="bkmrk-status%3A-201-created-2"><div class="endpoint-info" style="background-color: #fcf8e3; border-color: #faebcc; color: #8a6d3b;">Status: 201 Created</div></div>```json
{
    "payload": {
        "payment_link": null,
        "temporary_id": false,
        "status_data": {
            "changed": true,
            "message": "قیمت پرواز تغییر کرده است...",
            "new_price": 15500000
        }
    },
    "meta": {
        "timestamp": 1702123456
    }
}
```

#### حالت چهارم: خطای موجودی ناکافی

<div class="api-docs" id="bkmrk-status%3A-402-payment-"><div class="endpoint-info" style="background-color: #f2dede; border-color: #ebccd1; color: #a94442;">Status: 402 Payment Required</div></div>```json
{
    "error": {
        "code": 1001,
        "message": "اعتبار شما برای این خرید کافی نمی باشد. لطفا قبل از انجام این عملیات موجودی حساب خود را افزایش دهید."
    },
    "meta": {
        "timestamp": 1702123456
    }
}
```

# POST /b2c/v1/trade/completion

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Trade Completion &amp; Issuance

این اندپوینت برای **تکمیل فرآیند خرید** استفاده می‌شود.   
زمانی که پرداخت (آنلاین یا کیف پول) تایید شد، این متد برای صدور فاکتور (Factor)، بروزرسانی اطلاعات مسافران، ثبت تراکنش‌های مالی (Pledgers/Pays) و ارسال SMS نهایی به کاربر فراخوانی می‌گردد.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Finalize Order

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Ftrade%2Fc"><div class="endpoint-info"><div>**URL:** `/b2c/v1/trade/completion`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1TradeController@storeTradeAfterPay</div><div>**Middleware:** `authWithJwt` (Required)</div><div>**Transaction:** <span style="color: red;">Atomic (DB::transaction)</span></div></div>  </div>### Request Body Parameters

این متد یک آبجکت JSON پیچیده شامل اطلاعات پرداخت و جزئیات درخواست را دریافت می‌کند.

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>Yes</td><td>شناسه شعبه (Branch ID) برای تعیین تنظیمات مالی و اعتباری.</td></tr><tr><td dir="ltr">payment</td><td>Object</td><td>Yes</td><td>اطلاعات مربوط به پرداخت انجام شده (مبلغ، درگاه و...).</td></tr><tr><td dir="ltr">request</td><td>Object</td><td>Yes</td><td>شامل جزئیات اصلی سفارش: - `passengers`: لیست کامل مسافران برای آپدیت در DB.
- `data`: لیست آیتم‌های خریداری شده (پرواز، هتل، قطار).
- `pledgers`: (اختیاری) لیست متعهدان مالی (برای B2B).
- `notices`: (بولین) آیا SMS ارسال شود؟
- `income_id`: شناسه درآمد.

</td></tr></tbody></table>

---

  </div>### Step-by-Step Logic Breakdown

#### ۱. ایجاد فاکتور (Factor Creation)

یک رکورد در جدول `factors` ایجاد می‌شود:

<div class="api-docs" id="bkmrk-serial%3A-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-%D8%B3%D8%B1%DB%8C%D8%A7%D9%84-"><div class="logic-box" dir="rtl">- **Serial:** تولید سریال یکتا بر اساس شعبه.
- **Slug:** شناسه یکتای 8 کاراکتری برای پیگیری (Reference ID).
- **Status:** وضعیت پیش‌فرض `3` (صادر شده/موفق) تنظیم می‌شود.
- **Colleague Auth:** اگر کاربر B2B باشد، شناسه همکار ثبت می‌شود.

</div></div>#### ۲. مدیریت مسافران (Passengers Upsert)

سیستم روی آرایه `request['passengers']` حلقه می‌زند:

<div class="api-docs" id="bkmrk-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D9%87%D9%88%DB%8C%D8%AA%DB%8C-%28%D9%86%D8%A7%D9%85%D8%8C-"><div class="logic-box" dir="rtl">- اطلاعات هویتی (نام، نام خانوادگی، کد ملی، پاسپورت، تاریخ انقضا و...) در جدول `customers` **بروزرسانی (Update)** می‌شوند.
- تاریخ تولد و انقضای پاسپورت استانداردسازی می‌شود (حذف کاراکترهای اضافی).
- رابطه (Relationship) بین مسافر اصلی و همراهان تنظیم می‌شود.

</div></div>#### ۳. مدیریت مالی و تعهدات (Pledgers &amp; Pays)

این بخش مشخص می‌کند "چه کسی پول را می‌دهد":

<div class="api-docs" id="bkmrk-%D8%AD%D8%A7%D9%84%D8%AA-b2b-%28%D9%87%D9%85%DA%A9%D8%A7%D8%B1%29%3A-%D8%A7%DA%AF"><div class="logic-box" dir="rtl">- **حالت B2B (همکار):** اگر `pledgers` ارسال شده باشد، رکوردهایی در جدول `pledgers` و `pays` (نوع contract) ثبت می‌شود تا بدهی همکار مشخص شود.
- **حالت B2C (عادی):** اگر متعهدی نباشد، خودِ اپراتور/کاربر جاری به عنوان متعهد (type='operator') در نظر گرفته می‌شود.

</div></div>#### ۴. پردازش آیتم‌ها (Items Processing)

بر اساس نوع سرویس (`aircraft`, `train`, `accommodation`):

<div class="api-docs" id="bkmrk-aircraft%3A-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D8%A8%D9%84%DB%8C%D8%B7"><div class="logic-box" dir="rtl">- **Aircraft:** وضعیت بلیط بررسی شده، سن مسافر (برای تشخیص Infant) محاسبه می‌شود و آیتم در `factor_items` درج می‌شود.
- **Hub Reservation:** اگر سرویس از نوع `airplusHub` باشد، درخواست به جدول `hub_reservation` اضافه شده و مبلغ خرید به عنوان **Debit** (بدهی) در جدول `wallet` شعبه ثبت می‌شود.
- **Train/Hotel:** مشابه پرواز، اطلاعات رزرو پردازش و ذخیره می‌شوند.
- **Temporary Update:** وضعیت رزرو موقت (در جدول `temporary_reservations`) به تکمیل شده تغییر می‌کند.

</div></div>#### ۵. ارسال اعلان (Notification)

اگر `notices: true` باشد:

<div class="api-docs" id="bkmrk-%DB%8C%DA%A9-%D9%85%D8%AA%D9%86-%D9%BE%DB%8C%D8%A7%D9%85%DA%A9-%28magic-"><div class="logic-box" dir="rtl">- یک متن پیامک (Magic Text) به صورت تصادفی انتخاب می‌شود (مثلاً: "سفری هیجان انگیز").
- لینک فاکتور (`/f/{slug}`) تولید می‌شود.
- جاب `SendNotification` به صف `fastJob` ارسال می‌شود.
- لاگ سیستمی در `snailJob` ثبت می‌گردد.

</div>---

  </div>### Response Examples

#### پاسخ موفق (Success - 200 OK)

```json
{
    "status": true,
    "time": 1702123456,
    "payment": {
        "amount": 15000000,
        "method": "wallet"
    },
    "reference_id": "8XKA9L2P", // شناسه فاکتور (Factor Slug)
    "details": false // یا آرایه‌ای از خطاها در صورت وجود مشکل جزئی
}
```

#### پاسخ موفق همراه با هشدار (Success with Warnings)

اگر خرید کلی انجام شود اما برخی آیتم‌ها (مثلاً یکی از پروازها) رزرو نشوند:

```json
{
    "status": true,
    "time": 1702123456,
    "reference_id": "8XKA9L2P",
    "details": [
        {
            "local_id": 101,
            "message": "ظرفیت این کلاس پروازی تکمیل شده است."
        }
    ]
}
```

#### پاسخ خطا (Failure)

```json
{
    "status": false,
    "code": "1004-500",
    "message": "1004-500 : خطای داخلی سرور در هنگام ثبت رکورد",
    "trace": "..."
}
```

# POST /b2c/v1/online/search/{type}

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Search Flights &amp; Trains

این اندپوینت برای جستجوی بلیط‌های سفر استفاده می‌شود.   
سیستم به صورت هوشمند بر اساس نوع درخواست (قطار یا پرواز)، ورودی‌ها را اعتبارسنجی کرده، اطلاعات تکمیلی مبدا/مقصد را از دیتابیس یا کش Redis استخراج می‌کند و نتایج جستجو را از سرویس‌های تجمیع‌کننده (Aggregators) بازمی‌گرداند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Route Search

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/search/{type}`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1OnlineController@routeSearch</div><div>**Auth:** Optional (Handled manually via Pipeline)</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">{type}</td><td>String</td><td>نوع وسیله نقلیه. مقادیر مجاز: `train` یا `aircraft` (یا هر مقدار غیر train برای پرواز).</td></tr></tbody></table>

  </div>### Request Body Parameters

#### پارامترهای مشترک (Common)

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">branch</td><td>Integer</td><td>Yes</td><td>شناسه شعبه.</td></tr><tr><td dir="ltr">group</td><td>String</td><td>No</td><td>گروه کاربری (`b2c`, `b2b`, `b2e`, `colleague`). پیش‌فرض: `b2c`.</td></tr><tr><td dir="ltr">level</td><td>Integer</td><td>No</td><td>سطح دسترسی کاربر (پیش‌فرض: 1).</td></tr></tbody></table>

</div>#### اگر `type = 'train'` باشد:

<div class="api-docs" id="bkmrk-parameter-type-requi-1"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">origin</td><td>Integer</td><td>Yes</td><td>**شناسه شهر** مبدا (City ID) از جدول `cities`.</td></tr><tr><td dir="ltr">destination</td><td>Integer</td><td>Yes</td><td>**شناسه شهر** مقصد (City ID) از جدول `cities`.</td></tr><tr><td dir="ltr">departure</td><td>Datetime</td><td>Yes</td><td>تاریخ رفت (فرمت استاندارد).</td></tr><tr><td dir="ltr">returning</td><td>Datetime</td><td>No</td><td>تاریخ برگشت. باید بعد از تاریخ رفت باشد.</td></tr></tbody></table>

</div>#### اگر `type != 'train'` (پرواز) باشد:

<div class="api-docs" id="bkmrk-parameter-type-requi-2"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">origin</td><td>String</td><td>Yes</td><td>**کد IATA** فرودگاه مبدا (مانند `MHD`, `THR`).</td></tr><tr><td dir="ltr">destination</td><td>String</td><td>Yes</td><td>**کد IATA** فرودگاه مقصد.</td></tr><tr><td dir="ltr">departure\_date</td><td>Date</td><td>Yes</td><td>تاریخ رفت. <span style="color: red; font-size: 0.8em;">(توجه: نام پارامتر با قطار متفاوت است)</span>.</td></tr><tr><td dir="ltr">returning\_date</td><td>Date</td><td>No</td><td>تاریخ برگشت.</td></tr><tr><td dir="ltr">only\_charters</td><td>Boolean</td><td>No</td><td>فیلتر فقط پروازهای چارتر.</td></tr></tbody></table>

---

  </div>### Business Logic Details

#### ۱. احراز هویت اختیاری (Optional Pipeline Auth)

حتی اگر روت عمومی باشد، کد چک می‌کند اگر هدر `Authorization` وجود داشته باشد، درخواست را از پایپ‌لاین `AuthWithJWT` عبور می‌دهد. این کار برای اعمال تخفیف‌های خاص یا دسترسی‌های سطح کاربر (Level) انجام می‌شود.

#### ۲. اعتبارسنجی قطار (Train Validation)

برای جستجوی قطار اعتبارسنجی سخت‌گیرانه‌تری روی تاریخ‌ها انجام می‌شود:

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-%D9%81%D8%B1%D9%85%D8%AA-departure-%D8%A7"><div class="logic-box" dir="rtl">- اگر فرمت `departure` اشتباه باشد -&gt; **خطای 409** (کد 1000).
- اگر تاریخ برگشت قبل از رفت باشد -&gt; **خطای 409**.

</div></div>#### ۳. غنی‌سازی اطلاعات مبدا/مقصد (Metadata Enrichment)

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

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%BE%D8%B1%D9%88%D8%A7%D8%B2-%28string-i"><div class="logic-box" dir="rtl">- **برای پرواز (String input):** اطلاعات فرودگاه (IATA, Title, Country, State, City) را از جدول `airports` می‌گیرد. برای سرعت بیشتر، این اطلاعات در **Redis** با کلید `airports:{IATA}` کش می‌شوند.
- **برای قطار (Numeric input):** اطلاعات شهر (نام فارسی/انگلیسی، استان) را از جدول `cities` جوین شده با `states` دریافت می‌کند.

</div></div>#### ۴. فراخوانی سرویس پایه (Base Service)

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%82%D8%B7%D8%A7%D8%B1-%D9%85%D8%AA%D8%AF-basese"><div class="logic-box" dir="rtl">- برای قطار متد `BaseService::combineRoute` فراخوانی می‌شود.
- برای پرواز متد `LibBaseService::combineFlight` فراخوانی می‌شود.

</div>---

  </div>### Response Examples

#### پاسخ جستجوی قطار (Train Response)

ساختار پاسخ قطار شامل `payload` و `meta` است.

```json
{
    "payload": [
        // لیست قطارهای یافت شده از سرویس
        {
            "id": 105,
            "price": 2500000,
            "provider": "raja",
            "capacity": 4
        }
    ],
    "meta": {
        "search": {
            "origin": {
                "id": 37,
                "title": {"fa": "اصفهان", "en": "Isfahan"},
                "category": {"id": 5, "title": {"fa": "اصفهان", "en": "Isfahan"}}
            },
            "destination": {
                "id": 396,
                "title": {"fa": "مشهد", "en": "Mashhad"},
                "category": {"id": 10, "title": {"fa": "خراسان رضوی", "en": "Razavi Khorasan"}}
            },
            "departure": "2025-12-10",
            "returning": false
        },
        "timestamp": 1702123456
    }
}
```

#### پاسخ جستجوی پرواز (Flight Response)

ساختار پاسخ پرواز کمی متفاوت است و مستقیماً آرایه برمی‌گرداند.

```json
{
    "search": {
        "origin": {
            "id": 1,
            "iata": "MHD",
            "title": "فرودگاه هاشمی نژاد",
            "country": { "id": 100, "title_fa": "ایران" },
            "city": { "id": 396, "title_fa": "مشهد" }
        },
        "destination": {
            "id": 2,
            "iata": "THR",
            "title": "فرودگاه مهرآباد",
            "city": { "id": 1, "title_fa": "تهران" }
        },
        "departure_date": "2025-12-10",
        "returning_date": null
    },
    "data": [
        // لیست پروازهای یافت شده
        {
            "flight_id": "WS-123",
            "airline": "Mahan",
            "price": 15000000
        }
    ]
}
```

#### خطای اعتبارسنجی تاریخ (Train Error)

<div class="api-docs" id="bkmrk-status%3A-409-conflict"><div class="endpoint-info" style="background-color: #f2dede; border-color: #ebccd1; color: #a94442;">Status: 409 Conflict</div></div>```json
{
    "error": {
        "code": 1000,
        "message": "تاریخ شروع و پایان جستجو معتبر نمی باشد"
    }
}
```

# POST /b2c/v1/online/{type}/lock

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Lock Item (Pre-Booking)

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

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Lock Selection

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/{type}/lock`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1OnlineController@lockItemProgress</div><div>**Auth:** <span style="color: red;">Required (JWT)</span></div><div>**Middleware:** `authWithJwt`</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">{type}</td><td>String</td><td>نوع سرویس (مثلاً `train`, `aircraft`, `hotel`). اگرچه در بدنه کنترلر مستقیماً استفاده نشده، اما برای تفکیک منطقی روت‌ها کاربرد دارد.</td></tr></tbody></table>

  </div>### Request Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">data</td><td>Object/Array</td><td>Yes</td><td>آبجکت کامل آیتم انتخاب شده که از خروجی جستجو (Search) دریافت شده است. این داده عیناً برای اعتبارسنجی به سرویس ارسال می‌شود.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>No\*</td><td>شناسه شعبه. (معمولاً توسط میدل‌ور `authWithJwt` به درخواست تزریق می‌شود، اما اگر کلاینت ارسال کند اولویت دارد).</td></tr></tbody></table>

---

  </div>### Business Logic Details

#### ۱. احراز هویت و زمینه (Auth Context)

این متد توسط میدل‌ور `authWithJwt` محافظت می‌شود. این میدل‌ور اطلاعات کاربر (`operator`) و شعبه (`branch`) را به آبجکت `$request` تزریق می‌کند.

#### ۲. فراخوانی سرویس پایه (BaseService Call)

متد `BaseService::lockItemProgress` با پارامترهای زیر فراخوانی می‌شود:

<div class="api-docs" id="bkmrk-%24request-%3Edata%3A-%D8%A7%D8%B7%D9%84%D8%A7"><div class="logic-box" dir="rtl">- `$request->data`: اطلاعات آیتم انتخابی.
- `false`: پارامتر دوم (احتمالاً مربوط به force lock یا تنظیمات خاص که اینجا غیرفعال است).
- `$request->get('branch')`: زمینه شعبه برای قیمت‌گذاری.
- `1`: شناسه سیستمی یا نوع عامل (Hardcoded).

</div></div>#### ۳. ثبت در رزروهای موقت (Temporary Reservation)

نتیجه‌ی عملیات به همراه اطلاعات ورودی در جدول **`temporary_reservations`** ذخیره می‌شود:

<div class="api-docs" id="bkmrk-operator%3A-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%DA%A9%D8%A7%D8%B1%D8%A8"><div class="logic-box" dir="rtl">- **operator:** شناسه کاربر جاری (`$operator->id`).
- **data:** JSON ورودی (آنچه کاربر انتخاب کرده).
- **result:** JSON خروجی از سرویس (تاییدیه قفل و قیمت نهایی).

</div></div>شناسه این رکورد (`LockId`) تولید شده و در پاسخ بازگردانده می‌شود. این شناسه کلید اصلی برای مرحله بعد (ثبت مسافر و پرداخت) است.

#### ۴. مدیریت خطا (Exception Handling)

اگر سرویس خارجی خطا دهد یا آیتم موجود نباشد (Exception)، سیستم:

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-400-%28bad-re"><div class="logic-box" dir="rtl">- کد وضعیت **400 (Bad Request)** برمی‌گرداند.
- پیام خطا و Trace سیستم را در پاسخ قرار می‌دهد.
- اطلاعات تماس پشتیبانی (تلفن 021-91016838 و پنل Helpdesk) را به صورت خودکار به پاسخ اضافه می‌کند تا کلاینت بتواند به کاربر نمایش دهد.

</div>---

  </div>### Response Examples

#### مثال درخواست (Request)

```json
{
    "data": {
        "flight_id": "WS-5050",
        "provider_key": "some_encrypted_key_from_search_result",
        "price": 1500000,
        "class": "Economy"
    }
}
```

#### پاسخ موفق (Success - 200 OK)

```json
{
    "status": true,
    "time": 1702123456,
    "lock_id": 9852,
    "data": {
        "status": "confirmed",
        "new_price": 1500000,
        "token": "reservation_token_12345",
        "expiration": "20 mins"
    }
}
```

**نکته:** مقدار `lock_id` (در این مثال 9852) باید توسط کلاینت ذخیره شده و در اندپوینت خرید (`trade/store`) ارسال شود.

#### پاسخ خطا (Error - 400 Bad Request)

```json
{
    "status": false,
    "time": 1702123499,
    "error": {
        "code": 1002,
        "message": "ظرفیت پرواز تکمیل شده است یا قیمت تغییر کرده است.",
        "trace": [ ... ]
    },
    "support": {
        "phone": "021-91016838 in 121",
        "email": "ict@airplus.app",
        "panel": "helpdesk.airplus.app"
    }
}
```

# POST /b2c/v1/online/{type}/penalty

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Refund Penalty Calculation

این اندپوینت برای محاسبه جریمه استرداد رزرو یا بلیط آنلاین طراحی شده است.   
سیستم با توجه به نوع سرویس (`{type}`) و شعبه ارسال‌کننده، میزان جریمه، مبلغ قابل استرداد و قوانین مربوط به لغو رزرو را محاسبه می‌کند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>  
---

  </div># Calculate Refund Penalty

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/{type}/penalty`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** V1OnlineController@penaltyItemProgress</div><div>**Auth:** Public (No Authentication Required)</div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">{type}</td><td>String</td><td>نوع سرویس برای محاسبه جریمه؛ یکی از مقادیر: `aircraft`, `train`, `accommodation`.</td></tr></tbody></table>

  </div>### Request Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">data</td><td>Object / Array</td><td>✅</td><td>داده‌های مربوط به آیتم رزرو یا بلیط؛ شامل اطلاعات تاریخی، شماره بلیط، ارائه‌دهنده و اطلاعات سفر.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>✅</td><td>شناسه شعبه ارسال‌کننده درخواست برای محاسبه قوانین جریمه محلی.</td></tr></tbody></table>

---

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-item"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">items</td><td>خروجی اصلی محاسبه جریمه از کلاس `BaseService::getRefundPenalty`. شامل جزئیات هر آیتم است.</td></tr><tr><td dir="ltr">meta.timestamp</td><td>زمان پاسخ (Unix Timestamp).</td></tr></tbody></table>

</div>### Example Response - Success

```json
{
    "items": [
      {
        "provider": "Mahan Air",
        "ticket_number": "WS5050",
        "original_price": 15000000,
        "penalty_amount": 3000000,
        "refundable_amount": 12000000,
        "currency": "IRR",
        "rule": "لغو تا ۳ ساعت مانده به پرواز با جریمه ۲۰٪"
      }
    ],
    "meta": {
      "timestamp": 1733739501
    }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Error Response (400 Bad Request)

```json
{
    "status": false,
    "time": 1733739501,
    "error": {
      "code": 1002,
      "message": "Invalid booking data format or provider error.",
      "trace": [ ... ]
    },
    "support": {
      "phone": "021-91016838 in 121",
      "email": "ict@airplus.app",
      "panel": "helpdesk.airplus.app"
    }
}
```

<div class="api-docs" id="bkmrk--3">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request-body%3A-data-%2B"><div class="flowchart" dir="rtl"><div class="flow-item">Request Body: `data` + `branch`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Initialize BaseService**  
ساخت نمونه: `$BaseService = new \App\Lib\BaseService()`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Call getRefundPenalty()**  
`$BaseService->getRefundPenalty($request->data, $request->get('branch'))`</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Return JSON Result**  
شامل اطلاعات جریمه و مبلغ قابل بازگشت برای هر آیتم</div><div class="flow-arrow">↓</div><div class="flow-item-decision" style="background: #ffe6e6;">در صورت بروز Exception → ساخت پاسخ خطا با اطلاعات تماس پشتیبانی</div></div></div>

# POST /b2c/v1/online/{type}/refund

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Refund Request Operation

این اندپوینت جهت ثبت و پردازش عملیات **درخواست استرداد (Refund)** برای بلیط یا رزرو آنلاین ایجاد شده است. پس از ارسال اطلاعات رزرو، توضیحات کاربر و مشخصات شعبه، فرآیند استرداد از طریق سرویس اصلی `BaseService::refundItemProgress` اجرا می‌شود. پاسخ شامل اطلاعات تراکنش استرداد و وضعیت نهایی عملیات خواهد بود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>---

  </div># Refund Item Progress

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/{type}/refund`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** `V1OnlineController@refundItemProgress`</div><div>**Auth:** <span class="method-public">Public (No Authentication Required)</span></div></div></div>### Path Parameters

<div class="api-docs" id="bkmrk-parameter-type-descr"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">{type}</td><td>String</td><td>نوع سرویس آنلاین که قرار است فرایند استرداد برای آن انجام شود — یکی از `aircraft`، `train` یا `accommodation`.</td></tr></tbody></table>

---

  </div>### Request Body Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">data</td><td>Object / Array</td><td>✅</td><td>اطلاعات رزرو، شامل شماره بلیط، شناسه رزرو، مشخصات سرویس و داده‌های ارائه‌دهنده.</td></tr><tr><td dir="ltr">description</td><td>String</td><td>☑️</td><td>توضیحات متنی برای علت درخواست استرداد؛ توسط کاربر یا شعبه ارائه می‌شود.</td></tr><tr><td dir="ltr">branch</td><td>Integer</td><td>✅</td><td>شناسه شعبه‌ی درخواست‌دهنده که از قوانین داخلی برای محاسبه استرداد استفاده می‌کند.</td></tr></tbody></table>

---

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-item"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td dir="ltr">items</td><td>نتایج بازگشتی از تابع `BaseService::refundItemProgress` شامل جزئیات استرداد، کد رهگیری و وضعیت مالی.</td></tr><tr><td dir="ltr">meta.timestamp</td><td>زمان لحظه ثبت پاسخ (Unix Timestamp).</td></tr></tbody></table>

</div>### Example Response — Success

```json
{
  "items": {
    "reference": "RFD-83234",
    "status": "accepted",
    "refund_amount": 1200000,
    "currency": "IRR",
    "provider": "Mahan Airlines",
    "description": "لغو پرواز توسط مسافر",
    "processed_at": "2025-12-09T17:40:22Z"
  },
  "meta": {
    "timestamp": 1733739622
  }
}
```

### Example Response — Error (400)

```json
{
  "status": false,
  "time": 1733739622,
  "error": {
    "code": 1002,
    "message": "Refund process failed: invalid ticket reference",
    "trace": [ ... ]
  },
  "support": {
    "phone": "021-91016838 in 121",
    "email": "ict@airplus.app",
    "panel": "helpdesk.airplus.app"
  }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Technical Logic

<div class="api-docs" id="bkmrk-request%3A-includes-da"><div class="flowchart" dir="rtl"><div class="flow-item">Request: includes `data`, `description`, and `branch`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Initialize BaseService**  
`$BaseService = new \App\Lib\BaseService()`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Call refundItemProgress()**  
`$BaseService->refundItemProgress($request->data, $request->description, $request->get('branch'))`</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Return JSON Result**  
شامل داده‌های استرداد، مبلغ بازگشتی و وضعیت نهایی.</div><div class="flow-arrow">↓</div><div class="flow-item-decision" style="background: #ffe6e6;">در صورت خطا → ارسال پاسخ خطایی حاوی کد `1002` و اطلاعات تماس پشتیبانی.</div></div></div>

# GET /b2c/v1/online/accommodation/list

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div></div>## List Accommodation Items

این اندپوینت فهرستی از اقامتگاه‌ها (هتل‌ها) را بر اساس شهر، IATA، یا شناسه‌ی مستقیم هتل بازیابی می‌کند. امکان اعمال فیلترهای متنوع شامل ستاره، نوع، نام، سرویس‌دهنده‌ها و محدوده‌ی تاریخی رزرو وجود دارد. در صورت انتخاب **only\_charters=true**، نتایج محدود به هتل‌های دارای چارتر فعال در بازه‌ی داده‌شده خواهند بود.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/accommodation/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@listAccommodation`</div><div>**Service Layer:** `LibBaseService::listAccommodation()`</div><div>**Auth:** Public Access (No JWT Required)</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td>✅</td><td>نوع جستجو: یکی از مقادیر `city`، `iata` یا `accommodation`.</td></tr><tr><td>value</td><td>string|integer</td><td>✅</td><td>مقدار مرتبط با نوع؛ مثلاً نام شهر، کد IATA، یا شناسه هتل.</td></tr><tr><td>checkin\_date</td><td>date</td><td>✅</td><td>تاریخ ورود.</td></tr><tr><td>checkout\_date</td><td>date</td><td>✅</td><td>تاریخ خروج.</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه‌ی شعبه برای چک چارترهای فعال.</td></tr><tr><td>filters\[only\_charters\]</td><td>boolean</td><td>❌</td><td>اگر true باشد فقط هتل‌هایی که چارتر فعال دارند بازگردانده می‌شوند.</td></tr><tr><td>filters\[stars\]\[\]</td><td>array</td><td>❌</td><td>لیست تعداد ستاره‌های مد نظر (مثلاً \[3,4,5\]).</td></tr><tr><td>filters\[types\]\[\]</td><td>array</td><td>❌</td><td>انواع اقامتگاه شامل `hotel`، `apartment`، `guesthouse` و ...</td></tr><tr><td>filters\[name\]</td><td>string</td><td>❌</td><td>جستجو بر اساس بخشی از نام فارسی هتل.</td></tr><tr><td>filters\[services\]\[\]</td><td>array</td><td>❌</td><td>لیست سرویس‌دهنده‌ها (مثلاً `snapptrip`، `sepehr`، `tport`، `airplus`).</td></tr><tr><td>paginate\[length\]</td><td>integer</td><td>❌</td><td>تعداد رکورد در هر صفحه (پیش‌فرض 30).</td></tr><tr><td>paginate\[start\]</td><td>integer</td><td>❌</td><td>شماره شروع ایندکس صفحه (پیش‌فرض 0).</td></tr></tbody></table>

---

</div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-stat"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td>Status</td><td>وضعیت پاسخ (true در صورت موفقیت).</td></tr><tr><td>Time</td><td>Timestamp زمان پاسخ.</td></tr><tr><td>Search</td><td>اطلاعات شناسایی‌شده برای نوع جستجو (شهر، کشور، یا هتل).</td></tr><tr><td>Data</td><td>لیست اقامتگاه‌ها در قالب `AccommodationListResource`.</td></tr><tr><td>Table</td><td>اطلاعات مربوط به صفحه‌بندی (total, per\_page, current\_page, last\_page, از، تا، next\_page).</td></tr></tbody></table>

</div>### Example Response (Success)

```json
{
  "Status": true,
  "Time": 1733742000,
  "Search": {
    "city": {
      "id": 396,
      "title": {"fa": "مشهد", "en": "Mashhad"},
      "country": {"fa": "ایران", "en": "Iran"},
      "category": {"title": "شهر"}
    }
  },
  "Data": [
    {
      "id": 2652,
      "fa_title": "اکسلسیور کیش",
      "en_title": "Excelsior Kish Hotel",
      "rate": 5,
      "type": "hotel",
      "city": "مشهد",
      "priority": 0,
      "mapping": {"airplus": true, "snapptrip": true}
    }
  ],
  "Table": {
    "total": 250,
    "per_page": 30,
    "current_page": 1,
    "last_page": 9,
    "from": 1,
    "to": 30,
    "next_page": 2
  }
}
```

### Example Response (Error)

```json
{
  "status": false,
  "time": 1733742053,
  "error": {
    "code": 1002,
    "message": "Invalid city or date format.",
    "trace": [ ... ]
  },
  "support": {
    "phone": "021-91016838 in 121",
    "email": "ict@airplus.app",
    "panel": "helpdesk.airplus.app"
  }
}
```

<div class="api-docs" id="bkmrk--2">---

</div>### Technical Flow (Logic Overview)

<div class="api-docs" id="bkmrk-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%B4%D8%A7%D9%85%D9%84-t"><div class="flowchart" dir="rtl"><div class="flow-item">ورودی درخواست شامل type، value، filters و paginate</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Validate Pagination**  
اگر پارامتر paginate وجود نداشته یا کامل نیست، مقدار پیش‌فرض (length=30, start=0) ست می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Determine Search Mode**  
بسته به `type`، داده از جدول‌های cities، states، countries یا hotels واکشی می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Build Filter Query**  
روی جدول `hotels` با join بر روی `mapping_accommodations` انجام می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Filter Logic**  
- ستاره‌ها (rate)  
- نوع اقامتگاه (type)  
- نام جزئی (fa_title LIKE '%name%')  
- وجود چارتر فعال بین تاریخ‌های واردشده.</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Paginate &amp; Return**  
پاسخ شامل Search (محل جغرافیایی) + Data (Resource List) + Table meta.</div></div></div>

# GET /b2c/v1/online/accommodation/get_min_prices

<div class="api-docs" id="bkmrk-">  <div class="endpoint-section">  
</div></div>## Get Accommodation Minimum Prices

این اندپوینت حداقل قیمت (Minimum Price) اقامتگاه‌ها را در بازه‌ای از تاریخ ارائه می‌دهد. داده‌ها از چند سرویس خارجی شامل `AirPlus`، `Sepehr`، `Tport` و `SnappTrip` واکشی می‌شوند. نتایج هر اقامتگاه به‌صورت cache شده در Redis با TTL دینامیک نگهداری می‌شود تا بار API کاهش پیدا کند.

<div class="api-docs" id="bkmrk--1"><div class="endpoint-section">  
</div>---

  </div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/accommodation/get_min_prices`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@getAccommodationMinPrices`</div><div>**Service:** `LibBaseService::getAccommodationMinPrices`</div><div>**Auth:** Public (No JWT Required)</div></div>---

  </div>### Query Parameters

<div class="api-docs" id="bkmrk-parameter-type-requi"><table class="schema-table" dir="rtl"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>checkin\_date</td><td>string (YYYY-MM-DD)</td><td>✅</td><td>تاریخ ورود به اقامتگاه.</td></tr><tr><td>checkout\_date</td><td>string (YYYY-MM-DD)</td><td>✅</td><td>تاریخ خروج از اقامتگاه.</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه‌ی شعبه برای context فراخوانی AirPlus API.</td></tr><tr><td>accommodations\[\]</td><td>array of integers</td><td>✅</td><td>لیست شناسه‌های اقامتگاه‌هایی که باید نرخ حداقل‌شان محاسبه شود.</td></tr></tbody></table>

---

  </div>### Response Structure

<div class="api-docs" id="bkmrk-key-description-stat"><table class="schema-table" dir="rtl"><thead><tr><th>Key</th><th>Description</th></tr></thead><tbody><tr><td>Status</td><td>وضعیت درخواست، true در صورت موفقیت</td></tr><tr><td>Time</td><td>زمان پاسخ UNIX timestamp</td></tr><tr><td>Data</td><td>آبجکتی از اقامتگاه‌ها که کلید آن ID اقامتگاه است. برای هر مورد، `min\_price` و `min\_original\_price` بر اساس مجموع روزهای اقامت محاسبه شده است.</td></tr></tbody></table>

</div>### Example Response (Success)

```json
{
  "Status": true,
  "Time": 1733743200,
  "Data": {
    "2137": {
      "id": 2137,
      "min_price": 4800000,
      "min_original_price": 5500000
    },
    "2652": {
      "id": 2652,
      "min_price": 3600000,
      "min_original_price": 4100000
    }
  }
}
```

### Example Response (Error)

```json
{
  "status": false,
  "time": 1733743300,
  "error": {
    "code": 1002,
    "message": "Invalid accommodations parameter.",
    "trace": [ ... ]
  },
  "support": {
    "phone": "021-91016838 in 121",
    "email": "ict@airplus.app",
    "panel": "helpdesk.airplus.app"
  }
}
```

<div class="api-docs" id="bkmrk--2">---

  </div>### Logic Flow

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-ch%E1%BB%A9a-query-%D8%B4"><div class="flowchart" dir="rtl"><div class="flow-item">درخواست chứa query شامل checkin_date، checkout_date، branch و IDs</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Normalize Dates**  
محاسبه تعداد روز اقامت (diff)، همچنین تولید تاریخ‌های `next Tuesday → next Wednesday` برای تست قیمت‌ها.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Determine Active Providers**  
بررسی وضعیت APIهای فعال در جدول `application_interface` برای سرویس‌های `tport`، `sepehr_hotel`، `snapptrip_hotel`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Iterate accommodations**  
برای هر شناسه اقامتگاه → بررسی cache Redis. در نبود cache، شروع واکشی از همه providerها.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**External API Calls**  
- **TportApi::get\_hotel\_room\_price**: بر اساس mapped id و بازه تاریخی.
- **SepehrApi::search\_by\_city\_and\_date**: بر اساس IATA شهر یا استان.
- **AirPlusApi::search**: منبع داخلی hub.
- **SnappTripApi::get\_hotel\_availability\_calendar**.

</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Compute Min Prices**  
محاسبه کمترین مقدار نهایی و اصل بین همه نتایج موجود `minNetPrice = min(Tport, Sepehr, AirPlus, SnappTrip)`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Cache to Redis**  
کلید `accommodations:min_price:{id}` با TTL وابسته به درجه هتل (۵ستاره تا ۷روزه)</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Return JSON Result**  
لیست اقامتگاه‌ها با فیلدهای `min_price` و `min_original_price` ضربدر تعداد روزها.</div></div>---

  </div>### Expiration (Redis TTL Rules)

<div class="api-docs" id="bkmrk-hotel-rating-expirat"><table class="schema-table" dir="rtl"><thead><tr><th>Hotel Rating</th><th>Expiration (seconds)</th><th>Comment</th></tr></thead><tbody><tr><td>5 stars</td><td>21600 (6 hours)</td><td>High-traffic, frequent updates</td></tr><tr><td>4 stars</td><td>43200 (12 hours)</td><td>Medium update cycle</td></tr><tr><td>3 stars</td><td>172800 (2 days)</td><td>Stable pricing</td></tr><tr><td>Others</td><td>604800 (7 days)</td><td>Low-traffic accommodation</td></tr><tr><td>No price (fallback)</td><td>86400 (1 day)</td><td>در صورت عدم دریافت قیمت فعال از سرویس</td></tr></tbody></table>

</div>

# GET /b2c/v1/online/accommodation/get_room_type_prices

## Get Room Type Prices

این اندپوینت کلیهٔ قیمت‌های مرتبط با نوع اتاق (RoomType) را از سرویس‌دهندگان مختلف واکشی و تجمیع می‌کند. پس از محاسبهٔ قیمت خالص (Net)، قیمت اصلی (Board)، و ماکاپ‌ها برای هر تأمین‌کننده، نتیجه‌ی مرتب‌شده و کمترین قیمت محاسبه‌شده در Redis ذخیره می‌شود. این ورودی، موتور قیمت‌گذاری اصلی صفحه هتل در B2C است.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/accommodation/get_room_type_prices`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@getRoomTypePrices`</div><div>**Service:** `LibBaseService::getRoomTypePrices`</div><div>**Auth:** Public (No JWT Required)</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%B6%D8%B1%D9%88%D8%B1%DB%8C-%D8%AA%D9%88"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>ضروری</th><th>توضیح</th></tr></thead><tbody><tr><td>accommodation\_id</td><td>integer</td><td>✅</td><td>شناسهٔ اقامتگاه در جدول `hotels`.</td></tr><tr><td>checkin\_date</td><td>string (YYYY-MM-DD)</td><td>✅</td><td>تاریخ ورود.</td></tr><tr><td>checkout\_date</td><td>string (YYYY-MM-DD)</td><td>✅</td><td>تاریخ خروج.</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه شعبه برای پردازش قیمت نهایی.</td></tr><tr><td>group</td><td>integer</td><td>❌</td><td>شناسه گروه کاربری.</td></tr><tr><td>level</td><td>integer</td><td>❌</td><td>سطح کاربری (برای تفکیک نرخ‌گذاری مارکاپ).</td></tr><tr><td>adults\_count</td><td>integer</td><td>❌</td><td>تعداد بزرگسال؛ فیلتر ظرفیت اتاق‌ها.</td></tr><tr><td>children\_count</td><td>integer</td><td>❌</td><td>تعداد کودک؛ فیلتر ظرفیت.</td></tr></tbody></table>

---

</div>### Logical Flow

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%85%DB%8C%E2%80%8C%D8%B4%D9%88"><div class="flowchart" dir="rtl"><div class="flow-item">درخواست دریافت می‌شود و مقدار `accommodation_id` از `mapping_accommodations` خوانده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**RoomType Join**  
واکشی تمام رکوردهای `accommodation_roomtypes` با join روی `mapping_roomtypes` برای شناسه‌های تپورت، سپهر، ایرپلاس و اسنپ‌تریپ.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Activate Providers**  
بررسی فعال بودن اینترفیس‌های سرویس‌دهنده در جدول `application_interface`.  
فعال‌های معمول: `tport`، `sepehr_hotel`، `snapptrip_hotel`، و AirPlus (همیشه فعال).</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Tport Price Fetch**  
🔹 فراخوانی `TportApi::get_hotel_room_price` واکشی مالیات و قیمت هر روز → محاسبه‌ی تجمعی و markups از `BaseService::getRoomTypeMarkups`.  
ذخیره در `$room->board_type_list[]`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Sepehr Prices**  
🔹 فراخوانی `SepehrApi::search_by_city_and_date` بر اساس IATA مقصد. واکشی RoomTypeList و RateDetailList. فیلتر + مارکاپ → ایجاد Financial array.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**AirPlus Prices**  
🔹 فراخوانی `AirPlusApi::search` با نوع accommodation. محاسبه قیمت روزانه بزرگسال/کودک/تخت اضافه → ذخیره در فیلد financial_total.  
بررسی ظرفیت در جدول‌های رزرو موقت از `ReservationController::getAccommodationRooms()`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**SnappTrip Prices**  
🔹 فراخوانی `SnappTripApi::get_hotel_availability`. قیمت‌های نهایی بر اساس `pricing.original_sell_price` و تعداد روز اقامت محاسبه می‌شوند. ضرب در 10 برای تبدیل به ریال.</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Sort + Cache**  
مرتب‌سازی بر اساس کمترین `net_price` از فیلد `financial_total`. ذخیرهٔ کمترین قیمت با کلید Redis `accommodations:min_price:{id}` و TTL تطبیقی.</div></div>---

</div>### Redis Cache Logic

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%D8%AC%D9%87%D9%94-%D9%87%D8%AA%D9%84-ttl-%28secon"><table class="schema-table" dir="rtl"><thead><tr><th>درجهٔ هتل</th><th>TTL (seconds)</th><th>توضیح</th></tr></thead><tbody><tr><td>rate = 5</td><td>21600</td><td>نرخ گذاری زیاد تغییر می‌کند.</td></tr><tr><td>rate = 4</td><td>43200</td><td>دورهٔ به‌روزرسانی متوسط.</td></tr><tr><td>rate = 3</td><td>172800</td><td>پایدار دو روزه؛ قیمت ثابت‌تر.</td></tr><tr><td>سایر</td><td>604800</td><td>هتل‌های کم‌تردد؛ TTL یک هفته.</td></tr><tr><td>قیمت صفر</td><td>43200</td><td>در صورت عدم دریافت نرخ معتبر.</td></tr></tbody></table>

---

</div>### Response Structure

<div class="api-docs" id="bkmrk-%DA%A9%D9%84%DB%8C%D8%AF-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-status-tr"><table class="schema-table" dir="rtl"><thead><tr><th>کلید</th><th>توضیح</th></tr></thead><tbody><tr><td>Status</td><td>true در صورت واکشی موفق.</td></tr><tr><td>Time</td><td>تایم‌استمپ unix.</td></tr><tr><td>Data</td><td>آرایه‌ای از RoomType‌ها؛ هر آیتم شامل جزئیات `board_type_list`، قیمت‌ها، و ظرفیت خرید.</td></tr></tbody></table>

</div>### نمونه خروجی موفق

```json
{
  "Status": true,
  "Time": 1733744200,
  "Data": [
    {
      "id": 10502,
      "fa_title": "اتاق دوتخته فول برد",
      "purchase_capacity": 3,
      "board_type_list": [
        {
          "service": "airplus",
          "financial_total": {
            "board_price": 4860000,
            "net_price": 4500000,
            "markup": 0.07
          },
          "supplier": { "id": 28, "title_fa": "AirPlus Hub" }
        }
      ]
    }
  ]
}
```

# GET /b2c/v1/online/accommodation/get_details

## Get Accommodation Details

این اندپوینت تمام جزئیات مربوط به یک اقامتگاه (هتل) را بر اساس شناسه ورودی برمی‌گرداند. جزئیات شامل اطلاعات عمومی هتل، رسانه‌ها (تصاویر، ویدیو، لوگو)، امکانات گروه‌بندی‌شده، سیاست‌ها، قوانین عمومی و قوانین استرداد می‌باشد. در صورت وجود چارتر فعال برای آن هتل، اطلاعات انتقال (Transfer) نیز به سیاست‌ها افزوده می‌شود.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/accommodation/get_details`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@getAccommodationDetails`</div><div>**Service:** `LibBaseService::getAccommodationDetails`</div><div>**Auth:** Public (No JWT Required)</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>accommodation\_id</td><td>integer</td><td>✅</td><td>شناسه اقامتگاه مورد نظر در جدول `hotels`.</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D9%87%D8%AA%D9%84-%D8%A7%D8%B2-"><div class="flowchart" dir="rtl"><div class="flow-item">دریافت رکورد هتل از جدول `hotels` توسط `accommodation_id`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Media Aggregation** واکشی مدیاهای مرتبط از جدول `media`: - اگر `type = cover` → در اولویت نمایش.
- مدیاها بر اساس نوع گروه‌بندی می‌شوند: تصاویر (image)، ویدیوها (video).
- اگر لوگوی خاص موجود نباشد، لوگوی پیش‌فرض (default) جایگزین می‌شود.

</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Facilities &amp; Categories**- اتصال جدول `facilities` با `accommodation_facilities_mapping`.
- سپس گروه‌بندی امکانات بر اساس `facilities_categories`.
- اگر هیچ موردی موجود نباشد → مقدار false برگردانده می‌شود.

</div><div class="flow-arrow">↓</div><div class="flow-item-process">**Policies &amp; Rules**- واکشی `accommodation_policies`، `accommodation_cancellation_rules`، و `accommodation_rules`.
- در صورت وجود چارتر فعال (جدول `charters`)، قوانین Transfer (Welcome/Return) از `charter_items.details` افزوده می‌شود.

</div><div class="flow-arrow">↓</div><div class="flow-item-success">**Return JSON Response**  
داده‌ها در قالب `AccommodationResource` بازگردانده می‌شوند.</div></div>---

</div>### Response Structure

<div class="api-docs" id="bkmrk-%DA%A9%D9%84%DB%8C%D8%AF-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-status-tr"><table class="schema-table" dir="rtl"><thead><tr><th>کلید</th><th>توضیح</th></tr></thead><tbody><tr><td>Status</td><td>true در صورت موفقیت در یافتن هتل.</td></tr><tr><td>Time</td><td>timestamp یونیکس.</td></tr><tr><td>Data</td><td>شیء بازگشتی از نوع `AccommodationResource`.</td></tr></tbody></table>

</div>### AccommodationResource شامل فیلدهای زیر است:

<div class="api-docs" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AF-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD-id-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-"><table class="schema-table" dir="rtl"><thead><tr><th>فیلد</th><th>توضیح</th></tr></thead><tbody><tr><td>id</td><td>شناسه اقامتگاه</td></tr><tr><td>fa\_title / en\_title</td><td>عنوان فارسی و انگلیسی</td></tr><tr><td>rate</td><td>ستاره (رتبه)</td></tr><tr><td>city, state, country</td><td>محل جغرافیایی</td></tr><tr><td>address, location</td><td>آدرس و موقعیت مکانی</td></tr><tr><td>description</td><td>توضیحات</td></tr><tr><td>media</td><td>شامل `images` و `videos` (یا false در صورت عدم وجود)</td></tr><tr><td>logo</td><td>مسیر لوگوی اختصاصی یا پیش‌فرض.</td></tr><tr><td>facility\_categories</td><td>آرایه‌ای از دسته‌بندی امکانات (هر مورد شامل id، title، facilities).</td></tr><tr><td>rules</td><td>آبجکت شامل کلیدهای cancellation، public، و policies.</td></tr></tbody></table>

---

</div>### Example Response (Success)

```json
{
  "Status": true,
  "Time": 1733744800,
  "Data": {
    "id": 2137,
    "fa_title": "هتل بزرگ شیراز",
    "en_title": "Grand Hotel Shiraz",
    "rate": 5,
    "city": "شیراز",
    "address": "بلوار قرآن، نرسیده به دروازه قرآن",
    "logo": "https://storage.service01.ir/media/accommodations/2025/logo/grand-shiraz.png",
    "media": {
      "images": [{ "path": "https://storage.../1.jpg" }],
      "videos": false
    },
    "facility_categories": [
      {
        "id": 3,
        "title": "امکانات رفاهی",
        "facilities": [
          { "id": 12, "title": "استخر سرپوشیده" },
          { "id": 24, "title": "سونا و جکوزی" }
        ]
      }
    ],
    "rules": {
      "cancellation": [
        { "id": 1, "hours_before": 48, "percent": 10 }
      ],
      "public": [
        { "id": 2, "rule": "ورود حیوانات خانگی ممنوع است" }
      ],
      "policies": {
        "checkin": "14:00",
        "checkout": "12:00",
        "welcome_transfer": 1,
        "return_transfer": 0
      }
    }
  }
}
```

### Example Response (Not Found)

```json
{
  "Status": false,
  "Time": 1733744801,
  "Message": "Accommodation not found."
}
```

# GET /b2c/v1/online/flight/class/{calculation_id}

## Get Flight Class Data

این اندپوینت برای واکشی اطلاعات کامل یک کلاس پروازی از روی شناسه محاسبه (`calculation\_id`) طراحی شده است. داده بازگشتی شامل جزئیات فنی، مالی، مارکاپ‌ها و تخفیف‌ها برای سنین مختلف (Adult/Child/Infant) است. درصورت معتبر بودن شناسه، داده از سرویس `AirPlusApi` با متد **search** واکشی می‌شود و پس از بررسی فیلترهای `application_filter` و پردازش مارکاپ‌ها توسط `LibBaseService::checkSearchFlightItem()` به صورت JSON بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/flight/class/{calculation_id}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@getFlightClassData`</div><div>**Service:** `AirPlusApi & LibBaseService::checkSearchFlightItem`</div><div>**Auth:** Public (Without JWT)</div></div>---

</div>### Path Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>calculation\_id</td><td>integer</td><td>✅</td><td>شناسه محاسبهٔ نرخ که از جدول `charter_calculations_route` استخراج می‌شود. مقدار واقعی فیلد در DB برابر `calculation_id - 10000` است.</td></tr></tbody></table>

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA-1"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>branch</td><td>string</td><td>✅</td><td>شناسهٔ شعبه فعلی برای تنظیم مسیر فروش (مثل `b2c-shiraz`).</td></tr><tr><td>group</td><td>string</td><td>❌</td><td>گروه کاربری فعلی. مقادیر مجاز: `b2c`, `b2b`, `b2e`, `agency`.</td></tr><tr><td>level</td><td>integer</td><td>❌</td><td>سطح کاربر در گروه (پیش‌فرض ۱).</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A9-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-calcu"><div class="flowchart" dir="rtl"><div class="flow-item">📩 دریافت ورودی `calculation_id` و پارامترهای `branch`, `group`, `level`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۱. بررسی وجود رکورد محاسبه:**  
جستجو در جدول `charter_calculations_route` با شرط‌های:  
`id = calculation_id - 10000` و `status = 1` → واکشی `main_id`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۲. فراخوانی AirPlusApi:**  
متد `sendRequest('search')` با پارامترهای زیر:  
- charter\_id = main\_id
- calculation\_id = calculation\_id - 10000
- all\_routes = true

پاسخ در کلید `Data.Information[0]` شامل کلاس‌های پروازی است.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۳. اجرای منطق پردازش کلاس‌ها:**  
انتقال داده به `LibBaseService::checkSearchFlightItem()` برای:  
- مرتب‌سازی کلاس‌ها بر اساس قیمت، ظرفیت، و ویژگی‌های Remarks.
- اعمال مارکاپ‌ها و تخفیف‌ها بر اساس گروه کاربر و نوع پرواز.
- بررسی `application_filter` برای اعتبار مبدا/مقصد، ایرلاین و حالت‌های allowed/unallowed.

</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ در صورت موفقیت، دادهٔ نهایی کلاس‌ها در قالب `payload` بازگردانده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-failed">❌ در صورت عدم پیدا شدن رکورد محاسبه، پاسخ خطا با کد 404 و message "آیتم مورد نظر یافت نشد." برگردانده می‌شود.</div></div>---

</div>### Response Samples

#### ✅ موفق (پردازش کلاس‌های پروازی)

```json
{
  "payload": {
    "Airline": { "iata": "W5", "title": "Mahan Air" },
    "Origin": { "iata": "THR" },
    "Destination": { "iata": "MHD" },
    "FlightNumber": "W51234",
    "FlightType": "Charter",
    "Classes": [
      {
        "CabinType": "Economy",
        "Financial": {
          "Adult": {
            "BaseFare": 950000,
            "Tax": 50000,
            "Payable": 1000000,
            "Markup": {
              "percent": 10,
              "price": 100000,
              "final": 1100000,
              "discount": { "percent": 0, "price": 0 }
            }
          }
        },
        "AvailableSeat": 4,
        "Remarks": {
          "Inbound": { "Special": 0, "TourRequirement": false }
        }
      }
    ]
  },
  "meta": { "timestamp": 1733750500 }
}
```

#### ❌ خطا – آیتم یافت نشد

```json
{
  "error": {
    "code": 1001,
    "message": ".آیتم مورد نظر یافت نشد"
  },
  "meta": { "timestamp": 1733750510 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D9%85%D8%A7%D8%B1%DA%A9%D8%A7%D9%BE%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-hubcont">- مارکاپ‌ها از `HubController::markups($branch)` خوانده می‌شوند و طبق گروه و سطح کاربر اعمال می‌شوند.
- محاسبات تخفیف شامل فیلدهای `discount.percent` و `discount.value` برای هر سن مسافر است.
- فیلترهای سیستم (جدول `application_filter`): بسته به نوع پرواز (system/charter/any) و وضعیت (`allowed/unallowed`) ترتیب اجرا دارند.
- در صورتی که داده‌ای با شرایط فیلتر تطابق نداشته باشد، تابع `checkSearchFlightItem` مقدار `false` بازمی‌گرداند.
- در خروجی API، مقادیر مالی هر رده سنی شامل `BaseFare`, `Tax`, `TotalFare`, `Payable`, `Markup` و `Commission` است.

</div>

# GET /b2c/v1/online/payment/flight/tracking

## Tracking Payment Flight

اندپوینت زیر برای بررسی وضعیت پرداخت و رزرو پرواز از روی کد ملی و شماره رفرنس طراحی شده است. این متد با اتصال داخلی به `V2TradeController::operationTrade()` وضعیت فاکتور، اطلاعات پرواز و مسافران را بازیابی کرده و داده‌های حساس مالی را از خروجی حذف می‌کند.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fonline%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/online/payment/flight/tracking`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1OnlineController@trackingPaymentFlight`</div><div>**Auth:** Public (بدون نیاز به JWT)</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>special\_code</td><td>string</td><td>✅</td><td>کد ملی مسافر یا شناسه ویژه ثبت شده در سیستم مشتریان (`customers.national_code`)</td></tr><tr><td>reference</td><td>integer</td><td>✅</td><td>شماره فاکتور یا رفرنس پرداخت (با offset داخلی `-10000` هنگام مقایسه در جدول فاکتورها)</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسهٔ شعبه‌ای که پرداخت در آن انجام شده است</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%88%D8%B1%D9%88%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7%DB%8C-s"><div class="flowchart" dir="rtl"><div class="flow-item">📥 دریافت ورودی‌های `special_code` و `reference`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۱. بررسی مشتری با کد ملی:**  
جستجو در جدول `customers` برای یافتن رکوردی با `national_code = special_code`.  
اگر یافت نشود، پاسخ خطا با کد 404 و پیام "مسافر مورد نظر یافت نشد" برگردانده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۲. بررسی وجود فاکتور:**  
در جدول `factors` فیلدهای زیر بررسی می‌شوند:  
- `serial = reference - 10000`
- `branch = branch_id`
- `customer = customer.id`

اگر رکوردی یافت شود، ادامه می‌دهد؛ در غیر این صورت پاسخ خطا با پیام "رفرنس مورد نظر یافت نشد" برمی‌گردد.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۳. آماده‌سازی درخواست برای TradeController:**  
افزودن پارامتر `id = reference` به درخواست.  
تنظیم اپراتور فرضی: `{ id: 12, access: ... }` برای اجرای تابع `operationTrade`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۴. اجرای Trade Operation:**  
ساخت شیء `V2TradeController` و اجرای متد `operationTrade($request)`.  
دادهٔ بازگشتی (JSON) شامل فاکتور و اقلام تراکنش واکشی می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۵. پاک‌سازی داده‌ها:**  
در آرایهٔ خروجی `data`، فیلدهای محرمانه حذف می‌شوند:  
`[buy, value_added, serial, provider, currency, deadline, failure_bill]`  
و فیلدها به شکل زیر اصلاح می‌شوند:  
`serial_id = serial_id + 10000`</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ خروجی نهایی شامل اطلاعات کامل فاکتور و مسافران است و در قالب کلید `payload` برمی‌گردد.</div></div>---

</div>### Response Samples

#### ✅ موفق - فاکتور یافت شد

```json
{
  "payload": {
    "serial_id": 26789,
    "data": [
      {
        "serial_id": 26790,
        "title": "تهران → مشهد",
        "status": 3,
        "departure_datetime": "2025-12-09 10:15",
        "arrival_datetime": "2025-12-09 11:45",
        "airline_code": "W5",
        "aircraft": "Airbus A321",
        "cabin": "Economy"
      }
    ],
    "passengers": [
      { "name_fa": "علیرضا رضایی", "sex": "male", "national_code": "1234567890" }
    ],
    "leader": { "name_fa": "علیرضا رضایی", "mobile": "09121234567" }
  },
  "meta": { "timestamp": 1733750909 }
}
```

#### ❌ خطا – مشتری یافت نشد

```json
{
  "error": {
    "code": 1001,
    "message": ".مسافر مورد نظر یافت نشد"
  },
  "meta": { "timestamp": 1733750915 }
}
```

#### ❌ خطا – فاکتور یافت نشد

```json
{
  "error": {
    "code": 1001,
    "message": ".رفرنس مورد نظر یافت نشد"
  },
  "meta": { "timestamp": 1733750921 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D8%AA%D8%A7%D8%A8%D8%B9-operationtrade%28">- تابع `operationTrade()` از مسیر داخلی `/v2/trade/operation` فراخوانی شده و مسئول واکشی جزئیات پرداخت است.
- در فرآیند اعتبارسنجی، شناسهٔ فاکتور با آفست **10000−** بررسی می‌شود تا با ساختار فاکتورهای داخلی سازگار گردد.
- عامل اجرای عملیات با شناسهٔ ثابت `operator.id = 12` تنظیم شده تا از سطح دسترسی سیستم مرکزی استفاده کند.
- تمام زمان‌های بازگشتی در خروجی با فرمت میلادی و فیلد `meta.timestamp` (ثانیه‌های یونیکس) اضافه می‌شوند.
- کلید `leader` فقط در صورتی وجود دارد که فاکتور دارای سرپرست یا مشتری اصلی باشد.

</div>

# GET /b2c/v1/financial/list

## List Financial History

این اندپوینت برای نمایش لیست تراکنش‌های مالی کاربران طراحی شده است و بسته به نوع گروه کاربری (`b2c` یا `b2b`/ `colleague`) داده‌ها از جداول متفاوتی واکشی می‌گردند. برای کاربران B2C، از جداول `factor_items`، `factors` و `pays` اطلاعات استخراج می‌شود و برای کاربران B2B/Colleague از جدول `wallet`.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Ffinanci"><div class="endpoint-info"><div>**URL:** `/b2c/v1/financial/list`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@listFinancial`</div><div>**Auth:** JWT Required (`middleware: authWithJwt`)</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>group</td><td>string</td><td>✅</td><td>نوع گروه کاربری فعال. مقادیر مجاز: `b2c`, `b2b`, `colleague`.</td></tr><tr><td>branch</td><td>integer</td><td>❌</td><td>شناسه شعبه کاربر (مورد استفاده فقط برای B2B/Colleague).</td></tr><tr><td>operator.id</td><td>integer</td><td>✅</td><td>شناسهٔ کاربر یا اپراتور فعلی که از JWT تزریق می‌شود.</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87%D8%A7%DB%8C-"><div class="flowchart" dir="rtl"><div class="flow-item">📥 دریافت پارامترهای JWT شامل `operator.id` و `group`</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۱. مسیر B2C:**  
- جستجوی فاکتورها در `factor_items` که `customer_id = operator.id` است یا در `factors.customer = operator.id`.
- فاکتورها با وضعیت غیر از (۲،۵) انتخاب می‌شوند.
- شناسه‌های فاکتور استخراج و با جدول `pays` ترکیب می‌شوند.
- کوئری شامل تراکنش‌هایی با شرایط زیر است: 
    - `type = 'receive'`
    - `object_type = 'reference'`
    - `object ∈ references`
    - یا: `functor_type = 'customer'` و `functor_account = operator.id`

داده‌ها با وضعیت غیر از [۱،۲،۵] فیلتر می‌گردند.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۲. ترکیب داده با Redis Cache:**  
برای هر رکورد `pays`، مقدار توضیحات حسابداری از کلید `accounting:pays:{id}` در Redis واکشی می‌شود و در فیلد `description` درج می‌گردد.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۳. مسیر B2B / Colleague:**  
واکشی تراکنش‌های کیف پول (`wallet`) با شرط‌های زیر: - `branch = request.branch`
- `status = 1`
- `operator_type = 'b2b'`
- `operator = operator.id`

تراکنش‌ها در خروجی با فرمت: `id+10 000 ⇒ serial / tracking_code` و نوع تراکنش بر اساس `credit ≠ 0 ? 'receive' : 'payment'`.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ در نهایت آرایهٔ خروجی `data` شامل لیست کامل تراکنش‌ها (دریافت، پرداخت، کیف‌پول، فاکتور) همراه با فیلدهای استاندارد مالی بازگردانده می‌شود.</div></div>---

</div>### Response Sample

```json
{
  "status": true,
  "time": 1733751800,
  "data": [
    {
      "serial": 19087,
      "type": "receive",
      "type_pay": "reference",
      "deadline": "20251209",
      "currency": "IRR",
      "fee": 0,
      "amount": 2500000,
      "tracking_code": "TRX-19572",
      "description": {
        "reason": "پرداخت بابت فاکتور 17894",
        "gateway": "Parsian",
        "invoice": "Ref#X20991"
      }
    },
    {
      "serial": 26789,
      "type": "payment",
      "type_pay": "wallet",
      "deadline": "20251209",
      "currency": "IRR",
      "fee": 0,
      "amount": 1200000,
      "tracking_code": 26789,
      "description": "بابت هزینه خدمات هاب با عطف 2851"
    }
  ]
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-redis-%DA%A9%D9%84%DB%8C">- داده‌ها از Redis کلید `accounting:pays:{id}` فقط اگر مقدار JSON معتبر باشد استخراج می‌گردند.
- در بخش B2C، فقط فاکتورهایی با وضعیت فعال (غیر از ۲ و ۵) در نظر گرفته می‌شوند.
- در صورت عدم وجود تراکنش، خروجی مقدار `data: []` بازمی‌گرداند و `status` همواره `true` است.
- تمام تاریخ‌ها در خروجی با فرمت `YYYYMMDD` محاسبه می‌شوند.
- تفاوت مقدار `fee` با مبلغ کل در تراکنش‌ها، کارمزد داخلی نیست و صفر برمی‌گردد مگر از Redis قابل استخراج باشد.
- برای B2B، شناسهٔ تراکنش کیف‌پول همیشه با offset ثابت `+10 000` در serial نمایش داده می‌شود تا از شناسه‌های فاکتور تفکیک گردد.

</div>

# GET /b2c/v1/passengers/previous

## List Previous Passengers

این اندپوینت جهت واکشی لیست مسافرانی که پیش‌تر در رزروهای کاربر (یا همکار) شرکت داشته‌اند طراحی شده است. اطلاعات از جدول `customers` واکشی شده و شامل جزئیات هویتی، ملیتی، گذرنامه و تماس فرد می‌باشد.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fpasseng"><div class="endpoint-info"><div>**URL:** `/b2c/v1/passengers/previous`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `PassengersController@indexPassengersPrevious`</div><div>**Auth:** JWT Required (`middleware: authWithJwt`)</div></div>---

</div>### Query Parameters (from JWT + Request)

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>operator.id</td><td>integer</td><td>✅</td><td>شناسه اپراتور / مسافر اصلی (از JWT)</td></tr><tr><td>group</td><td>string</td><td>✅</td><td>نوع گروه کاربری فعال. مقادیر مجاز: `b2c`، `b2b`، `colleague`.</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%D8%A7%D9%BE%D8%B1%D8%A7%D8%AA"><div class="flowchart" dir="rtl"><div class="flow-item">📥 دریافت شناسه اپراتور از JWT → `$passengerId = $request->get('operator')->id`</div><div class="flow-arrow">↓</div><div class="flow-item-check">بررسی وجود `passengerId`. اگر نباشد، پاسخ خطای 400 با پیام **Passenger ID is required** و `code = passenger_id_required`.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۱. واکشی مسافران قبلی:**  
- اگر گروه کاربری `b2b` یا `colleague` باشد: 
    - از جدول `factors` رکوردهایی که `colleague_auth = operator.id` دارند و وضعیتشان خارج از \[2,5\] است استخراج می‌شود.
    - مقادیر فیلد `customer` از آن‌ها گرفته و از جدول `customers` واکشی می‌شود.
- اگر گروه کاربری `b2c` باشد: در جدول `customers` رکوردهایی که `relationship = passengerId` هستند واکشی می‌شوند.

سپس برای اطمینان، خود کاربر نیز با شرط `orWhere('id', passengerId)` به نتایج افزوده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">**۲. تبدیل داده‌ها:**  
برای هر مسافر: - در صورت نبود ملیت، `citizenship = 118` (ایران) تنظیم می‌شود.
- دادهٔ کشور از جدول `countries` فیلتر می‌شود با `status=1` و `fa_nationality IS NOT NULL`.
- تارگت خروجی شامل ساختارهای استاندارد برای **fullname**، **identity**، **passport**، **mobile**، **birth** و ... است.

</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ خروجی نهایی: `{"items": [...], "meta": {"timestamp": time()}}`</div></div>---

</div>### Response Samples

#### ✅ موفق - لیست مسافران قبلی

```json
{
  "items": [
    {
      "id": 1547,
      "gender": 1,
      "fullname": {
        "first_name": { "fa": "علیرضا", "en": "Alireza" },
        "last_name": { "fa": "رضایی", "en": "Rezaei" }
      },
      "email": "alireza@example.com",
      "mobile": "09123456789",
      "birth": "13721021",
      "identity": {
        "id": "1234567890",
        "nationality": {
          "id": 118,
          "iso": "IR",
          "title": { "fa": "ایران", "en": "Iran" },
          "nationality": { "fa": "ایرانی", "en": "Iranian" }
        }
      },
      "passport": {
        "id": "P98651234",
        "expire_at": "20270930"
      }
    }
  ],
  "meta": { "timestamp": 1733752711 }
}
```

#### ❌ خطا – Passenger ID خالی

```json
{
  "error": {
    "message": "Passenger ID is required",
    "code": "passenger_id_required"
  },
  "meta": { "timestamp": 1733752715 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D9%85%D9%86%D8%A8%D8%B9-%D8%A7%D8%B5%D9%84%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%3A-%D8%AC%D8%AF%D9%88%D9%84">- منبع اصلی داده: جدول `customers`، با ارتباط ضمنی از فیلد `relationship` به شناسهٔ اپراتور.
- در حالت B2B/Colleague، ارتباط با فیلد `colleague_auth` از جدول `factors` برقرار می‌شود.
- ملیت‌ها از جدول `countries` بر اساس وضعیت فعال (status=1) و فیلدهای `fa_nationality`/`en_nationality` استخراج می‌شوند.
- فرایند map در انتها داده‌ها را از آبجکت DB به قالب JSON با فرمت Front‑Friendly تبدیل می‌کند.
- در همه پاسخ‌ها کلید `meta.timestamp` جهت تطبیق زمانی اضافه می‌شود.

</div>

# GET /b2c/v1/gateway/details

## Payment Gateway Details

این اندپوینت جزئیات تراکنش از درگاه بانکی (Payment Gateway) را بر اساس `serial_id` برمی‌گرداند. در صورتی‌که وضعیت تراکنش موفق (`status=3`) باشد، اطلاعات کارت و کد رهگیری نمایش داده می‌شود؛ در غیر این صورت، پیام خطا و جزئیات تراکنش ناموفق بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-"><div class="endpoint-section">  
</div>---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fgateway"><div class="endpoint-info"><div>**URL:** `/b2c/v1/gateway/details`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@paymentGatewayDetails`</div><div>**Auth:** ندارد (مشاهده جزئیات بر اساس serial)</div><div>**Source Tables:** `payment_gateway`, `gateways`</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>serial\_id</td><td>string | integer</td><td>✅</td><td>شناسه‌ٔ تراکنش در جدول `payment_gateway`</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامتر `serial_id` از QueryString</div><div class="flow-arrow">↓</div><div class="flow-item-process">۲. اجرای Query:</div></div></div>```
SELECT payment_gateway.*, gateways.drive as drive_title
FROM payment_gateway
JOIN gateways ON payment_gateway.drive = gateways.id
WHERE payment_gateway.serial_id = :serial_id
```

<div class="api-docs" id="bkmrk-%E2%86%93-%D8%A2%DB%8C%D8%A7-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF%DB%8C-%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D8%AF"><div class="flowchart" dir="rtl"><div class="flow-arrow">↓</div><div class="flow-item-decision">آیا رکوردی یافت شد؟ - ✅ بله → ادامه پردازش
- ❌ خیر → بازگشت پیام 404

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">بررسی فیلد `status`: - `3` → پرداخت موفق ✅
- غیر از 3 → پرداخت ناموفق ❌

</div><div class="flow-arrow">↓</div><div class="flow-item-process">**در حالت پرداخت موفق:**- اگر `drive_title = 'behpardakht'` یا `'sep'` → مقدار `result` دیکد (JSON decode) می‌شود.
- مقادیر زیر استخراج می‌شوند: 
    - `amount`: مبلغ پرداخت
    - `card`: شماره کارت (از `CardHolderPan`)
    - `tracking_code`: کد رهگیری (`SaleReferenceId`)
    - `datetime`: زمان تراکنش
- درگاه‌های دیگر در خروجی خام (`$tempPaymentGateway`) بازگردانده می‌شوند.

</div><div class="flow-arrow">↓</div><div class="flow-item-process">**در حالت پرداخت ناموفق:**  
اگر **behpardakht** باشد → پیام خطا از `$result->message` گرفته می‌شود و `SaleOrderId` به عنوان `tracking_code` بازگردانده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ پاسخ نهایی JSON با وضعیت `200` (در موفقیت) یا `400`/`404` در خطا بازگردانده می‌شود.</div></div>---

</div>### Response Samples

#### 🎯 پرداخت موفق (درگاه Behpardakht)

```json
{
  "payload": {
    "drive": "behpardakht",
    "amount": 1250000,
    "card": "610433******8124",
    "datetime": "2025-12-09T10:12:35",
    "tracking_code": "74632159"
  },
  "meta": { "timestamp": 1733749500 }
}
```

#### ⚠️ پرداخت ناموفق (درگاه Behpardakht)

```json
{
  "error": {
    "code": 1000,
    "message": "پرداخت ناموفق | تراکنش توسط دارنده کارت لغو شد"
  },
  "meta": {
    "timestamp": 1733749600,
    "data": {
      "message": "تراکنش توسط دارنده کارت لغو شد",
      "datetime": "2025-12-09T10:12:35",
      "tracking_code": "69854172"
    }
  }
}
```

#### ❌ پرداختی یافت نشد

```json
{
  "error": {
    "code": 1000,
    "message": "پرداختی با این مشخصات یافت نشد."
  },
  "meta": { "timestamp": 1733749700 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D9%85%D9%86%D8%A8%D8%B9-%D8%A7%D8%B5%D9%84%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87%3A-%D8%AC%D8%AF%D9%88%D9%84">- منبع اصلی داده: جدول `payment_gateway` (اطلاعات تراکنش) + جدول `gateways` (متادیتای درگاه).
- پارامتر `serial_id` از Query دریافت می‌شود و باید منحصربه‌فرد باشد.
- فیلد `status=3` معرف پرداخت نهایی و تایید شده است.
- درگاه‌های اختصاصی (Behpardakht و Sep) دارای فیلد JSON `result` هستند که حاوی داده‌های تراکنش مانند شماره کارت و کد مرجع است.
- در صورت عدم match درگاه خاص، پاسخ به صورت خام (Object کامل DB row) بازگردانده می‌شود.

</div>

# GET /b2c/v1/discount/submit

## Submit Discount Code

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

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fdiscoun"><div class="endpoint-info"><div>**URL:** `/b2c/v1/discount/submit`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@discountSubmit`</div><div>**Authorization:** اختیاری (استفاده از JWT برای استخراج `operator`)</div><div>**Related Table:** `payment_discount`</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه دفتر (Branch ID)</td></tr><tr><td>type</td><td>string</td><td>✅</td><td>نوع استفاده از تخفیف (مثلاً `flight`، `hotel`، `train`)</td></tr><tr><td>code</td><td>string</td><td>✅</td><td>کد تخفیف واردشده توسط کاربر</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%DB%B1%EF%B8%8F%E2%83%A3-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%B1%D9%88%DB%8C-%D8%AC%D8%AF%D9%88%D9%84-p"><div class="flowchart" dir="rtl"><div class="flow-item">۱️⃣ کوئری روی جدول `payment_discount`</div></div></div>```
SELECT * FROM payment_discount
WHERE branch = :branch
  AND status = 1
  AND type = :type
  AND code = LOWER(:code)
LIMIT 1;
```

<div class="api-docs" id="bkmrk-%E2%86%93-%D8%A2%DB%8C%D8%A7-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D8%AF%D8%9F"><div class="flowchart" dir="rtl"><div class="flow-arrow">↓</div><div class="flow-item-decision">آیا رکورد یافت شد؟ - ✅ بله → ادامه
- ❌ خیر → پیام خطای «کد تخفیف معتبر نمی‌باشد»

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">آیا محدودیت استفاده (`limit`) دارد؟ - اگر null → بدون محدودیت
- اگر &gt;0 → قابل استفاده
- اگر 0 → پیام خطای «کد تخفیف تمام شده است»

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">آیا تخفیف برای کاربر خاص تعریف شده؟ - اگر user=null → برای همه مجاز
- اگر user=id اپراتور → مجاز
- در غیر این صورت → «کد تخفیف برای کاربر دیگری می‌باشد»

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">بررسی انقضا (`expiration`) - اگر null → همیشه معتبر
- اگر زمان فعلی &lt; expiration → معتبر
- در غیر این صورت → «کد تخفیف منقضی شده است»

</div><div class="flow-arrow">↓</div><div class="flow-item-process">🔄 اگر limit برابر 1 → بروزرسانی رکورد:</div></div></div>```
status = 3, limit = 0
```

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1-%D8%BA%DB%8C%D8%B1-%D8%A7%DB%8C%D9%86-%D8%B5%D9%88%D8%B1%D8%AA-%D9%81%D9%82%D8%B7-"><div class="flowchart" dir="rtl"><div class="flow-item-process">در غیر این صورت فقط عدد limit یک واحد کاهش می‌یابد.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ بازگرداندن پاسخ موفق با داده‌های تخفیف: - `id`
- `title`
- `type` (discount\_type در DB)
- `value` (discount\_value در DB)

</div></div>---

</div>### Response Samples

#### ✅ تخفیف معتبر

```json
{
  "status": true,
  "time": 1733756000,
  "data": {
    "id": 47,
    "title": "تخفیف ویژه پرواز نوروزی",
    "type": "percent",
    "value": 10
  }
}
```

#### ❌ کد تخفیف منقضی شده

```json
{
  "status": false,
  "time": 1733756005,
  "message": "کد تخفیف وارد شده منقضی شده است."
}
```

#### ❌ کد تخفیف برای کاربر دیگری است

```json
{
  "status": false,
  "time": 1733756010,
  "message": "کد تخفیف وارد شده برای کاربر دیگری می باشد."
}
```

#### ❌ کد تخفیف پیدا نشد

```json
{
  "status": false,
  "time": 1733756020,
  "message": "کد تخفیف وارد شده معتبر نمی باشد."
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84-%D9%85%D9%88%D8%B1%D8%AF-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87%3A-p">- جدول مورد استفاده: `payment_discount` فیلدهای کلیدی: `id`, `title`, `type`, `discount_type`, `discount_value`, `branch`, `user`, `limit`, `status`, `expiration`.
- در صورت `status = 3` تخفیف عملاً غیرفعال محسوب می‌شود.
- کیس‑اِنسنسیتیو بودن بررسی کد با `strtolower($request->code)`.
- مدیریت TTL (انقضا) و محدودیت استفاده در همان جدول انجام می‌شود؛ نیازی به Redis نیست.
- پاسخ‌ها ساختاری ساده و سازگار با سایر اندپوینت‌های B2C دارند: کلیدهای مشترک `status`، `time` و `message` در اختیاری‌ترین حالت.

</div>

# GET /b2c/v1/discount/unsubmit

## Unsubmit Discount Code

این اندپوینت برای بازگرداندن ظرفیت یا اعتبار مصرف‌شده‌ی یک کد تخفیف در سیستم B2C طراحی شده است. در واقع، اگر کد تخفیف اشتباهی استفاده یا تراکنش مربوط به آن لغو شود، با این درخواست می‌توان مقدار `limit` آن را افزایش داد و `status` را در حالت فعال (`1`) قرار داد.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fdiscoun"><div class="endpoint-info"><div>**URL:** `/b2c/v1/discount/unsubmit`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@discountUnSubmit`</div><div>**Authorization:** ندارد (در نسخه فعلی عمومی)</div><div>**Related Table:** `payment_discount`</div><div>**کد بازگرداننده اعتبار:** با id منحصر‌به‌فرد تخفیف فراخوانی می‌شود.</div></div>---

</div>### Query Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D8%AC%D8%A8%D8%A7%D8%B1%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>اجباری</th><th>توضیح</th></tr></thead><tbody><tr><td>id</td><td>integer</td><td>✅</td><td>شناسه رکورد تخفیف در جدول `payment_discount`</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامتر `id` از Query string.</div><div class="flow-arrow">↓</div><div class="flow-item-process">۲. جستجوی رکورد تخفیف در جدول `payment_discount` با شرط id.</div><div class="flow-arrow">↓</div><div class="flow-item-decision">آیا رکورد یافت شد؟ - ✅ بله → ادامه پردازش
- ❌ خیر → بازگرداندن پیام «کد تخفیف معتبر نمی‌باشد»

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">بررسی مقدار `limit`: - اگر `limit == 0` → یعنی تخفیف کاملاً مصرف شده بوده؛ حالا برگردان به حالت اولیه: 
    - `limit = 1`
    - `status = 1` (فعال)
- اگر `limit > 0` → فقط یک واحد افزایش پیدا می‌کند.

</div><div class="flow-arrow">↓</div><div class="flow-item-process">✏️ بروزرسانی جدول `payment_discount` با مقادیر جدید.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ بازگشت پاسخ موفق با: - `status = true`
- `time = timestamp()`

</div></div>---

</div>### Response Samples

#### ✅ بازگردانی موفق اعتبار

```json
{
  "status": true,
  "time": 1733758800
}
```

#### ❌ کد تخفیف یافت نشد

```json
{
  "status": false,
  "time": 1733758810,
  "message": "کد تخفیف معتبر نمی باشد."
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-%D8%AC%D8%AF%D9%88%D9%84-%D8%A7">- عملیات بر روی جدول اصلی `payment_discount` انجام می‌شود.
- مقادیر قابل تغییر: 
    - `limit`: شمارش باقی‌مانده استفاده از تخفیف.
    - `status`: وضعیت فعلی تخفیف (۱ = فعال، ۳ = مصرف شده).
- اگر `limit == 0` یعنی تخفیف قبلاً کامل مصرف شده، پس این متد آن را مجدداً فعال می‌کند.
- در صورت موفقیت هیچ دیتا از رکورد برگردانده نمی‌شود؛ فقط تایید عملیات با timestamp عرضه می‌شود.
- در آینده بهتر است امنیت اضافه شود تا فقط کاربران مجاز یا مدیران سیستم قادر به revert باشند.

</div>

# GET /b2c/v1/wallet/ballance

## Wallet Balance

این اندپوینت برای واکشی موجودی کیف‌پول کاربر در سیستم B2C طراحی شده است. بر اساس `group` (مقدار JWT احراز هویت)، نوع کاربر بین **B2C** و **B2B (colleague)** تشخیص داده می‌شود و سپس موجودی کیف پول از جدول `wallet` با استفاده از کنترلر مالی اصلی (`AccountingController`) فراخوانی می‌گردد.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Info

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fwallet%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/wallet/ballance`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@walletBalance`</div><div>**Middleware:** `authWithJwt`</div><div>**Related Method:** `AccountingController::getBalanceWallet()`</div><div>**Database Table:** `wallet`</div></div>---

</div>### Query Parameters (JWT‑required)

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>group</td><td>string</td><td>✅</td><td>گروه کاربر: `b2c` یا `colleague`. هر مقدار دیگر منجر به خطای 1000 می‌شود.</td></tr><tr><td>operator</td><td>object (JWT)</td><td>✅</td><td>شیء اپراتور احراز هویت‌شده شامل فیلدهای `id`، `ceiling` و `deadline_month`</td></tr></tbody></table>

---

</div>### Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-jwt"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. بررسی ورودی JWT و تعیین نوع کاربر (`$request->group`)</div><div class="flow-arrow">↓</div><div class="flow-item-decision">اگر group مقدار **b2c** داشت → `$userType='b2c'`  
اگر group مقدار **colleague** داشت → `$userType='b2b'`  
در غیر این صورت → بازگشت خطا:</div></div></div>```
{
  "error": {
    "code": 1000,
    "message": "گروه کاربری یافت نشد"
  },
  "meta": { "timestamp": 1733763100 }
}
```

<div class="api-docs" id="bkmrk-%E2%86%93-%F0%9F%93%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D9%85%D9%82%D8%A7%D8%AF%DB%8C%D8%B1-%D8%B2"><div class="flowchart" dir="rtl"><div class="flow-item-decision"></div><div class="flow-arrow">↓</div><div class="flow-item-process">📊 استخراج مقادیر زیر از JWT اپراتور: - `ceiling` → سقف اعتبار (Credit Ceiling)
- `deadline_month` → مهلت تسویه حساب ماهیانه

</div><div class="flow-arrow">↓</div><div class="flow-item-process">💰 صدا زدن متد `AccountingController::getBalanceWallet(type, operator.id, 'website')`</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ بازگرداندن پاسخ با ساختار استاندارد شامل موجودی و متادیتا.</div></div>---

</div>### Response Samples

#### ✅ پاسخ موفق (کاربر B2C)

```json
{
  "payload": {
    "ceiling": 2000000,
    "deadline": "2025-12",
    "wallet": {
      "credit": 5000000,
      "debit": 2500000,
      "balance": 2500000,
      "diagnosis": "creditor"
    }
  },
  "meta": { "timestamp": 1733763100 }
}
```

#### ✅ پاسخ موفق (کاربر colleague/B2B)

```json
{
  "payload": {
    "ceiling": 10000000,
    "deadline": false,
    "wallet": {
      "credit": 3200000,
      "debit": 3500000,
      "balance": -300000,
      "diagnosis": "debtor"
    }
  },
  "meta": { "timestamp": 1733763120 }
}
```

#### ❌ گروه اشتباه

```json
{
  "error": {
    "code": 1000,
    "message": "گروه کاربری یافت نشد"
  },
  "meta": { "timestamp": 1733763130 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### Internal Logic – `AccountingController::getBalanceWallet()`

```php
static function getBalanceWallet($type, $id, $method='branch') {
    // تعیین نوع اپراتور
    if ($type == 'colleague') { $type = 'b2b'; } else { $type = 'erp'; }

    // جمع مجموع بدهکار و بستانکار
    $wallet = DB::table('wallet')
      ->selectRaw('SUM(credit) as credit, SUM(debit) as debit')
      ->where(function ($q) use ($type, $id, $method) {
          if ($method == 'branch')
              $q->where('operator_type', 'erp')->where('branch', $id);
          else
              $q->where('operator_type', $type)->where('operator', $id);
      })
      ->where('status', '!=', 2)
      ->first();

    return [
      "credit" => (int)$wallet->credit,
      "debit" => (int)$wallet->debit,
      "balance" => ($wallet->credit - $wallet->debit),
      "diagnosis" => ($wallet->credit - $wallet->debit) >= 0
        ? (($wallet->credit - $wallet->debit) == 0 ? 'neutral' : 'creditor')
        : 'debtor'
    ];
  }
```

<div class="api-docs" id="bkmrk--2">---

</div>### Technical Notes

<div class="api-docs" id="bkmrk-%D9%85%D9%86%D8%A8%D8%B9-%D8%AF%D8%A7%D8%AF%D9%87%3A-%D8%AC%D8%AF%D9%88%D9%84-wall">- منبع داده: جدول `wallet` با فیلدهای `credit` و `debit`.
- تمام رکوردهایی با وضعیت `status != 2` در محاسبه لحاظ می‌شوند.
- تشخیص نوع حساب: 
    - **creditor**: بستانکار (balance &gt; 0)
    - **debtor**: بدهکار (balance &lt; 0)
    - **neutral**: خنثی (balance = 0)
- در پاسخ نهایی، تمام مقادیر به ریال هستند و integer برگردانده می‌شوند.
- **نکته مهم:** در پیاده‌سازی فعلی خطای تایپی وجود دارد. خط زیر در متد AccountingController باید با `==` تصحیح گردد: ```
    if ($type = 'colleague')
    ```
    
    ✅ باید باشد: ```
    if ($type == 'colleague')
    ```
    
    در غیر این صورت همیشه مقدار `'colleague'` ست شده و مسیر شرطی اشتباه عمل می‌کند.

</div>

# POST /b2c/v1/wallet/credit

## POST /b2c/v1/wallet/credit

این اندپوینت به کاربران B2C و همکاران (B2B/Colleague) اجازه می‌دهد تا از طریق درگاه پرداخت فعال دفتر خود، مبلغی را به کیف‌پولشان واریز کنند. نتیجه نهایی تولید لینک پرداخت منحصر‌به‌فرد با ساختار `/p/{slug}` است.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fwallet%2F"><div class="endpoint-info"><div>**URL:** `/b2c/v1/wallet/credit`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** `CreditDebitController@walletCredit`</div><div>**Middleware:** `authWithJwt`</div><div>**Related Tables:** `pays`, `payment_gateway`, `gateways`, `office_config`, `offices`</div></div>---

</div>### Request Body Parameters

<div class="api-docs" id="bkmrk-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA"><table class="schema-table" dir="rtl"><thead><tr><th>پارامتر</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>price</td><td>integer</td><td>✅</td><td>مبلغ واریز (حداقل 10000 ریال)</td></tr><tr><td>driver</td><td>string</td><td>❌</td><td>اختیاری؛ نوع درگاه بانکی (مثل `sep` یا `behpardakht`)</td></tr><tr><td>return\_link</td><td>string (URL)</td><td>❌</td><td>آدرس بازگشت پس از پرداخت موفق</td></tr><tr><td>group</td><td>enum('b2c','colleague','b2b')</td><td>✅</td><td>گروه کاربر برای تنظیم نوع حساب مالی</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه دفتر کاربر در سیستم</td></tr><tr><td>operator</td><td>object (JWT)</td><td>✅</td><td>شامل اطلاعات کاربر جاری (id, colleague\_id, ...)</td></tr></tbody></table>

---

</div>### Validation &amp; Logic Flow

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامترها از بدنه درخواست</div><div class="flow-arrow">↓</div><div class="flow-item-decision">بررسی فیلد **price**: - ❌ در صورت خالی بودن → پاسخ با HTTP 422 و پیام «لطفا تمامی فیلدها را پر کنید»
- ❌ اگر مبلغ &lt; 10000 → پاسخ با HTTP 422 و پیام «حداقل مبلغ قابل پرداخت 10000 ریال است»
- ✅ در غیر این‌صورت → ادامه

</div><div class="flow-arrow">↓</div><div class="flow-item-process">⚙️ ۲. فراخوانی `Functions::getGatewayConfig(branch, driver)` برای یافتن درگاه فعال: - اگر درگاه به‌صورت مستقیم انتخاب شود (driver)، از جدول `gateways` بررسی می‌گردد.
- در غیر این‌صورت مقدار پیش‌فرض از جدول `office_config` با کلید `DEFAULT_BANKING_GATEWAY` استخراج می‌شود.
- ❌ در صورت نبودن درگاه فعال → پاسخ 400 با پیام «درگاه پرداخت فعال یافت نشد»

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">تعیین نوع حساب بر اساس group: - **b2c** → userType='b2c', objectType='customer', functorType='customer'
- **colleague یا b2b** → userType='b2b', objectType='colleague', functorType='colleague\_user'
- ❌ سایر موارد → پاسخ 400 (پیام «گروه کاربری یافت نشد»)

</div><div class="flow-arrow">↓</div><div class="flow-item-process">🧾 ۳. تولید رکورد جدید در جدول `pays` با مشخصات: - type='receive'
- moeen=14 (حساب معین برای شارژ کیف پول)
- type\_pay='online'
- currency\_amount = مبلغ درخواست شده
- status=1 (در انتظار پرداخت)
- year = `StaticController::getYearFinancial()`

</div><div class="flow-arrow">↓</div><div class="flow-item-process">💳 ۴. ایجاد رکورد در جدول `payment_gateway`: - `object_type='pay'`, `object={id_pay}`
- مقدار `slug` منحصر به فرد با `Functions::generateSlugUnique(...)`
- تعیین لینک بازگشت (return\_link) و شناسه درایو درگاه پرداخت

</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۵. پاسخ موفق (HTTP 201): شامل اطلاعات پرداخت و لینک پرداخت آماده برای کاربر.</div></div>---

</div>### 🎯 Response Samples

#### ✅ موفق

```json
{
  "payload": {
    "status": "payment_link",
    "amount": 250000,
    "url": "https://agency-domain.com/p/ABX21SD2",
    "slug": "ABX21SD2",
    "pay_id": 35876,
    "gateway_id": 44
  },
  "meta": { "timestamp": 1733765953 }
}
```

#### ❌ فیلد ناقص

```json
{
  "error": {
    "code": 1000,
    "message": "لطفا تمامی فیلد ها را پر کنید."
  },
  "meta": { "timestamp": 1733765958 }
}
```

#### ❌ درگاه پیدا نشد

```json
{
  "error": {
    "code": 1000,
    "message": "درگاه پرداخت فعال یافت نشد"
  }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### ساختار جدول‌های مرتبط

<div class="api-docs" id="bkmrk-%D8%AC%D8%AF%D9%88%D9%84-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7%DB%8C-%DA%A9%D9%84%DB%8C%D8%AF%DB%8C-%D9%86"><table class="schema-table" dir="rtl"><thead><tr><th>جدول</th><th>فیلدهای کلیدی</th><th>نقش</th></tr></thead><tbody><tr><td>`pays`</td><td>type, serial, object\_type, object, currency\_amount, functor\_type, functor\_account, status</td><td>ثبت پرداخت در انتظار برای کیف پول</td></tr><tr><td>`payment_gateway`</td><td>slug, object\_type, object, drive, amount, return</td><td>رهگیری پرداخت و ایجاد لینک پرداخت</td></tr><tr><td>`gateways`</td><td>id, bank, drive, branch, data(JSON)</td><td>مشخصات اتصال به درگاه بانکی</td></tr><tr><td>`office_config`</td><td>office, key='DEFAULT\_BANKING\_GATEWAY', value</td><td>تعیین درگاه پیش‌فرض شعبه</td></tr></tbody></table>

---

</div>### توضیح توابع وابسته

<div class="api-docs" id="bkmrk-functions%3A%3Agetgatewa">- **Functions::getGatewayConfig($branch, $drive)**: در صورتی که درگاه مشخص نشده باشد، مقدار پیش‌فرض (DEFAULT\_BANKING\_GATEWAY) را از پیکربندی دفتر می‌خواند.
- **StaticController::getYearFinancial()**: سال مالی را بر اساس تاریخ شمسی برمی‌گرداند (مثلاً 1404).
- **Functions::generateSlugUnique('payment\_gateway', 8)**: رشته یکتا برای URL پرداخت ایجاد می‌کند.

---

</div>### نکات فنی

<div class="api-docs" id="bkmrk-%D8%AD%D8%AF%D8%A7%D9%82%D9%84-%D9%85%D8%A8%D9%84%D8%BA-%D9%85%D8%AC%D8%A7%D8%B2-%D9%BE%D8%B1%D8%AF%D8%A7">- حداقل مبلغ مجاز پرداخت 10 000 ریال است.
- خطای عمومی برای group ناشناخته با پیام «گروه کاربری یافت نشد» بازگردانده می‌شود.
- **status** پرداخت در جدول `pays` مقدار ۱ (در انتظار تأیید) دارد تا پس از بازگشت درگاه تغییر یابد.
- لینک نهایی پرداخت از `offices.short_domain` ساخته می‌شود.

</div>

# GET /b2c/v1/ledger-account

## ledger-account

این اندپوینت برای واکشی حساب کل دفتر همکار (colleague) در سیستم مالی B2C به‌کار می‌رود و از داده‌های تجمیع‌شده در Redis و خروجی متد `ColleaguesController::colleagueLedgerAccounts` استفاده می‌کند تا هم جزئیات صورت‌حساب‌ها (ledger items) و هم مانده‌ی کل حساب را برگرداند.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fledger-"><div class="endpoint-info"><div>**URL:** `/b2c/v1/ledger-account`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `CreditDebitController@ledgerAccount`</div><div>**Middleware:** `authWithJwt`</div><div>**Dependencies:** `ColleaguesController@colleagueLedgerAccounts`, `Redis`, `Morilog\Jalali\Jalalian`</div></div>---

</div>### پارامترهای ورودی

<div class="api-docs" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD"><table class="schema-table" dir="rtl"><thead><tr><th>نام</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>operator (JWT)</td><td>object</td><td>✅</td><td>حاوی اطلاعات کاربر احراز شده، خصوصاً `colleague_id`</td></tr><tr><td>branch</td><td>integer</td><td>✅</td><td>شناسه دفتر یا شعبه کاربر</td></tr></tbody></table>

---

</div>### منطق پردازشی (Flowchart)

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت درخواست و استخراج شناسه همکار از JWT (`operator->colleague_id`)</div><div class="flow-arrow">↓</div><div class="flow-item-process">⚙️ ۲. ساخت پیش‌فرض‌های JSON جستجو (فیلتر زمانی و پارامترهای گزارش): - `from` = یک ماه قبل به تاریخ شمسی (با `Jalalian::now()->subMonths(1)`)
- `to` = تاریخ امروز
- `status` = "0"
- `lbalance` = false

</div><div class="flow-arrow">↓</div><div class="flow-item-process">🧩 ۳. تزریق داده‌ی JSON تولیدشده در `$request['json']` و `$request['id']` سپس ایجاد نمونه از **ColleaguesController** و فراخوانی متد **colleagueLedgerAccounts()** برای واکشی جزئیات تراکنش‌ها.</div><div class="flow-arrow">↓</div><div class="flow-item-process">🗄️ ۴. واکشی مانده کلی حساب از Redis با کلید: `colleagues:general_billing:all{colleague_id}` در صورت موجود بودن داده، پارس آن با `json_decode()`.</div><div class="flow-arrow">↓</div><div class="flow-item-decision">بررسی وجود داده در Redis: - ✅ اگر موجود بود → ساخت آرایه شامل debit, credit, balance, diagnosis, documents از داده Redis
- ❌ اگر نبود → مقادیر صفر پیش‌فرض تنظیم می‌شود

</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۵. ساخت پاسخ JSON شامل لیست آیتم‌های دفتر کل و جزئیات موجودی: `{ items, payload, meta.timestamp }`</div></div>---

</div>### ساختار خروجی JSON

#### پاسخ موفق

```json
{
  "items": [
    {
      "serial_id": "1404-22",
      "datetime": "1404/09/01 00:00:00",
      "credit": 500000,
      "debit": 0,
      "description": { "html": "واریز بابت فاکتور #22", "text": "واریز بابت فاکتور #22" },
      "details": { "type": { "title": "دریافت نقدی" } }
    }
  ],
  "payload": {
    "debit": 1250000,
    "credit": 500000,
    "balance": -750000,
    "diagnosis": "بدهکار",
    "documents": 17
  },
  "meta": { "timestamp": 1733768901 }
}
```

#### پاسخ بدون داده

```json
{
  "items": [],
  "payload": {
    "debit": 0,
    "credit": 0,
    "balance": 0,
    "diagnosis": 0,
    "documents": 0
  },
  "meta": { "timestamp": 1733768903 }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### نکات فنی و باگ‌های شناسایی‌شده

<div class="api-docs" id="bkmrk-%D8%A7%DA%AF%D8%B1-redis-%DA%A9%D9%84%DB%8C%D8%AF-colle">- اگر Redis کلید `colleagues:general_billing:all{id}` را نداشته باشد، پاسخ همواره با موجودی صفر ست می‌شود؛ بهتر است در این حالت سرویس `colleagueLedgerAccounts` برای جمع‌آوری داده‌ی جدید اجرا شود.
- در نسخه فعلی، فیلد زمان‌ها و فرمت تاریخی بر اساس **تاریخ شمسی** با Morilog\\Jalali تنظیم شده اما در سطح پایگاه داده (Carbon) تاریخ میلادی ذخیره می‌شود — نیاز به تطبیق هنگام sort در کلاینت دارد.
- پاسخ دارای آرایه‌ای از آیتم‌های Ledger است که به‌صورت مستقیم از کنترلر همکار (`colleagueLedgerAccounts`) گرفته می‌شوند؛ در صورت تغییر ساختار آن متد، این خروجی نیز باید به‌روزرسانی شود.
- بازگشت داده درون `meta.timestamp` با `time()` انجام می‌شود (یونیکس تایم).

</div>

# GET /b2c/v1/articles

## GET /b2c/v1/articles

این اندپوینت برای دریافت فهرست مقالات در سیستم B2C استفاده می‌شود. امکان فیلتر بر اساس دسته‌بندی‌ها، تگ‌ها و موقعیت‌ها (places) و همچنین مرتب‌سازی بر اساس امتیاز (score) یا تعداد بازدید (views) فراهم شده است. در حالت عادی خروجی صفحه‌بندی‌شده برمی‌گردد، اما در حالت مرتب‌سازی خاص، داده‌ها به صورت محدود و غیر صفحه‌بندی بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Farticle"><div class="endpoint-info"><div>**URL:** `/b2c/v1/articles`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1ArticleController@index`</div><div>**Middleware:** `web` (بدون JWT)</div><div>**Model:** `Article`</div><div>**Resource:** `ArticleResource`</div></div>---

</div>### پارامترهای Query قابل استفاده

<div class="api-docs" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7"><table class="schema-table" dir="rtl"><thead><tr><th>نام پارامتر</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>`branch`</td><td>integer</td><td>✅</td><td>شناسه دفتر یا شعبه‌ای که مقاله در آن تعریف شده است</td></tr><tr><td>`categories`</td><td>array (JSON)</td><td>❌</td><td>لیست شناسه‌های دسته‌بندی برای فیلتر (مثلاً \[1,5,7\])</td></tr><tr><td>`tags`</td><td>array (JSON)</td><td>❌</td><td>لیست شناسه‌های تگ‌ها برای فیلتر</td></tr><tr><td>`places`</td><td>array (JSON)</td><td>❌</td><td>لیست شناسه‌ نواحی مکانی مرتبط با مقاله</td></tr><tr><td>`sortByScore`</td><td>boolean</td><td>❌</td><td>مرتب‌سازی بر اساس بیشترین امتیاز (برترین ۱۰ مقاله)</td></tr><tr><td>`sortByViews`</td><td>boolean</td><td>❌</td><td>مرتب‌سازی بر اساس بیشترین بازدید (۶ مقاله برتر)</td></tr></tbody></table>

---

</div>### منطق پردازشی و جریان داده (Flowchart)

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامترها از Query (categories, tags, places, sortBy…)</div><div class="flow-arrow">↓</div><div class="flow-item-process">⚙️ ۲. ایجاد Query Builder روی مدل `Article` با شرط اولیه `branch = $request->branch`</div><div class="flow-arrow">↓</div><div class="flow-item-decision">🔍 ۳. بررسی فیلترها: - اگر `categories` ارسال شده → `orWhereJsonContains('categories', $value)` در حلقه
- اگر `tags` ارسال شده → `orWhereJsonContains('tags', $value)`
- اگر `places` ارسال شده → `orWhereJsonContains('places', $value)`

</div><div class="flow-arrow">↓</div><div class="flow-item-decision">✳️ ۴. اعمال مرتب‌سازی‌ها: - در صورت وجود `sortByScore`: مرتب‌سازی بر اساس امتیاز نزولی و محدودیت به ۱۰ نتیجه
- در صورت وجود `sortByViews`: مرتب‌سازی بر اساس بازدید نزولی و محدودیت به ۶ نتیجه

</div><div class="flow-arrow">↓</div><div class="flow-item-process">📑 ۵. خروجی داده‌ها: - اگر `sortByScore` یا `sortByViews` فعال باشد → خروجی `get()` بدون pagination
- در غیر این صورت → خروجی `paginate(15)`

</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۶. ساخت پاسخ JSON شامل وضعیت، زمان، داده‌ها و لینک‌های صفحه‌بندی.</div></div>---

</div>### 📦 ساختار پاسخ JSON

#### پاسخ موفق

```json
{
  "status": true,
  "time": 1733799551,
  "data": [
    {
      "id": 245,
      "title": "راهنمای سفر مشهد",
      "excerpt": "در این مقاله به جاذبه‌های گردشگری...",
      "cover": "https://cdn.site.com/uploads/article245.jpg",
      "views": 932,
      "score": 4.8,
      "categories": [5, 7],
      "tags": [12, 98],
      "created_at": "2025-11-22T10:00:00Z"
    },
    ...
  ],
  "links": {
    "first": "https://api.domain.com/b2c/v1/articles?page=1",
    "last": "https://api.domain.com/b2c/v1/articles?page=15",
    "prev": null,
    "next": "https://api.domain.com/b2c/v1/articles?page=2"
  }
}
```

#### 🔹 پاسخ در حالت مرتب‌سازی (بدون pagination)

```json
{
  "status": true,
  "time": 1733799553,
  "data": [ { ... }, { ... }, ... ],
  "links": false
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### نکات فنی

<div class="api-docs" id="bkmrk-%D9%81%DB%8C%D9%84%D8%AA%D8%B1%D9%87%D8%A7-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7">- فیلترها با استفاده از `orWhereJsonContains` اعمال می‌شوند، بنابراین اگر مقاله شامل هرکدام از مقادیر داده‌شده باشد در نتایج بازمی‌گردد.
- رتبه‌بندی بر اساس `score` یا `views` باعث بای‌پس شدن pagination می‌شود تا فقط n نتیجه برتر برگردد.
- در خروجی از `ArticleResource` برای ساختاردهی داده‌ها استفاده شده است.
- پارامتر `branch` الزامی است و باید در Query لحاظ شود؛ در غیر این صورت هیچ رکوردی پیدا نخواهد شد.

---

</div>### 🚀 پیشنهادات بهبود برای توسعه آینده

<div class="api-docs" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-limit">- افزودن پارامتر `limit` دلخواه برای sortByScore و sortByViews جهت کنترل خروجی.
- بهینه‌سازی Query با استفاده از `->whereIn()` در صورتی که فیلترها زیاد تکرار شوند تا از تکرار `orWhereJsonContains` جلوگیری شود.
- افزودن cache با کلید `articles:list:{branch}:{hash_of_params}` جهت افزایش سرعت واکشی مقالات محبوب.

</div>

# GET /b2c/v1/articles/{article}

## GET /b2c/v1/articles/{article}

این اندپوینت برای مشاهده جزئیات یک مقاله خاص در سامانه‌ی B2C طراحی شده است. ورودی شامل شناسه یا slug مقاله است که به‌صورت Model Binding لاراول به `Article` تبدیل می‌شود. نتیجه در قالب `ArticleResource` بازگردانده می‌شود تا داده‌ها به شکل استاندارد JSON ارائه شود.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Farticle"><div class="endpoint-info"><div>**URL:** `/b2c/v1/articles/{article}`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1ArticleController@show`</div><div>**Middleware:** `web` (بدون JWT)</div><div>**Model:** `Article`</div><div>**Resource:** `ArticleResource`</div></div>---

</div>### پارامترهای مسیر (Path Parameters)

<div class="api-docs" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD"><table class="schema-table" dir="rtl"><thead><tr><th>نام</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>`article`</td><td>integer|string</td><td>✅</td><td>شناسه عددی یا slug مقاله؛ به‌صورت خودکار در لاراول از طریق Route Model Binding واکشی می‌شود.</td></tr></tbody></table>

---

</div>### منطق اجرای متد (Flow Logic)

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامتر مسیر `{article}` و Resolve به مدل `Article` توسط لاراول.</div><div class="flow-arrow">↓</div><div class="flow-item-process">🧩 ۲. بسته‌بندی داده مقاله در قالب `ArticleResource` برای تبدیل فیلدها به JSON ساخت‌یافته.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۳. بازگرداندن پاسخ JSON شامل داده، وضعیت موفق و زمان فراخوانی.</div></div>---

</div>### پاسخ موفق (Success Response)

```json
{
  "status": true,
  "time": 1733802321,
  "data": {
    "id": 245,
    "title": "راهنمای سفر به تبریز",
    "excerpt": "بررسی بهترین جاذبه‌های گردشگری …",
    "body": "
```

در این مقاله به بررسی مکان‌های دیدنی ...

```json
",
    "cover": "https://cdn.site.com/uploads/articles/245.jpg",
    "views": 537,
    "score": 4.6,
    "categories": [1, 5],
    "tags": [12, 33, 70],
    "places": [18],
    "created_at": "2025-11-29T08:00:00Z",
    "updated_at": "2025-12-05T14:10:00Z"
  }
}
```

#### خطاهای احتمالی

<div class="api-docs" id="bkmrk-%DA%A9%D8%AF-%D9%88%D8%B6%D8%B9%DB%8C%D8%AA-%D8%B4%D8%B1%D8%B7-%D9%88%D9%82%D9%88%D8%B9-%D8%AA%D9%88"><table class="schema-table" dir="rtl"><thead><tr><th>کد وضعیت</th><th>شرط وقوع</th><th>توضیح</th></tr></thead><tbody><tr><td>404</td><td>هنگامی که شناسه‌ی مقاله در پایگاه داده یافت نشود.</td><td>پاسخ پیش‌فرض خطای ModelNotFound</td></tr></tbody></table>

---

</div>### 📘 توضیحات اجرایی برای توسعه‌دهنده (Dev Notes)

<div class="api-docs" id="bkmrk-binding-%D8%A7%D8%AA%D9%88%D9%85%D8%A7%D8%AA%DB%8C%DA%A9-%D9%84%D8%A7%D8%B1">- Binding اتوماتیک لاراول ارجاع `{article}` را بر اساس route parameter انجام می‌دهد—درصورت داشتن ستون slug می‌توان آن را در مدل `Article::getRouteKeyName()` تعریف کرد.
- Resource در اینجا می‌تواند شامل روابط `author`، `comments` یا متادیتا باشد، بسته به پیاده‌سازی `ArticleResource`.
- پارامتر `time` برای نمایش زمان پاسخ API (به‌صورت Unix timestamp) افزوده شده است.

</div>

# GET /b2c/v1/categories

## GET /b2c/v1/categories

این اندپوینت برای دریافت فهرست دسته‌بندی‌ها (Categories) طراحی شده است. داده‌ها از جدول `categories` گرفته می‌شوند و امکان فیلتر بر اساس نوع (`type`) و شعبه (`branch`) وجود دارد. فقط دسته‌بندی‌های سطح اول (یعنی مواردی که `main` آن‌ها `NULL` است) بازگردانده می‌شوند.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Fcategor"><div class="endpoint-info"><div>**URL:** `/b2c/v1/categories`</div><div>**Method:** <span class="method-get">GET</span></div><div>**Controller:** `V1CategoryController@index`</div><div>**Middleware:** `web` (بدون JWT)</div><div>**Model:** `Category`</div><div>**Resource:** `CategoryResource`</div></div>---

</div>### پارامترهای ورودی (Query Parameters)

<div class="api-docs" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD"><table class="schema-table" dir="rtl"><thead><tr><th>نام</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>`branch`</td><td>integer</td><td>✅</td><td>شناسه دفتر یا شعبه‌ای که دسته‌بندی‌ها به آن تعلق دارند</td></tr><tr><td>`type`</td><td>string</td><td>❌</td><td>نوع دسته‌بندی (مثلاً `article`، `product`، `destination`) برای فیلتر اختیاری</td></tr><tr><td>`page`</td><td>integer</td><td>❌</td><td>شماره صفحه برای صفحه‌بندی (به‌صورت پیش‌فرض ۱)</td></tr></tbody></table>

---

</div>### منطق پردازشی (Process Flow)

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1%D9%87"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت پارامترهای `branch`، `type` و `page` از Query</div><div class="flow-arrow">↓</div><div class="flow-item-process">⚙️ ۲. اجرای کوئری روی مدل `Category`:</div>- اعمال شرط `where('branch', branch)`
- در صورت وجود `type` → `where('type', type)`
- نمایش فقط دسته‌های سطح اول (`whereNull('main')`)

<div class="flow-arrow">↓</div><div class="flow-item-process">📑 ۳. صفحه‌بندی خروجی با `paginate(15)`.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۴. بازگرداندن JSON شامل وضعیت، زمان، داده‌ها و لینک‌های صفحه‌بندی.</div></div>---

</div>### 📦 ساختار پاسخ JSON

#### پاسخ موفق

```json
{
  "status": true,
  "time": 1733806124,
  "data": [
    {
      "id": 12,
      "title": {
        "fa": "مقاصد محبوب",
        "en": "Popular Destinations"
      },
      "slug": "popular-destinations",
      "type": "article",
      "main": null,
      "cover": "https://cdn.site.com/uploads/categories/12.jpg",
      "created_at": "2025-11-25T07:10:00Z"
    },
    {
      "id": 13,
      "title": {
        "fa": "تورهای داخلی",
        "en": "Domestic Tours"
      },
      "slug": "domestic-tours",
      "type": "tour",
      "main": null,
      "cover": "https://cdn.site.com/uploads/categories/13.jpg"
    }
  ],
  "links": {
    "first": "https://api.domain.com/b2c/v1/categories?page=1",
    "last": "https://api.domain.com/b2c/v1/categories?page=10",
    "prev": null,
    "next": "https://api.domain.com/b2c/v1/categories?page=2"
  }
}
```

#### پاسخ خطا (نمونه)

```json
{
  "status": false,
  "time": 1733806125,
  "message": "پارامتر branch ارسال نشده است."
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### نکات فنی و اجرایی

<div class="api-docs" id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85-%D8%AF%D8%B3%D8%AA%D9%87%E2%80%8C%D9%87%D8%A7-%D8%A7%D8%B2-%D8%AC%D8%AF%D9%88%D9%84">- تمام دسته‌ها از جدول `categories` فیلتر می‌شوند و تنها سطح ریشه (main = NULL) نمایش داده می‌شود.
- در خروجی داده‌های هر رکورد توسط `CategoryResource` قالب‌بندی شده‌اند.
- صفحه‌بندی بر اساس ۱۵ آیتم در هر صفحه انجام می‌شود و لینک‌ها توسط Laravel Paginator تولید می‌شوند.
- پارامتر `branch` اجباری است؛ در صورت نبود، خروجی خالی یا خطای منطقی بازگردانده می‌شود.
- پاسخ شامل فیلد کمکی `time` جهت هماهنگی با دیگر APIهای B2C است.

---

</div>### 🚀 پیشنهاد بهبود آینده

<div class="api-docs" id="bkmrk-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%D9%82%D8%A7%D8%A8%D9%84%DB%8C%D8%AA-includ">- افزودن قابلیت `includeChildren=true` برای لود درختی زیردسته‌ها همراه سطح اول.
- امکان تعیین `limit` دلخواه در Query برای انعطاف بیشتری در بخش Front.
- افزودن پارامتر `sort=popular|alphabetical` جهت کنترل ترتیب خروجی.

</div>

# POST /b2c/v1/articles/{id}/views

## POST /b2c/v1/articles/{id}/views

این اندپوینت برای افزایش شمارش بازدید مقاله استفاده می‌شود. هنگام نمایش جزئیات مقاله در سمت کاربر (مانند صفحه‌ی article detail)، فرانت‌اند می‌تواند پس از بارگذاری موفق مقاله، این متد را برای ثبت بازدید فراخوانی کند. افزایش بازدید مستقیماً در دیتابیس انجام می‌شود و مقدار جدید بازگردانده می‌شود.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoint Information

<div class="api-docs" id="bkmrk-url%3A-%2Fb2c%2Fv1%2Farticle"><div class="endpoint-info"><div>**URL:** `/b2c/v1/articles/{id}/views`</div><div>**Method:** <span class="method-post">POST</span></div><div>**Controller:** `V1ArticleController@incrementViews`</div><div>**Middleware:** `web` (بدون JWT)</div><div>**Model:** `Article`</div><div>**Action:** افزایش مقدار `views` در ستون پایگاه داده</div></div>---

</div>### پارامترهای مسیر (Path Parameter)

<div class="api-docs" id="bkmrk-%D9%86%D8%A7%D9%85-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%AA%D9%88%D8%B6%DB%8C%D8%AD"><table class="schema-table" dir="rtl"><thead><tr><th>نام</th><th>نوع</th><th>الزامی</th><th>توضیح</th></tr></thead><tbody><tr><td>`id`</td><td>integer</td><td>✅</td><td>شناسه عددی مقاله که می‌خواهیم بازدید آن را افزایش دهیم</td></tr></tbody></table>

---

</div>### بدنه درخواست (Request Body)

```json
{}  
// بدون نیاز به بدنه خاص؛ صرفاً فراخوانی ساده POST کافی است.

```

<div class="api-docs" id="bkmrk--1">---

</div>### خروجی موفق (Success Response)

```json
{
  "status": true,
  "time": 1733810253,
  "views": 121
}
```

#### در صورت بروز خطا (Error Response)

```json
{
  "message": "No query results for model [App\\Models\\Article] 9999",
  "exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",
  "status_code": 404
}
```

<div class="api-docs" id="bkmrk--2">---

</div>### فلوچارت منطق عملکرد (Flowchart)

<div class="api-docs" id="bkmrk-%F0%9F%93%A5-%DB%B1.-%D8%AF%D8%B1%DB%8C%D8%A7%D9%81%D8%AA-%D8%B4%D9%86%D8%A7%D8%B3%D9%87-%7Bi"><div class="flowchart" dir="rtl"><div class="flow-item">📥 ۱. دریافت شناسه `{id}` از مسیر درخواست</div><div class="flow-arrow">↓</div><div class="flow-item-process">🔍 ۲. اجرای `Article::findOrFail($id)`  
در صورت نبود مقاله ⇒ خطای ۴۰۴ با پیام ModelNotFoundException برگردانده می‌شود.</div><div class="flow-arrow">↓</div><div class="flow-item-process">➕ ۳. فراخوانی متد `$article->increment('views')`  
مقدار ستون `views` در پایگاه داده یک واحد افزایش می‌یابد.</div><div class="flow-arrow">↓</div><div class="flow-item-success">✅ ۴. پاسخ JSON شامل وضعیت، زمان (timestamp) و مقدار جدید `views` برگردانده می‌شود.</div></div>---

</div>### نکات فنی برای توسعه‌دهنده (Developer Notes)

<div class="api-docs" id="bkmrk-%D8%A7%D8%B2-%D8%AA%D8%A7%D8%A8%D8%B9-eloquent-inc">- از تابع Eloquent `increment()` برای جلوگیری از race condition در محیط‌های هم‌زمان (concurrent) استفاده شده است.
- در صورت نیاز به جلوگیری از افزایش تکراری در همان session، توصیه می‌شود کنترل فرانت‌اند (یا cookie flag) اضافه شود.
- فیلد `views` باید از نوع `unsignedBigInteger` باشد تا در بازدید بالا overflow نکند.
- بهتر است عملیات آماری (views امروز، بازدید یکتا و …) در آینده با Redis counter یا جدول جداگانه مدیریت شود.

</div>

# RESOURCE /b2c/v1/travel_requests

## Travel Requests API

مجموعه اندپوینت‌های `/b2c/v1/travel_requests` برای ثبت، مشاهده، و مدیریت درخواست‌های سفر طراحی شده است. این مجموعه از ساختار `Route::resource` لاراول استفاده می‌کند و زیر مجموعه‌ای از کنترلر `TravelRequestsController` می‌باشد. تمام مسیرها از طریق `middleware: authWithJwt` محافظت می‌شوند.

<div class="api-docs" id="bkmrk-">---

</div>### Endpoints Overview

<div class="api-docs" id="bkmrk-method-endpoint-acti"><table class="schema-table" dir="rtl"><thead><tr><th>Method</th><th>Endpoint</th><th>Action</th><th>Description</th></tr></thead><tbody><tr><td>GET</td><td>/b2c/v1/travel\_requests</td><td>index</td><td>لیست درخواست‌های سفر با فیلتر و صفحه‌بندی</td></tr><tr><td>POST</td><td>/b2c/v1/travel\_requests</td><td>store</td><td>ثبت درخواست جدید سفر</td></tr><tr><td>GET</td><td>/b2c/v1/travel\_requests/{id}</td><td>show</td><td>دریافت جزئیات یک درخواست مشخص</td></tr><tr><td>PUT/PATCH</td><td>/b2c/v1/travel\_requests/{id}</td><td>update</td><td>ویرایش اطلاعات درخواست</td></tr><tr><td>DELETE</td><td>/b2c/v1/travel\_requests/{id}</td><td>destroy</td><td>حذف درخواست</td></tr><tr><td>POST</td><td>/b2c/v1/travel\_requests/{id}/change-status</td><td>changeStatus</td><td>تغییر وضعیت (status) با امکان ثبت reference</td></tr></tbody></table>

---

</div>### ۱. لیست درخواست‌ها — `GET /b2c/v1/travel_requests`

<div class="api-docs" id="bkmrk-%D8%B5%D9%81%D8%AD%D9%87%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C%3A-%D8%AF%D8%B1-%D9%81%DB%8C%D9%84%D8%AF-p">- صفحه‌بندی: در فیلد `paginate.length` (پیش‌فرض 30)، `paginate.start` (پیش‌فرض 0)
- فیلترهای پشتیبانی‌شده: 
    - `method` (accommodation, flight, tour...)
    - `submethod`
    - `status`
    - `payment_status`
    - `operator_id`
- دسترسی به داده‌ها محدود به گروه کاربر است: 
    - **B2C/Agent:** فقط درخواست‌های خودش
    - **B2B/Colleague:** درخواست‌های اپراتور جاری
    - **Base/B2E:** از فیلد `requester_id` در ورودی

</div>#### پاسخ نمونه

```json
{
  "items": [
    {
      "id": 45,
      "title": "اصفهان به مشهد هتل آوازه مشهد از ‎2025/12/14‎ تا ‎2025/12/18‎",
      "method": "accommodation",
      "budget": 5000000,
      "status": 1,
      "operator_group": "b2c",
      "origin": { "id": 37, "fa_name": "اصفهان", "en_name": "Isfahan" },
      "destination": { "id": 396, "fa_name": "مشهد", "en_name": "Mashhad" },
      "accommodation": { "title": { "fa": "هتل آوازه مشهد" }, "category": { "title": "hotel" } },
      "details": { "rooms": 2, "adults": 3 },
      "requester_id": { "id": 12, "first_name": "علیرضا", "last_name": "احمدی" }
    }
  ],
  "meta": {
    "timestamp": 1733821800,
    "table": { "total": 25, "per_page": 30, "current_page": 1, "last_page": 1 }
  }
}
```

<div class="api-docs" id="bkmrk--1">---

</div>### ۲. ایجاد درخواست — `POST /b2c/v1/travel_requests`

در این درخواست، اپراتور با استفاده از JWT خود به عنوان `operator`، رکوردی جدید در جدول `travel_requests` ایجاد می‌کند.

#### پارامترهای ورودی (JSON Body)

```json
{
  "method": "accommodation",
  "origin": {"id": 37},
  "destination": {"id": 396},
  "accommodation": {"id": 52, "type": "accommodation"},
  "from_date": "2025-12-14",
  "to_date": "2025-12-18",
  "budget": 5000000,
  "cost_center": 8,
  "trip_type": "business",
  "description": "سفر کاری به مشهد",
  "details": { "adults": 3, "children": 1 }
}
```

<div class="api-docs" id="bkmrk-method-%D8%A7%D9%84%D8%B2%D8%A7%D9%85%DB%8C-%D8%A7%D8%B3%D8%AA-%28%D9%85">- `method` الزامی است (مانند accommodation, flight, tour).
- اگر فیلد `accommodation.type = "destination"` باشد، مقدار آن جایگزین مقصد می‌شود.
- در تمام حالات، branch و group از JWT استخراج می‌شوند.

</div>#### خروجی موفق (201 Created)

```json
{
  "payload": {
    "id": 45,
    "title": "اصفهان به مشهد هتل آوازه مشهد از ‎2025/12/14‎ تا ‎2025/12/18‎",
    "budget": 5000000,
    "method": "accommodation"
  },
  "meta": { "timestamp": 1733821800 }
}
```

<div class="api-docs" id="bkmrk--2">---

</div>### ۳. مشاهده جزئیات — `GET /b2c/v1/travel_requests/{id}`

کلیه روابط مرتبط (origin، destination، accommodation، requester و غیره) از جداول مجزا خوانده می‌شوند.

<div class="api-docs" id="bkmrk-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA%E2%80%8C%DA%A9%D9%86%D9%86%D8%AF%D9%87-%28b2c-%E2%86%92">- درخواست‌کننده (B2C → customers، B2B → colleagues).
- رفرنس فاکتور در صورت اتصال به `factors` با offest +10000 بازگردانده می‌شود.
- عنوان اصلی مبتنی بر مسیر + تاریخ + اقامتگاه تولید می‌شود.

---

</div>### ۴. ویرایش — `PUT /b2c/v1/travel_requests/{id}`

<div class="api-docs" id="bkmrk-%D8%AA%D9%85%D8%A7%D9%85%DB%8C-%D9%81%DB%8C%D9%84%D8%AF%D9%87%D8%A7-%D9%85%D8%B4%D8%A7%D8%A8%D9%87-s">- تمامی فیلدها مشابه Store هستند؛ فقط نیاز به شناسه مسیر دارد.
- در بروزرسانی، `status` و `payment_status` نیز می‌توانند تغییر کنند.

</div>#### پاسخ نمونه

```json
{
  "payload": {
    "id": 45,
    "status": 3,
    "payment_status": 1,
    "title": "اصفهان به مشهد هتل آوازه مشهد از ‎2025/12/14‎ تا ‎2025/12/18‎"
  },
  "meta": { "timestamp": 1733821880 }
}
```

<div class="api-docs" id="bkmrk--3">---

</div>### ۵. حذف — `DELETE /b2c/v1/travel_requests/{id}`

درخواست با شناسه مشخص حذف می‌شود و مقدار حذف (true/false) بازگردانده می‌شود.

<div class="api-docs" id="bkmrk--4">---

</div>### ۶. تغییر وضعیت — `POST /b2c/v1/travel_requests/{id}/change-status`

<div class="api-docs" id="bkmrk-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D9%87%E2%80%8C%D8%B1%D9%88%D8%B2%D8%B1%D8%B3%D8%A7%D9%86%DB%8C-%D8%B3%D8%B1%DB%8C">- برای به‌روزرسانی سریع وضعیت (status) و تخصیص اپراتور یا فاکتور مرجع به کار می‌رود.
- اگر مقدار `status = 4` و `reference_id` ارسال شود → فاکتور مرتبط ثبت می‌شود.

</div>```json
{
  "operator_id": 5,
  "status": 4,
  "reference_id": 999
}
```

#### خروجی موفق

```json
{
  "payload": { "status": true },
  "meta": { "timestamp": 1733821930 }
}
```

<div class="api-docs" id="bkmrk--5">---

</div>### نکات فنی و عملکردی

<div class="api-docs" id="bkmrk-%D8%B9%D9%86%D9%88%D8%A7%D9%86-%D8%AA%D8%B1%DA%A9%DB%8C%D8%A8%DB%8C-%D8%A7%D8%B2-%D9%85%D8%A8%D8%AF%D8%A3">- عنوان ترکیبی از مبدأ، مقصد، اقامتگاه و بازه تاریخ به‌شکل داینامیک ساخته می‌شود.
- روابط داده‌ای: 
    - Customers ↔ Requester (برای B2C)
    - Colleagues ↔ Requester (برای B2B)
    - Cities ↔ Origin/Destination
    - Hotels ↔ Accommodation
- تمامی تاریخ‌ها در فرمت شمسی-لاتین با `LTR Markers` جلوگیری از بهم‌ریختگی نمایش دارند.
- فیلد `details` به‌صورت JSON ذخیره و در خروجی decode م‌شود.

</div>