> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quinnsambal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sales pipeline management

<style>
  {`
    [class*="max-w-none"][class*="table"] {
      display: block !important;
      overflow-x: auto !important;
      max-width: 100% !important;
      width: 100% !important;
      flex-grow: 0 !important;
    }
    [class*="max-w-none"][class*="table"] > table {
      width: 100% !important;
      max-width: 100% !important;
      table-layout: fixed !important;
    }
    .mermaid {
      max-width: 100% !important;
      overflow-x: auto !important;
    }
    article svg[role="img"] {
      max-width: 100% !important;
      height: auto !important;
    }
    article img, .prose img {
      max-width: 100% !important;
      height: auto !important;
    }
    img[src*="LOGO"], img[src*="logo"] {
      max-width: 120px !important;
      max-height: 40px !important;
      width: auto !important;
      height: auto !important;
      object-fit: contain !important;
    }
    `}
</style>

***

title: "Sales Pipeline Management — Manajemen Pipeline Lanjutan"
description: "SalesPipelineManagement.jsx (963 baris) — dual view (Kanban + List), 5 KPI metrics bar, 6-stage pipeline, lead CRUD via React Query, lead-to-customer conversion, dan overdue detection."
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Sales Pipeline Management — Manajemen Pipeline Lanjutan

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/crm/sales-pipeline-management.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=38678a3683d02270647464a146282c02" alt="Sales Pipeline Management" width="1920" height="1080" data-path="docs/mintlify/screenshots/crm/sales-pipeline-management.png" />

Sales Pipeline Management (`SalesPipelineManagement.jsx` — **963 baris**) adalah halaman standalone untuk mengelola pipeline penjualan dengan fitur lengkap. Menggunakan **React Query** (`useQuery`, `useMutation`, `useQueryClient`) untuk data fetching dan cache management, halaman ini menyediakan **dual view** (Kanban + List), **5 KPI metrics bar** real-time, lead CRUD lengkap, dan **lead-to-customer conversion** dengan duplicate detection.

Halaman ini berbagi logic yang sama dengan embedded pipeline (`SalesFunnelBoard.jsx` — 715 baris) di tab "Pipeline" pada halaman CRM, namun menawarkan UI yang lebih kaya dengan toggle view dan metrics bar.

## Arsitektur Komponen

```mermaid theme={null}
graph TB
    subgraph "SalesPipelineManagement.jsx — 963 lines"
        direction TB
        RQ[React Query Layer<br/>useQuery + useMutation]
        VIEW[View Toggle<br/>Kanban / List]
        KANBAN[Kanban Board<br/>6 columns]
        LIST[List View<br/>Sortable table]
        METRICS[Metrics Bar<br/>5 KPIs]
        MODAL[Lead Modal<br/>Create / Edit / Convert]
    end

    subgraph "SalesPipeline Entity"
        direction TB
        F1[Identity: title, company_name, contact_name, email]
        F2[Deal: estimated_value, probability, expected_close_date]
        F3[Stage: prospect → qualified → proposal → negotiation → won/lost]
        F4[Source: lead_source, lead_medium, campaign]
        F5[Link: customer_id, invoice_ids[]]
    end

    subgraph "Server Functions"
        S1[listSalesPipelines]
        S2[createSalesPipeline]
        S3[updateSalesPipeline]
        S4[deleteSalesPipeline]
        S5[convertLeadToCustomer]
    end

    RQ --> S1 & S2 & S3 & S4 & S5
    VIEW --> KANBAN & LIST
    KANBAN & LIST --> MODAL
    MODAL --> S2 & S3 & S5
    S1 --> F1 & F2 & F3 & F4 & F5
```

## Perbedaan dengan Embedded Pipeline

| Aspek | Embedded (`SalesFunnelBoard`) | Standalone (`SalesPipelineManagement`) |
| - | - | - |
| **Lokasi** | Tab "Pipeline" di `/crm` | Halaman `/sales-pipeline` |
| **Komponen** | 715 baris | 963 baris |
| **View** | Kanban only | Kanban + List toggle |
| **Metrics Bar** | Tidak ada | 5 KPI real-time |
| **UI** | Sederhana, theme tokens | Full-featured, action buttons |
| **Integration Banner** | Ada (CRM count, invoice count) | Tidak ada |
| **Data Fetching** | base44 API direct | React Query (cached) |
| **Logic** | Sama — 6 stages | Sama — 6 stages |

