# BAGIAN 4: FLOWCHART BACKEND (BE)
## Detailed Backend Application Flowcharts

---

## 4.1 BE General Service Architecture

Arsitektur umum Backend menggunakan pattern **Repository → Service → Route** (Layered Architecture).

![](./images/4.1.png)

---

## 4.2 BE Auth & RBAC Flow (Detailed)

Alur lengkap autentikasi dan authorization di Backend.

![](./images/4.2.png)

---

## 4.3 BE Conversation & Message Flow (Chat Engine)

Alur lengkap mesin chat Backend, baik inbound maupun outbound.

![](./images/4.3.png)

---

## 4.4 BE Conversation Status Flow

Alur perubahan status conversation. Status mengalir **satu arah** (one-way pipeline), tidak bisa mundur.

> **Catatan Penting (Kondisi Aktual):**
> - **4 status** didefinisikan: `UNASSIGNED`, `ASSIGNED`, `RESOLVED`, `CLOSED`
> - **CLOSED** didefinisikan di enum tapi **tidak ada implementasi** — tidak ada endpoint/code path yang mengarah ke status ini
> - **UNASSIGNED ← ASSIGNED (BLOCKED)** — kode secara eksplisit throw error: *"Status cannot be changed to unassigned, you must resolve or reassign to another member."*
> - **RESOLVED tidak bisa di-reopen** — customer reply setelah RESOLVED akan membuat **conversation BARU** dengan status UNASSIGNED, bukan membuka kembali yang lama
> - **Chain Mechanism**: Conversation baru di-link ke conversation lama via `previousConversationId` (linked list)
> - **RESOLVED tidak bisa di-resolve ulang** — kode throw `ConversationAlreadyResolvedError`
> - **Hanya agent yang di-assign** yang bisa resolve conversation-nya sendiri
> - **Pesan ke conversation RESOLVED diblok** — kode throw `ConversationAlreadyResolvedError`

### State Diagram

```mermaid
stateDiagram-v2
    [*] --> UNASSIGNED: Webhook menerima pesan dari customer<br/>(WA / IG / TG)<br/>→ createConversation()<br/>status: UNASSIGNED

    UNASSIGNED --> ASSIGNED: ① Manual Assign<br/>Admin/Owner assign ke agent<br/>PATCH /conversations/:id/status<br/>body: {conversationStatus:"ASSIGNED", assignedTo}

    UNASSIGNED --> ASSIGNED: ② Get New Chat<br/>Agent klaim chat terlama<br/>POST /conversations/get-new-chat<br/>(hanya role: member,<br/>developer & devops diblok)

    UNASSIGNED --> ASSIGNED: ③ Auto-Assign (WhatsApp)<br/>Agent kirim pesan → otomatis assign<br/>(di-trigger saat WA status: SENT,<br/>skipped untuk campaign blast)

    ASSIGNED --> ASSIGNED: Reassign ke agent lain<br/>PATCH /conversations/:id/status<br/>(member: hanya miliknya,<br/>admin/owner: semua)

    ASSIGNED --> RESOLVED: Resolve<br/>Hanya assigned user yang bisa<br/>PATCH /conversations/:id/status<br/>body: {conversationStatus:"RESOLVED",<br/>resolveCategoryIds, resolvedDescription}

    RESOLVED --> NEW_UNASSIGNED: Customer balas pesan<br/>→ TIDAK reopen!<br/>→ Buat conversation BARU<br/>(previousConversationId = lama)

    state NEW_UNASSIGNED {
        [*] --> NEW_CONV: findOrCreateConversation()<br/>status: UNASSIGNED
        note right of NEW_CONV: Ter-chain ke<br/>conversation RESOLVED<br/>sebelumnya via<br/>previousConversationId
    }

    state "CLOSED<br/>(DEFINED tapi TIDAK DIIMPLEMENTASI)" as CLOSED_NOT_IMPL
```

### Detail Per Transisi

