# AKİA Arxiv — REST API v1

Xarici sistemlərin arxivə sənəd göndərmək, mövcud sənədləri yoxlamaq və şöbə konfiqurasiyasını oxumaq üçün API-si.

- **Base URL:** `https://<sizin-domain>/api/v1`
- **Format:** JSON
- **Encoding:** UTF-8
- **Auth:** Bearer token (Sanctum)
- **Rate limit:** 60 sorğu/dəqiqə token başına

---

## 1. Authentication

### Token əldə etmək

API tokenləri admin tərəfindən serverdə artisan komandası ilə yaradılır. Hər inteqrasiya üçün ayrı token olmalıdır.

```bash
php artisan api:token-issue --user=user@example.com --name="External system A"
```

Nümunə çıxış:

```
✓ API tokeni yaradıldı

Tokeni indi göstərib bir daha göstərməyəcəyik. Qoruyun:

  3|hSh2xJxYR5Tj9w...long-token-string...P0kY

İstifadə nümunəsi:
  curl -H "Authorization: Bearer 3|hSh2xJxYR5Tj9w..." \
       https://<sizin-domain>/api/v1/health
```

**Vacib:** Token yalnız bir dəfə göstərilir. Onu təhlükəsiz yerdə saxlayın (parol meneceri, secrets vault və s.).

### Hər sorğuda istifadə

```http
GET /api/v1/health HTTP/1.1
Host: arxiv.example.az
Authorization: Bearer 3|hSh2xJxYR5Tj9w...
Accept: application/json
```

### Tokeni ləğv etmək

İstifadəçi öz hesabını açıb Settings > API Tokens → Revoke (UI hazırlanır), və ya:

```bash
php artisan tinker
> \Laravel\Sanctum\PersonalAccessToken::find(3)->delete();
```

---

## 2. Cavab strukturu

### Uğurlu cavab

```json
{
    "data": { ... }
}
```

### Xəta

```json
{
    "message": "İnsan-oxunabilən xəta mesajı",
    "errors": {
        "field_name": ["Detallı validasiya mesajı"]
    }
}
```

### HTTP status kodları

| Kod | Mənası |
|-----|--------|
| 200 | OK |
| 201 | Created (yeni sənəd yaradıldı) |
| 401 | Token yoxdur / etibarsızdır |
| 403 | Tokenin bu əməliyyatı icra etmək icazəsi yoxdur |
| 404 | Tapılmadı |
| 422 | Validasiya xətası |
| 429 | Rate limit aşıldı |
| 500 | Server xətası |

---

## 3. Endpoints

### 3.1. Health check

`GET /api/v1/health` — token doğruluğunu yoxlamaq üçün istifadə olunur.

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://arxiv.example.az/api/v1/health
```

**Cavab:**
```json
{
    "status": "ok",
    "api": "v1",
    "user_id": 5,
    "timestamp": "2026-05-27T14:30:00+04:00"
}
```

---

### 3.2. Şöbələrin siyahısı

`GET /api/v1/departments` — istifadəçinin icazəli şöbələri.

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://arxiv.example.az/api/v1/departments
```

**Cavab:**
```json
{
    "data": [
        { "id": 1, "name": "Kredit şöbəsi",            "slug": "kredit-sobesi" },
        { "id": 2, "name": "Aqroservis şöbəsi",        "slug": "aqroservis-sobesi" },
        { "id": 3, "name": "Maliyyə-Satınalma şöbəsi", "slug": "maliyye-satinalma-sobesi" }
    ]
}
```

---

### 3.3. Şöbənin tam metadata-sı