## Akses Halaman

| Metode | Detail |
| - | - |
| **URL** | `/sales-pipeline` |
| **Sidebar** | Menu **CRM** → **Sales Pipeline** |
| **CRM Tab** | Via tab "Pipeline" di `/crm` (embedded version) |

## Fitur Utama

### Dual View Mode

```mermaid theme={null}
graph LR
    subgraph "Kanban Board"
        K1[Prospect<br/>5 leads<br/>Rp 45jt] --> K2[Qualified<br/>3 leads<br/>Rp 28jt]
        K2 --> K3[Proposal<br/>2 leads<br/>Rp 35jt]
        K3 --> K4[Negotiation<br/>1 lead<br/>Rp 15jt]
        K4 --> K5[Won<br/>4 leads<br/>Rp 62jt]
        K4 --> K6[Lost<br/>2 leads<br/>Rp 18jt]
    end

    subgraph "List View"
        L1[Table: sortable columns<br/>title, value, stage, probability, close_date]
    end

    TOGGLE[Toggle View] --> K1 & L1
```

| Mode | Deskripsi | Best For |
| - | - | - |
| **Kanban Board** | Kolom per tahap, card per lead, drag-and-drop | Visual overview, daily standup |
| **List View** | Tabel dengan semua lead, sortable & filterable | Detail analysis, bulk operations |

### Metrics Bar — 5 KPI Real-Time

```mermaid theme={null}
graph LR
    M1[Total Pipeline Value<br/>Rp 138.000.000<br/>SUM estimated_value]
    M2[Won Value<br/>Rp 62.000.000<br/>SUM where stage=won]
    M3[Weighted Forecast<br/>Rp 71.200.000<br/>SUM value × probability]
    M4[Conversion Rate<br/>66.7%<br/>won / (won + lost)]
    M5[Overdue Leads<br/>3<br/>close_date < today]
```

| Metrik | Formula | Insight |
| - | - | - |
| **Total Pipeline Value** | `SUM(estimated_value) WHERE stage ∉ {lost}` | Total potensi revenue |
| **Won Value** | `SUM(estimated_value) WHERE stage = won` | Revenue yang sudah diamankan |
| **Weighted Forecast** | `SUM(estimated_value × probability/100)` | Prediksi revenue berdasarkan probabilitas |
| **Conversion Rate** | `won_count / (won_count + lost_count) × 100%` | Efisiensi pipeline |
| **Overdue Leads** | `COUNT(expected_close_date < today AND stage ∉ {won, lost})` | Lead yang butuh perhatian segera |

### Stage Pipeline — 6 Tahap

```mermaid theme={null}
stateDiagram-v2
    [*] --> prospect: Lead masuk<br/>(sumber: website, WA, referral)
    prospect --> qualified: Verifikasi kebutuhan & budget
    qualified --> proposal: Kirim penawaran
    proposal --> negotiation: Diskusi & negosiasi
    negotiation --> won: Deal closed ✅
    negotiation --> lost: Deal gagal ❌
    proposal --> lost: Proposal ditolak
    qualified --> lost: Tidak qualify

    won --> [*]
    lost --> [*]
```

| Stage | Default Probability | Deskripsi | Aksi Utama |
| - | - | - | - |
| **prospect** | 10% | Lead baru masuk, belum diverifikasi | Initial contact, qualification |
| **qualified** | 25% | Kebutuhan & budget terkonfirmasi | Prepare proposal |
| **proposal** | 50% | Proposal sudah dikirim | Follow up, present |
| **negotiation** | 75% | Sedang negosiasi harga/term | Close deal |
| **won** | 100% | Deal closed, revenue diamankan | Convert to customer |
| **lost** | 0% | Deal gagal, tidak lanjut | Post-mortem, re-engage later |

### Lead Management