```mermaid
flowchart TD
    subgraph CREATE["① PEMBUATAN (UNASSIGNED)"]
        WEBHOOK["Webhook menerima pesan"] --> FIND{"Cari conversation aktif?\n(not RESOLVED + 24h window)"}
        FIND -->|"Ditemukan"| UPDATE_EXIST["Update existing:\nlast_message, last_message_at,\nunread_counts++"]
        FIND -->|"Tidak ditemukan"| CREATE_NEW["Create conversation BARU:\nstatus: UNASSIGNED,\nunread_counts: 1"]
        FIND -->|"RESOLVED ditemukan"| CREATE_CHAIN["Create conversation BARU:\nstatus: UNASSIGNED,\npreviousConversationId = lama"]
    end

    subgraph ASSIGN["② ASSIGN (UNASSIGNED → ASSIGNED)"]
        A1["Manual Assign (PATCH)"] --> SET_ASSIGNED["assignedTo = userId\nassignedAt = now()\nassignedBy = callerId"]
        A2["Get New Chat (POST)"] --> FIND_OLDEST["Find UNASSIGNED tertua\nper contact (row_number)"]
        FIND_OLDEST --> SET_ASSIGNED
        A3["Auto-Assign (WA)"] --> CHECK_SENT{"WA status = SENT?\n+ bukan campaign blast?"}
        CHECK_SENT -->|"Ya"| AUTO_SET["assignedTo = sender\nfirst_response_at = now()\nfirst_response_by = sender"]
        CHECK_SENT -->|"Tidak"| SKIP["Skip auto-assign"]
    end

    subgraph RESOLVE["③ RESOLVE (ASSIGNED → RESOLVED)"]
        R1["Agent resolve (PATCH)"] --> CHECK{assignedTo === userId?\n+ resolvedAt === null?}
        CHECK -->|"Ya"| SET_RESOLVED["conversationStatus: RESOLVED\nresolvedAt = now()\nresolvedBy = userId\n+ resolve categories\n+ system message (type 4)"]
        CHECK -->|"Tidak"| ERR_RESOLVE["Error:\n- Bukan assigned user\n- Sudah pernah di-resolve"]
    end

    subgraph BLOCKED["④ DIBLOKIR / TIDAK BISA"]
        B1["ASSIGNED → UNASSIGNED"] --> ERR1["BLOCKED\n'Status cannot be changed to\nunassigned'"]
        B2["RESOLVED → RESOLVED"] --> ERR2["BLOCKED\nConversationAlreadyResolvedError"]
        B3["Kirim pesan ke RESOLVED"] --> ERR3["BLOCKED\nConversationAlreadyResolvedError"]
        B4["RESOLVED → CLOSED"] --> ERR4["NOT IMPLEMENTED\nCLOSED hanya ada di enum"]
    end

    style CREATE fill:#E8F5E9,stroke:#4CAF50
    style ASSIGN fill:#FFF3E0,stroke:#FF9800
    style RESOLVE fill:#E3F2FD,stroke:#1976D2
    style BLOCKED fill:#FFCDD2,stroke:#F44336
```

### Chain Mechanism (Customer Reply Setelah RESOLVED)

```mermaid
flowchart TD
    subgraph SCENARIO["CUSTOMER BALAS PESAN SETELAH RESOLVED"]
        OLD_CONV["Conversation A\nstatus: RESOLVED\nassignedTo: Agent 1\nresolvedAt: 10:00"] --> CUSTOMER_REPLY["Customer kirim pesan baru"]
        CUSTOMER_REPLY --> FIND{"findConversation()\nCari: not RESOLVED + 24h window"}
        FIND -->|"A sudah RESOLVED"| NOT_FOUND["Tidak ditemukan!"]
        NOT_FOUND --> FIND_PREV{"Cari conversation RESOLVED\ntersebelumnya\n(contact + channel)"}
        FIND_PREV -->|"Ditemukan: Conv A"| CREATE_NEW["Create Conversation B:\nstatus: UNASSIGNED\npreviousConversationId = Conv A\nunread_counts: 1"]
    end

    subgraph CHAIN_VIEW["HASIL CHAIN DI UI"]
        CONV_A["Conv A (RESOLVED)\nassignedTo: Agent 1\nresolvedAt: 10:00\n↑ previousConversationId = null"]
        CONV_B["Conv B (UNASSIGNED)\n↑ previousConversationId = Conv A"]
        CONV_A -->|"chain"| CONV_B
    end

    style SCENARIO fill:#FFF3E0,stroke:#FF9800
    style CHAIN_VIEW fill:#E3F2FD,stroke:#1976D2
```

### Tabel Perubahan Field Per Transisi

| Transisi | `conversationStatus` | `assignedTo` | `assignedAt` | `assignedBy` | `resolvedAt` | `resolvedBy` | `resolvedDescription` | `unreadCounts` | `readBy` |
|---|---|---|---|---|---|---|---|---|---|
| **Create** | `UNASSIGNED` | `null` | `null` | `null` | `null` | `null` | `null` | `0` atau `1` | `null` |
| **→ ASSIGNED** | `ASSIGNED` | `userId` | `now()` | `callerId` | — | — | — | — | — |
| **Reassign** | `ASSIGNED` | `newUserId` | `now()` | `callerId` | — | — | — | — | — |
| **→ RESOLVED** | `RESOLVED` | *(tidak di-clear)* | *(tidak di-clear)* | *(tidak di-clear)* | `now()` | `callerId` | optional | — | — |
| **→ UNASSIGNED** | **BLOCKED** | — | — | — | — | — | — | — | — |
| **Mark Read** | — | — | — | — | — | — | — | `0` | `userId` |
| **Pesan Masuk** | — | — | — | — | — | — | — | `+1` | `null` |

---

## 4.5 BE Campaign Execution Flow (Detailed)

Alur lengkap eksekusi campaign di Backend.

![](./images/4.5.png)

---

## 4.6 BE Ticket Lifecycle Flow (Detailed)

Alur lengkap lifecycle ticket di Backend.

![](./images/4.6.png)

---

## 4.7 BE Webhook Processing Architecture

Arsitektur pemrosesan webhook dari external channels.

![](./images/4.7.png)

---

## 4.8 BE Service Module Pattern

Pattern yang digunakan di setiap service module Backend.

![](./images/4.8.png)

---

## 4.9 BE Error Handling Flow

![](./images/4.9.png)