`GET /api/v1/departments/{slug}` — sənəd göndərmək üçün lazım olan bütün ID-lər və sahələr.

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://arxiv.example.az/api/v1/departments/kredit-sobesi
```

**Cavab:**
```json
{
    "data": {
        "id": 1,
        "name": "Kredit şöbəsi",
        "slug": "kredit-sobesi",
        "types": [
            { "id": 12, "name": "Akt",      "color_code": "#10b981" },
            { "id": 14, "name": "Ərizə",    "color_code": "#f59e0b" },
            { "id": 13, "name": "Protokol", "color_code": "#3b82f6" }
        ],
        "statuses": [
            { "id": 21, "name": "Arxivdə",      "color_code": "#64748b" },
            { "id": 18, "name": "Əməkdaşdadır", "color_code": "#ef4444" }
        ],
        "shelves":          [ { "id": 30, "name": "R1" }, { "id": 31, "name": "R2" } ],
        "document_folders": [ { "id": 40, "name": "Q1" }, { "id": 41, "name": "Q2" } ],
        "folders": [
            {
                "id": 100, "name": "Texnika",
                "children": [
                    { "id": 101, "name": "Kapitalbank", "children": [] },
                    { "id": 102, "name": "Paşabank",    "children": [] }
                ]
            }
        ],
        "fields": [
            { "id": 1, "name": "Sənədin nömrəsi", "type": "text",     "system_key": "application_number", "dropdown_source": null,    "is_required": true,  "is_system": true,  "sort_order": 1 },
            { "id": 8, "name": "VÖEN",            "type": "text",     "system_key": null,                 "dropdown_source": null,    "is_required": true,  "is_system": false, "sort_order": 8 }
        ]
    }
}
```

---

### 3.4. Yeni sənəd göndər

`POST /api/v1/appeals` — multipart/form-data.

**Tələb olunan sahələr:**

| Sahə | Tip | Mənbə |
|------|-----|-------|
| `department_id` | int | `/departments` |
| `folder_id` | int | dept-in `folders` ağacının yarpaq qovluğu |
| `type_id` | int | dept-in `types` |
| `status_id` | int | dept-in `statuses` |
| `shelf_id` | int | dept-in `shelves` |
| `document_folder_id` | int | dept-in `document_folders` |
| `application_date` | string (YYYY-MM-DD) | sənədin tarixi |
| `file` | binary | əsas sənəd faylı |

**İxtiyari:**

| Sahə | Tip | Qeyd |
|------|-----|------|
| `application_number` | string | boş qoysanız avtomatik (`AZ-YYYYMMDD-XXXXXX`) |
| `description` | string | qısa məlumat (max 5000) |
| `custom_fields[<id>]` | string | dynamic sahələr — bax aşağı |
| `attachments[]` | binary[] | qoşma fayllar (max 20) |

#### Dinamik (custom) sahələrin göndərilməsi

Hər şöbənin öz custom field-ləri var (VÖEN, FİN, Müqavilə nömrəsi, Kredit məbləği və s.). Onları göndərmək üçün:

1. **Mövcud field-ləri öyrən** — `GET /api/v1/departments/{slug}` cavabında `fields[]` siyahısı gəlir. Hər field-in `id`, `name`, `type`, `is_system`, `is_required` xüsusiyyətləri var.

2. **POST sorğusunda göndər** — `custom_fields[<field_id>]` formasında:

```bash
# Misal: dept-də 3 custom field var (id 8 VÖEN, id 9 Ad soyad, id 11 Kredit məbləği)
-F "custom_fields[8]=1234567890"          # VÖEN
-F "custom_fields[9]=Sarkhan Babayev"     # Müraciət a.s.a
-F "custom_fields[11]=200000"             # Kredit məbləği (number tip)
```

3. **Qaydalar:**
   - `is_system=true` field-lər **göndərmə** — onlar üçün ayrı top-level parametr var (`application_number`, `application_date`, `type_id`, `status_id`, `shelf_id`, `document_folder_id`, `description`).
   - `is_required=true` custom field-lər **mütləq göndərilməlidir**, yoxsa 422 alacaqsınız.
   - Hər dəyər **string** kimi göndərilir (number/date də string formatında).

4. **Cavabda formatı:**

```json
"custom_fields": {
    "8":  "1234567890",
    "9":  "Sarkhan Babayev",
    "11": "200000"
}
```

JSON object kimi gəlir, key-lər field ID-ləridir. Boş halda `{}`.

5. **Hardcoded field ID-lərə güvənməyin** — hər şöbədə fərqlidir + UI-dan dəyişə bilər. Hər inteqrasiyada əvvəlcə `GET /departments/{slug}` çağırın, `fields` listindən `name` və ya `system_key` ilə uyğun ID-ni tapın.

**Tipik kod (pseudo):**

```python
dept = api_get(f"/departments/{slug}")
field_id_voen = next(f["id"] for f in dept["fields"] if f["name"] == "VÖEN")
field_id_amount = next(f["id"] for f in dept["fields"] if f["name"] == "Kredit məbləği")