| Operasi | Deskripsi | Implementasi |
| - | - | - |
| **Create** | Dialog form dengan semua field SalesPipeline | `useMutation(createSalesPipeline)` |
| **Edit** | Update data lead (termasuk stage transition) | `useMutation(updateSalesPipeline)` |
| **Delete** | Hapus lead dengan konfirmasi dialog | `useMutation(deleteSalesPipeline)` |
| **Advance Stage** | Pindah ke tahap berikutnya (drag-drop di Kanban) | `updateSalesPipeline({stage: next})` |
| **Convert** | Konversi lead won menjadi Customer | `convertLeadToCustomer(lead_id)` |

## Lead-to-Customer Conversion

```mermaid theme={null}
flowchart TD
    A[Lead stage = won] --> B[Klik "Convert to Customer"]
    B --> C{Cek duplikat<br/>by email / nama}
    C -->|Ada duplikat| D[Link ke existing customer<br/>Update customer_id di lead]
    C -->|Tidak ada| E[Create new Customer<br/>from lead data]
    D --> F[Update lead: customer_id linked]
    E --> F
    F --> G[Invalidate React Query cache]
    G --> H[CRM database updated]
```

| Step | Aksi | Data Source |
| - | - | - |
| 1 | Cek duplikat berdasarkan email/nama | `Customer` entity |
| 2a | Jika ada duplikat → link ke customer existing | `updateSalesPipeline({customer_id})` |
| 2b | Jika tidak → buat customer baru | `createCustomer(lead_data)` |
| 3 | Update lead: stage = `won`, link `customer_id` | `updateSalesPipeline` |
| 4 | Invalidate cache → refresh UI | `queryClient.invalidateQueries` |

## Invoice Matching

Cross-reference `Invoice` entity berdasarkan nama/email pelanggan untuk menampilkan invoice yang terkait dengan lead:

```mermaid theme={null}
graph LR
    SP[SalesPipeline<br/>contact_email] --> MATCH{Match by<br/>email/name}
    INV[Invoice<br/>customer_email] --> MATCH
    MATCH --> LINK[Linked invoices<br/>displayed in lead detail]
```

## Overdue Detection

Lead yang melewati `expected_close_date` dan belum won/lost ditandai sebagai **overdue** dengan badge merah:

| Kondisi | Indikator | Aksi |
| - | - | - |
| `expected_close_date < today` AND `stage ∉ {won, lost}` | 🔴 Red badge + "Overdue" label | Prioritize follow-up |
| `expected_close_date` within 3 days | 🟡 Yellow badge + "Due Soon" | Schedule follow-up |
| `expected_close_date` > 3 days | 🟢 Normal | On track |

## React Query Implementation

| Hook | Purpose | Cache |
| - | - | - |
| `useQuery('salesPipelines')` | Fetch all leads | 5 min staleTime |
| `useMutation(createSalesPipeline)` | Create lead | Invalidate on success |
| `useMutation(updateSalesPipeline)` | Update lead/stage | Invalidate on success |
| `useMutation(deleteSalesPipeline)` | Delete lead | Invalidate on success |
| `useMutation(convertLeadToCustomer)` | Convert lead | Invalidate both queries |

## Tips

* **Gunakan Kanban view** untuk overview visual pipeline dan daily standup
* **Gunakan List view** untuk sorting dan filtering detail, terutama saat volume lead tinggi
* **Monitor Weighted Forecast** untuk prediksi revenue yang lebih akurat daripada total pipeline value
* **Review overdue leads** setiap pagi — prioritaskan follow-up lead yang sudah melewati close date
* **Convert lead ke customer** segera setelah deal closed agar data terpusat di CRM
* **Set probability yang realistis** per stage — ini mempengaruhi akurasi weighted forecast
* **Gunakan lead\_source** untuk menganalisis channel mana yang paling efektif menghasilkan deal

***

## Entity Schema — Sales Pipeline Management

### ER Diagram

```mermaid theme={null}
erDiagram
    SalesPipeline {
        string company_id PK "ID perusahaan"
        string lead_id "ID prospek/lead"
        string lead_name "Nama prospek"
        string lead_email "Email prospek"
        string lead_phone "Telepon prospek"
        string company_name "Nama perusahaan prospek"
        string assigned_to FK "ID sales person"
        string pipeline_stage "prospect | qualified | proposal | negotiation | won | lost"
        number estimated_value "Perkiraan nilai penjualan"
        string currency "Mata uang (default IDR)"
        number probability "Probabilitas konversi (%)"
        date expected_close_date "Tanggal penutupan yang diharapkan"
        date actual_close_date "Tanggal penutupan aktual"
        string source_lead "website | referral | advertisement | cold_call | social_media | event | other"
        array product_interest "Produk yang diminati"
        array interaction_history "Riwayat interaksi"
        string next_action "Aksi selanjutnya"
        date next_action_date "Tanggal aksi selanjutnya"
        string notes "Catatan"
    }

    Customer {
        string company_id PK "ID perusahaan"
        string name "Nama customer"
        string email "Email customer"
        string phone "Telepon customer"
        string customer_type "individual | business | retail | reseller | distributor | modern_market"
        string status "lead | prospect | customer | inactive"
        number lifetime_value "Total nilai transaksi"
        number total_orders "Jumlah transaksi"
        number average_order_value "Rata-rata nilai transaksi"
    }

    User {
        string id PK "ID user/sales person"
        string name "Nama lengkap"
        string email "Email"
        string role "admin | manager | sales | staff"
    }

    SalesPipeline }o--|| User : "assigned_to > User.id"
    SalesPipeline }o--o| Customer : "convert → customer_id"
    Customer }o--|| SalesPipeline : "sumber dari lead won"
```

### Tabel Schema — SalesPipeline

| Field | Tipe | Required | Default | Enum / Format | Deskripsi |
| - | - | - | - | - | - |
| `company_id` | `string` | **Ya** | — | — | ID perusahaan pemilik pipeline |
| `lead_id` | `string` | Tidak | — | — | ID prospek/lead (referensi eksternal) |
| `lead_name` | `string` | **Ya** | — | — | Nama lengkap prospek |
| `lead_email` | `string` | Tidak | — | email | Alamat email prospek |
| `lead_phone` | `string` | Tidak | — | — | Nomor telepon prospek |
| `company_name` | `string` | Tidak | — | — | Nama perusahaan prospek (B2B) |
| `assigned_to` | `string` | **Ya** | — | — | ID sales person yang bertanggung jawab |
| `pipeline_stage` | `string` | Tidak | `"prospect"` | `prospect \| qualified \| proposal \| negotiation \| won \| lost` | Tahap saat ini dalam pipeline |
| `estimated_value` | `number` | Tidak | — | — | Perkiraan nilai deal (dalam `currency`) |
| `currency` | `string` | Tidak | `"IDR"` | ISO 4217 | Mata uang estimasi nilai |
| `probability` | `number` | Tidak | `0` | 0–100 | Probabilitas konversi (%) — mempengaruhi weighted forecast |
| `expected_close_date` | `date` | Tidak | — | ISO 8601 | Target tanggal penutupan deal |
| `actual_close_date` | `date` | Tidak | — | ISO 8601 | Tanggal aktual deal ditutup (won/lost) |
| `source_lead` | `string` | Tidak | — | `website \| referral \| advertisement \| cold_call \| social_media \| event \| other` | Sumber asal prospek |
| `product_interest` | `array<string>` | Tidak | — | — | Daftar produk/jasa yang diminati prospek |
| `interaction_history` | `array<object>` | Tidak | — | — | Riwayat interaksi (call, email, meeting, message) |
| `next_action` | `string` | Tidak | — | — | Deskripsi aksi selanjutnya yang harus dilakukan |
| `next_action_date` | `date` | Tidak | — | ISO 8601 | Tanggal rencana aksi selanjutnya |
| `notes` | `string` | Tidak | — | — | Catatan tambahan tentang lead/deal |

**Sub-object `interaction_history[]`:**

| Field | Tipe | Format | Deskripsi |
| - | - | - | - |
| `interaction_date` | `date` | ISO 8601 | Tanggal interaksi |
| `interaction_type` | `string` | `call \| email \| meeting \| message` | Jenis interaksi |
| `notes` | `string` | — | Catatan hasil interaksi |
| `done_by` | `string` | — | ID user yang melakukan interaksi |

### Tabel Schema — Customer (Hasil Konversi Lead)