api_post("/appeals", files={"file": pdf}, data={
    "department_id": dept["id"],
    ...,
    f"custom_fields[{field_id_voen}]": "1234567890",
    f"custom_fields[{field_id_amount}]": "200000",
})
```

**Misal (curl):**

```bash
curl -X POST https://arxiv.example.az/api/v1/appeals \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" \
  -F "department_id=1" \
  -F "folder_id=101" \
  -F "type_id=12" \
  -F "status_id=18" \
  -F "shelf_id=30" \
  -F "document_folder_id=40" \
  -F "application_date=2026-05-27" \
  -F "application_number=AZ-2026-001" \
  -F "description=Test sənəd" \
  -F "custom_fields[8]=1234567890" \
  -F "custom_fields[9]=Sarkhan Babayev" \
  -F "file=@/path/to/document.pdf" \
  -F "attachments[]=@/path/to/attachment1.pdf"
```

**Cavab (201 Created):**

```json
{
    "data": {
        "id": 142,
        "application_number": "AZ-2026-001",
        "application_date": "2026-05-27",
        "description": "Test sənəd",
        "source": "api",
        "department": { "id": 1, "name": "Kredit şöbəsi", "slug": "kredit-sobesi" },
        "type":       { "id": 12, "name": "Akt" },
        "status":     { "id": 18, "name": "Əməkdaşdadır" },
        "shelf":      { "id": 30, "name": "R1" },
        "folder":     { "id": 101, "name": "Kapitalbank" },
        "custom_fields": { "8": "1234567890", "9": "Sarkhan Babayev" },
        "documents": [
            { "id": 500, "kind": "main",       "original_name": "document.pdf",    "mime_type": "application/pdf", "size_bytes": 245678, "ocr_status": "pending" },
            { "id": 501, "kind": "attachment", "original_name": "attachment1.pdf", "mime_type": "application/pdf", "size_bytes": 110234, "ocr_status": "pending" }
        ],
        "created_at": "2026-05-27T14:35:12+04:00",
        "updated_at": "2026-05-27T14:35:12+04:00"
    }
}
```

**Validasiya xətaları (422):**

```json
{
    "message": "Validasiya xətası.",
    "errors": {
        "type_id": ["The selected type id is invalid."],
        "custom_fields.8": ["«VÖEN» sahəsi mütləq doldurulmalıdır."]
    }
}
```

---

### 3.5. Göndərilmiş sənədlərin siyahısı

`GET /api/v1/appeals` — paginated.

**Query:**
- `per_page` (1-100, default 20)
- `department_id` (int, ixtiyari)
- `source` (`web` / `api`, ixtiyari)

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://arxiv.example.az/api/v1/appeals?source=api&per_page=10"
```

**Cavab:** `data` (sıra), `links`, `meta` (Laravel standart pagination).

---

### 3.6. Tək sənəd detalı

`GET /api/v1/appeals/{id}`

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://arxiv.example.az/api/v1/appeals/142
```

Cavab strukturu — POST /appeals ilə eyni.

---

### 3.7. Status (polling)

`GET /api/v1/appeals/{id}/status` — yalnız status + OCR vəziyyəti.

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://arxiv.example.az/api/v1/appeals/142/status
```

**Cavab:**
```json
{
    "id": 142,
    "application_number": "AZ-2026-001",
    "status": "Əməkdaşdadır",
    "ocr_statuses": ["completed", "pending"],
    "updated_at": "2026-05-27T14:35:12+04:00"
}
```

OCR vəziyyətləri: `pending`, `processing`, `completed`, `failed`.