| Field | Tipe | Required | Default | Enum / Format | Deskripsi |
| - | - | - | - | - | - |
| `company_id` | `string` | **Ya** | — | — | ID perusahaan |
| `name` | `string` | **Ya** | — | — | Nama customer |
| `email` | `string` | Tidak | — | email | Alamat email |
| `phone` | `string` | **Ya** | — | — | Nomor telepon |
| `whatsapp_number` | `string` | Tidak | — | — | Nomor WhatsApp untuk follow up |
| `company` | `string` | Tidak | — | — | Nama perusahaan customer |
| `address` | `string` | Tidak | — | — | Alamat lengkap |
| `customer_type` | `string` | Tidak | `"individual"` | `individual \| business \| retail \| reseller \| distributor \| modern_market` | Tipe customer |
| `status` | `string` | Tidak | `"lead"` | `lead \| prospect \| customer \| inactive` | Status lifecycle customer |
| `source` | `string` | Tidak | — | — | Sumber customer (ads, referral, walk-in, dll) |
| `lifetime_value` | `number` | Tidak | `0` | — | Total nilai transaksi sepanjang waktu |
| `total_orders` | `number` | Tidak | `0` | — | Jumlah total transaksi |
| `average_order_value` | `number` | Tidak | `0` | — | Rata-rata nilai per transaksi |
| `last_purchase_date` | `date` | Tidak | — | ISO 8601 | Tanggal pembelian terakhir |
| `tags` | `array<string>` | Tidak | — | — | Label/tag kategorisasi customer |

### State Machine — Pipeline Management Workflow

```mermaid theme={null}
stateDiagram-v2
    direction LR

    [*] --> prospect: Lead baru dibuat<br/>(createSalesPipeline)

    prospect --> qualified: Verifikasi kebutuhan & budget<br/>probability: 10% → 25%
    prospect --> lost: Tidak qualify<br/>probability → 0%

    qualified --> proposal: Proposal dikirim<br/>probability: 25% → 50%
    qualified --> lost: Budget tidak sesuai<br/>probability → 0%

    proposal --> negotiation: Masuk fase negosiasi<br/>probability: 50% → 75%
    proposal --> lost: Proposal ditolak<br/>probability → 0%

    negotiation --> won: Deal closed!<br/>probability → 100%<br/>actual_close_date diisi
    negotiation --> lost: Negosiasi gagal<br/>probability → 0%

    won --> [*]: Convert to Customer<br/>(convertLeadToCustomer)
    lost --> [*]: Arsip / re-engage later

    state prospect {
        [*] --> InitialContact
        InitialContact --> Qualification
        Qualification --> [*]
    }

    state negotiation {
        [*] --> PriceDiscussion
        PriceDiscussion --> TermAgreement
        TermAgreement --> FinalReview
        FinalReview --> [*]
    }
```

**Transisi Stage & Probabilitas Otomatis:**

| Dari Stage | Ke Stage | Trigger | Probability Change |
| - | - | - | - |
| — | `prospect` | Create lead baru | → 10% |
| `prospect` | `qualified` | Verifikasi kebutuhan & budget | 10% → 25% |
| `qualified` | `proposal` | Proposal dikirim ke prospek | 25% → 50% |
| `proposal` | `negotiation` | Prospek masuk fase negosiasi | 50% → 75% |
| `negotiation` | `won` | Deal closed, kontrak ditandatangani | 75% → 100% |
| `prospect` | `lost` | Lead tidak qualify | → 0% |
| `qualified` | `lost` | Budget/kebutuhan tidak cocok | → 0% |
| `proposal` | `lost` | Proposal ditolak prospek | → 0% |
| `negotiation` | `lost` | Negosiasi gagal | → 0% |

### Sequence Diagram — Pipeline Analytics & Forecast Calculation