---

## 4. Rate limiting

Hər token üçün **dəqiqədə 60 sorğu**. Aşılarsa 429 Too Many Requests + `Retry-After` header.

---

## 5. İcazə sistemi

API tokeni hansı istifadəçi adından yaradılıbsa, **o istifadəçinin icazələri** ilə işləyir:

- `readableDepartmentIds()` → hansı şöbələri görə bilər
- `canActionInDepartment(deptId, 'write')` → o şöbədə sənəd göndərə bilər

İdeal yanaşma: hər inteqrasiya üçün **xüsusi user yaradın** (məs. `api-system-a@arxiv.local`), ona yalnız lazımi şöbələrə **write** icazəsi verin, sonra token yaradın.

---

## 6. Tipik inteqrasiya axını

```
1. GET /api/v1/health                              → token uğurludur?
2. GET /api/v1/departments                         → hansı şöbələr əlçatandır?
3. GET /api/v1/departments/kredit-sobesi           → ID-ləri öyrən (type, status, folder, ...)
4. POST /api/v1/appeals                            → sənədi göndər (multipart)
5. GET /api/v1/appeals/{id}/status                 → vaxtaşırı OCR statusunu yoxla
```

---

## 7. Test üçün lokal mühit

Docker ilə qaldırın:

```bash
docker compose up -d
docker compose exec -T app php artisan migrate --seed
docker compose exec -T app php artisan api:token-issue --user=root@example.com --name=test
```

Sonra ortaya çıxan tokenlə test edin:

```bash
TOKEN="paste-here"
curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/v1/health
```

---

## 8. Versionlama

API versiyası URL prefix-də (`/v1`). Breaking dəyişikliklər olarsa `/v2` yaradılacaq, `/v1` ən azı 6 ay paralel saxlanılacaq.

---

## 9. Postman Collection (hazır)

Bütün 7 endpoint-i sınamaq üçün hazır Postman collection layihənin daxilindədir:

```
docs/AKIA-API.postman_collection.json
```

### 9.1. Import etmək

1. Postman aç
2. Sol yuxarıda **"Import"** düyməsi (`File → Import` da olur)
3. **"Upload Files"** → `docs/AKIA-API.postman_collection.json` faylını seç
4. **"Import"** düyməsinə bas
5. Sol panelda **"AKİA Arxiv API v1"** adlı collection görünəcək. İçində 7 hazır request var:
   - `1. Health check`
   - `2. Departments — siyahı`
   - `3. Departments — Kredit detalı`
   - `4. Appeals — yeni sənəd yarat (Kredit)`
   - `5. Appeals — siyahı (API mənbəli)`
   - `6. Appeals — tək sənədin detalı`
   - `7. Appeals — status polling`

### 9.2. Tokeni və domain-i təyin etmək

Collection import olunduqda 2 dəyişən gəlir: `base_url` və `token`. Onları doldur:

1. Sol panelda **AKİA Arxiv API v1** collection-a sağ klik → **Edit**
2. **"Variables"** tab seç
3. `token` sətrinin **Current Value** sütununa cari tokeni yapışdır:

   ```
   3|hSh2xJxYR5Tj9w...long-token-string...P0kY
   ```

4. `base_url` default `http://localhost:8000`-dir. Production-da:
   - `https://arxiv.example.az` (öz domeyninlə əvəzlə)

5. **Save** düyməsinə bas.

### 9.3. Test ardıcıllığı

Bu sırayla işlət:

1. **Health check** → cavab `{"status":"ok","user_id":...}` olarsa token işləyir. Əks halda 401 alacaqsan.

2. **Departments siyahı** → mövcud şöbələrin id və slug-larını göstərir.

3. **Kredit detalı** → cavabdakı **ID-ləri** yadda saxla:
   - `data.types[].id` (məs. `1` = Akt)
   - `data.statuses[].id` (məs. `1` = Rəfdədir)
   - `data.shelves[].id` (məs. `1` = R1)
   - `data.document_folders[].id` (məs. `2` = Q1)
   - `data.folders[].children[].id` (yarpaq qovluğun id-si, məs. `2` = Texnika/Kapitalbank)
   - `data.fields[].id` — hər custom field üçün