```mermaid theme={null}
sequenceDiagram
    participant U as User (Sales/Manager)
    participant UI as SalesPipelineManagement.jsx
    participant RQ as React Query Cache
    participant API as base44 API
    participant DB as Database

    rect rgb(230, 245, 255)
        Note over U,DB: Pipeline Analytics — 5 KPI Metrics Bar
        U->>UI: Buka halaman /sales-pipeline
        UI->>RQ: useQuery('salesPipelines')
        RQ-->>UI: Cache hit / stale check

        alt Cache miss atau stale
            UI->>API: listSalesPipelines()
            API->>DB: SELECT * FROM sales_pipelines WHERE company_id = ?
            DB-->>API: All pipeline records
            API-->>UI: Pipeline data[]
            UI->>RQ: Set cache (staleTime: 5 min)
        end

        Note over UI: Hitung 5 KPI secara client-side:
        UI->>UI: totalPipelineValue = SUM(estimated_value) WHERE stage ≠ lost
        UI->>UI: wonValue = SUM(estimated_value) WHERE stage = won
        UI->>UI: weightedForecast = SUM(estimated_value × probability/100)
        UI->>UI: conversionRate = won_count / (won_count + lost_count) × 100%
        UI->>UI: overdueLeads = COUNT(expected_close_date < today AND stage ∉ {won, lost})
        UI-->>U: Render metrics bar + Kanban/List view
    end
```

```mermaid theme={null}
sequenceDiagram
    participant U as User (Sales Person)
    participant UI as Lead Modal
    participant RQ as React Query
    participant API as base44 API
    participant DB as Database

    rect rgb(255, 245, 230)
        Note over U,DB: Stage Transition via Kanban Drag-and-Drop
        U->>UI: Drag card dari "proposal" ke "negotiation"
        UI->>UI: Validasi transisi: proposal → negotiation ✓
        UI->>UI: Set probability = 75%
        UI->>RQ: useMutation(updateSalesPipeline)
        RQ->>API: PATCH /salesPipelines/{id} {pipeline_stage: "negotiation", probability: 75}
        API->>DB: UPDATE sales_pipelines SET ...
        DB-->>API: Updated record
        API-->>RQ: Success response
        RQ->>RQ: invalidateQueries('salesPipelines')
        RQ->>API: Refetch all pipelines
        API->>DB: SELECT * FROM sales_pipelines
        DB-->>API: Fresh data
        API-->>RQ: Updated pipeline data[]
        RQ-->>UI: Re-render Kanban board
        UI-->>U: Card pindah kolom + metrics bar terupdate
    end
```

```mermaid theme={null}
sequenceDiagram
    participant U as User (Sales Person)
    participant UI as SalesPipelineManagement.jsx
    participant RQ as React Query
    participant API as base44 API
    participant DB as Database
    participant CRM as Customer Entity

    rect rgb(230, 255, 230)
        Note over U,CRM: Lead-to-Customer Conversion Flow
        U->>UI: Klik "Convert to Customer" pada lead won
        UI->>API: Cek duplikat by email/nama
        API->>CRM: SELECT * FROM customers WHERE email = ? OR name = ?
        CRM-->>API: Existing customers

        alt Duplikat ditemukan
            API-->>UI: Existing customer found
            UI->>API: updateSalesPipeline({customer_id: existing_id})
            API->>DB: UPDATE sales_pipelines SET customer_id = ?
        else Tidak ada duplikat
            UI->>API: createCustomer(lead_data)
            API->>CRM: INSERT INTO customers (name, email, phone, ...)
            CRM-->>API: New customer_id
            API->>DB: UPDATE sales_pipelines SET customer_id = new_id
        end

        DB-->>API: Updated
        API-->>RQ: Success
        RQ->>RQ: invalidateQueries('salesPipelines') + invalidateQueries('customers')
        RQ-->>UI: Re-render
        UI-->>U: Lead ter-convert, customer linked
    end
```

### Sequence Diagram — Weighted Forecast Calculation

```mermaid theme={null}
sequenceDiagram
    participant UI as Metrics Bar
    participant DATA as Pipeline Data[]
    participant CALC as Forecast Engine (client)

    rect rgb(245, 230, 255)
        Note over UI,CALC: Weighted Forecast — Prediksi Revenue
        DATA->>CALC: pipeline[] (semua lead aktif)

        loop Untuk setiap lead WHERE stage ∉ {lost}
            CALC->>CALC: weighted_value = estimated_value × (probability / 100)
            CALC->>CALC: accumulator += weighted_value
        end

        CALC-->>UI: weightedForecast = Rp 71.200.000

        Note over UI: Breakdown per stage:
        UI->>UI: prospect: 5 leads × Rp 9jt × 10% = Rp 4.5jt
        UI->>UI: qualified: 3 leads × Rp 9.3jt × 25% = Rp 7jt
        UI->>UI: proposal: 2 leads × Rp 17.5jt × 50% = Rp 17.5jt
        UI->>UI: negotiation: 1 lead × Rp 15jt × 75% = Rp 11.25jt
        UI->>UI: won: 4 leads × Rp 15.5jt × 100% = Rp 62jt (pasti)
    end
```

### Enum Tables

#### `pipeline_stage` — Tahap Pipeline Penjualan

| Nilai | Label | Probabilitas Default | Deskripsi |
| - | - | - | - |
| `prospect` | Prospect | 10% | Lead baru masuk, belum diverifikasi kebutuhan dan budgetnya |
| `qualified` | Qualified | 25% | Kebutuhan & budget terkonfirmasi, prospek layak dilanjutkan |
| `proposal` | Proposal | 50% | Proposal/penawaran sudah dikirim ke prospek |
| `negotiation` | Negotiation | 75% | Sedang dalam fase negosiasi harga dan ketentuan |
| `won` | Won | 100% | Deal berhasil ditutup, revenue sudah diamankan |
| `lost` | Lost | 0% | Deal gagal, prospek tidak lanjut (arsip untuk re-engage) |

#### `source_lead` — Sumber Prospek

| Nilai | Label | Deskripsi |
| - | - | - |
| `website` | Website | Prospek datang dari website perusahaan |
| `referral` | Referral | Referensi dari customer atau partner existing |
| `advertisement` | Advertisement | Dari kampanye iklan (digital/print) |
| `cold_call` | Cold Call | Hasil dari outbound calling |
| `social_media` | Social Media | Dari platform media sosial |
| `event` | Event | Dari event, seminar, atau pameran |
| `other` | Lainnya | Sumber lainnya |

#### `interaction_type` — Jenis Interaksi

| Nilai | Label | Deskripsi |
| - | - | - |
| `call` | Telepon | Interaksi melalui panggilan telepon |
| `email` | Email | Interaksi melalui email |
| `meeting` | Meeting | Pertemuan tatap muka atau virtual meeting |
| `message` | Pesan | Interaksi melalui chat/pesan (WhatsApp, dll) |

#### `forecast_method` — Metode Forecast (Konseptual)

| Metode | Formula | Kapan Digunakan |
| - | - | - |
| **Weighted Pipeline** | `SUM(estimated_value × probability/100)` | Default — semua stage aktif |
| **Stage-Value** | `SUM(estimated_value) WHERE stage = won` | Konservatif — hanya yang sudah won |
| **Total Pipeline** | `SUM(estimated_value) WHERE stage ∉ {lost}` | Optimis — seluruh potensi revenue |
| **Conversion-Based** | `total_leads × avg_conversion_rate × avg_deal_value` | Perencanaan jangka panjang |

### RBAC — Hak Akses Sales Pipeline

| Role | Create Lead | Read Pipeline | Update Lead | Delete Lead | Convert to Customer | View All Team Leads |
| - | - | - | - | - | - | - |
| **Admin** | ✅ Full access | ✅ Semua data | ✅ Semua data | ✅ Semua data | ✅ | ✅ |
| **Manager** | ✅ | ✅ Semua data (company-scoped) | ✅ | ✅ | ✅ | ✅ |
| **Sales Person** | ✅ | ✅ Milik sendiri + company | ✅ Milik sendiri | ✅ Milik sendiri | ✅ | ❌ |
| **Staff** | ❌ | ✅ Milik sendiri | ❌ | ❌ | ❌ | ❌ |

**Row-Level Security (RLS) Rules:**

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `company_id = user.active_company_id` AND `company_id ∉ {null, ""}` | Harus dalam perusahaan aktif |
| **Read** | `company_id = user.active_company_id` OR `created_by_id = user.id` OR `role = admin` | Company-scoped + creator + admin |
| **Update** | `company_id = user.active_company_id` OR `created_by_id = user.id` OR `role = admin` | Company-scoped + creator + admin |
| **Delete** | `company_id = user.active_company_id` OR `created_by_id = user.id` OR `role = admin` | Company-scoped + creator + admin |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.