4. **Yeni sənəd yarat** request-i aç:
   - Body tab-da bütün form-data sahələri hazır gəlir (Kredit üçün real ID-lərlə).
   - `file` sahəsinə skroll et → **"Select Files"** düyməsindən bir PDF/JPG/PNG seç.
   - (İxtiyari) `attachments[]` sahəsi default olaraq **disabled**-dır. Qoşma fayl yükləmək istəyirsənsə:
     - sahənin sol tərəfindəki tick-i aktiv et
     - "Select Files" düyməsindən fayl(ları) seç
   - **Send** bas.
   - Cavab `201 Created` olmalıdır, içində yaradılmış appeal-ın id-si və metadata-sı.

5. **List/Show/Status** request-lərində URL-dəki nümunə id-ni (məs. `/appeals/1`) real appeal id ilə əvəzləyib sına.

### 9.4. Başqa şöbəyə sənəd göndərmək

Collection-dakı **"Yeni sənəd yarat"** request-i Kredit üçün hazırlanıb. Başqa şöbəyə (məs. Aqroservis) göndərmək üçün:

1. Əvvəl `Departments — Kredit detalı` request-i nümunə tutaraq URL-də `kredit-sobesi` slug-unu **`aqroservis-sobesi`** ilə əvəzlə → Send → cavabdakı ID-ləri yadda saxla.
2. `Yeni sənəd yarat` request-ini **duplicate et** (sağ klik → Duplicate), Body-də:
   - `department_id` → yeni şöbənin id
   - `folder_id`, `type_id`, `status_id`, `shelf_id`, `document_folder_id` → yeni şöbənin ID-ləri ilə əvəzlə
   - `custom_fields[<id>]` açarları → yeni şöbənin field id-lərinə uyğun yenilə
   - `file` → yeni fayl seç
3. **Send**.

### 9.5. Yeni token əlavə etmək

Yeni inteqrasiya/test üçün serverdən token al:

```bash
docker compose exec -T app php artisan api:token-issue \
  --user=root@example.com \
  --name="Integration ABC"
```

Çıxan token-i collection variables-ə yapışdır (yuxarıdakı 9.2).

### 9.6. Tipik xətalar və həll yolları

| Cavab | Səbəb | Həll |
|-------|-------|------|
| `{"message":"Unauthenticated."}` 401 | Token yoxdur, etibarsızdır, və ya `Bearer ` prefiksi düşüb | Collection-da Authorization tab-ında token yoxlanışı et |
| `{"message":"Bu şöbəyə icazəniz yoxdur."}` 403 | Token-in sahibi istifadəçinin həmin şöbəyə icazəsi yoxdur | Tokeni başqa userdən yarat, və ya user-ə role/departmemt icazəsi əlavə et |
| `{"errors":{"file":["Faylın formatı qəbul edilmir."]}}` 422 | Yüklədiyin fayl izn verilən formatda deyil | PDF, JPG, JPEG, PNG, TIF, TIFF, BMP, WEBP, DOC, DOCX, XLS, XLSX |
| `{"errors":{"type_id":["The selected type id is invalid."]}}` 422 | Verdiyin `type_id` o şöbədə yoxdur | `Departments/{slug}` çağırıb real `id`-ləri yenilə |
| `{"errors":{"custom_fields.X":["...mütləqdir."]}}` 422 | `is_required=true` olan custom field göndərilməyib | O field-ə dəyər yaz: `custom_fields[X]` |
| `{"message":"Bu sənəd nömrəsi artıq mövcuddur..."}` 422 | `application_number` unikal deyildir | Başqa nömrə seç, və ya `application_number`-ı boş qoy (avtomatik yaranar) |
| HTML login səhifəsi gəlir | (artıq düzəldildi) Bearer token yox idi və brauzer-detected redirect olunmuşdur | `Accept: application/json` header-i göndər, ya da auth header-i yoxla |

---
 