# 🔍 Analisis Mendalam Modul `kompensasiharga`

## Arsitektur Modul

```mermaid
graph TB
    subgraph "Controller Hierarchy"
        MX["MX_Controller"]
        MC["Modul_Controller"]
        TR["Transaksi.php (6807 lines)"]
        CR["Create.php (11579 lines)"]
        FU["FollowUp.php (1504556 bytes)"]
        SC["_shoppingCart.php"]
        PSN["_processSelectNota.php"]
        PSNI["_processSelectNotaItem.php"]
        PSP["_processSelectProduct.php"]
        SI["_selectorItem.php"]
        SP["_selectorPihak.php"]
        SPM["_selectorPihakMain.php"]
        PR["Printing.php"]
        AR["ActivityReport.php"]
        
        MX --> MC
        MC --> TR
        MC --> CR
        MC --> FU
        MC --> SC
        MC --> PSN
        MC --> PSNI
        MC --> PSP
        MC --> SI
        MC --> SP
        MC --> SPM
        MC --> PR
        MC --> AR
    end
```

---

## 1. `Modul_Controller.php` — Base Controller

**File:** [Modul_Controller.php](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Modul_Controller.php)

Standar HMVC base controller dengan:
- Session validation (`gotoLogin()`, `validateUserSession()`)
- Load 4 config wajib: `coTransaksiUi`, `coTransaksiCore`, `coTransaksiLayout`, `coTransaksiValues`
- Parse `jenisTr` dari URI segment 4
- Build `cCode = "_TR_" + jenisTr`

---

## 2. `Transaksi::index()` — Halaman Index DataTable

**File:** [Transaksi.php L4258-L4393](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Transaksi.php#L4258-L4393)

### Alur Kerja

```mermaid
flowchart TD
    A["Transaksi::index()"] --> B["Validasi Login Session"]
    B --> C["Baca Config UI: paymentConfig, historyFields, steps"]
    C --> D["Cek Hak Akses: placeCanMakeTrans_he_menu()"]
    D --> E{"Boleh Create?"}
    E -->|Ya| F["Build addLink → Create/index/{jenisTr}"]
    E -->|Tidak| G["addLink = null"]
    F --> H["Deteksi Mobile: isMobile_he_misc()"]
    G --> H
    H --> I["Build Script Bottom (3 Panel AJAX Loader)"]
    I --> J["Load View: transaksi"]
```

### 3 Panel AJAX yang Di-load Setelah Index Render

| Panel ID | Target URL | Fungsi |
|---|---|---|
| `#undoneList` | `Transaksi/viewUndoneItemsIndex/{jenisTr}` | Daftar transaksi belum selesai |
| `#historyList` | `History/showData/{jenisTr}` | Riwayat transaksi 10 terakhir |
| `#klaimList` | `Transaksi/viewKlaimSupplierIndex/{jenisTr}` | **Daftar Klaim ke Supplier** |

> [!IMPORTANT]
> Untuk `jenisTr = '3333'` (Realisasi Klaim), panel `#undoneList` di-hide dan di-empty. Hanya `#historyList` dan `#klaimList` yang ditampilkan.

### Data yang Dikirim ke View

```php
$data = array(
    "mode"        => "index",
    "template"    => $configUi[$jenisTr]["template"],
    "title"       => $configUi[$jenisTr]["label"],
    "subTitle"    => $configUi[$jenisTr]["steps"][1]['label'],
    "jenisTr"     => $jenisTr,
    "addLink"     => $addLink,     // Link ke Create
    "scriptBotto" => $scriptBottom, // AJAX loaders
    "steps"       => $steps,
    "allSteps"    => $allSteps,
    "linkMenu"    => $linkMenu,
);
```

---

## 3. `Transaksi::viewKlaimSupplierIndex()` — Panel Klaim Supplier

**File:** [Transaksi.php L5722-L5800](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Transaksi.php#L5722-L5800)

Ini adalah **container HTML** yang menampilkan DataTable server-side untuk "DAFTAR KLAIM KE SUPPLIER". 

### Fitur Utama
- **Paired Model Lookup:** Load `MdlSupplier` untuk dropdown filter supplier
- **Filter by Supplier:** Jika `$_GET['sid']` ada, filter per supplier tertentu
- **Server-Side DataTable URL:** `Transaksi/viewKlaimSupplierDataAjax/{jenisTr}`

---

## 4. `Transaksi::viewKlaimSupplierDataAjax()` — Server-Side DataTable Engine

**File:** [Transaksi.php L5802-L6639](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Transaksi.php#L5802-L6639)

### Diagram Alur Query

```mermaid
flowchart TD
    A["viewKlaimSupplierDataAjax"] --> B["Parse DataTable Params"]
    B --> C["Build whereBase"]
    C --> D["Count Total Records"]
    D --> E["Count Sisa: JOIN locker UNION cache"]
    E --> F["Hitung Badge Counts per Diskon 1-8"]
    F --> G["Hitung Kategori Badge"]
    G --> H{"Filter Aktif?"}
    H -->|reguler| I1["JOIN: has_fp=0, has_reg=1"]
    H -->|freeproduk| I2["JOIN: has_fp=1, has_reg=0"]
    H -->|kombinasi| I3["JOIN: has_fp=1, has_reg=1"]
    H -->|d1 s/d d8| I4["JOIN: produk_id = N spesifik"]
    H -->|sisa - default| I5["JOIN: semua yang nilai masih ada"]
    I1 --> J
    I2 --> J
    I3 --> J
    I4 --> J
    I5 --> J
    J["Search Value Processing"] --> K["Reverse Prefix Translation Map"]
    K --> L["Counter Extraction"]
    L --> M["PO/Pre-PO Relation Expansion"]
    M --> N["Query Page Data + Lookups"]
    N --> O["Build JSON Response"]
```

### Sumber Data Diskon (Dual-Source + Fallback)

```mermaid
flowchart LR
    subgraph "Sumber Primer"
        SL["stock_locker_diskon"]
    end
    subgraph "Sumber Sekunder - Fallback"
        SP["subpiutangsuppliertrans_cache"]
    end
    subgraph "Mutasi Klaim"
        MK["subpiutangsuppliertrans 1010020030"]
    end

    SL --> |"UNION"| COMBINED["Combined Result Set"]
    SP --> |"UNION"| COMBINED
    COMBINED --> BADGES["Badge Counts + Filter"]
    MK --> KLAIM_HISTORY["Riwayat Klaim Diproses"]
```

### Logika Fallback Per-Slot Diskon

Setiap transaksi GRN menampilkan hingga 8 slot diskon (`diskon_1_nilai` s/d `diskon_8_nilai`). Logika pengisian:

1. **Prioritas:** Data dari `stock_locker_diskon` (locker fisik)
2. **Fallback:** Jika slot locker kosong (`nilai = 0`), cek `_rek_pembantu_subpiutangsuppliertrans_cache` (subpiutang cache) untuk slot yang sama
3. **Agregasi:** Total `sisa_diskon` dihitung dari gabungan keduanya

### Badge Filter System

| Filter Key | Keterangan | Logika SQL |
|---|---|---|
| `all` | Semua nota dengan sisa diskon | UNION locker + cache, nilai >= 1 |
| `reguler` | Hanya diskon reguler (tanpa free produk) | has_fp=0, has_reg=1 |
| `freeproduk` | Hanya free produk (produk_id=7) | has_fp=1, has_reg=0 |
| `kombinasi` | Campuran reguler + free produk | has_fp=1, has_reg=1 |
| `d1`..`d8` | Diskon spesifik per produk_id | produk_id = N |

### Reverse Prefix Translation Map (Search)

Map pencarian dari format tampilan UI ke kode database:

| UI Format | DB Code |
|---|---|
| `PRE.PO.FG.IMP` | `460r` |
| `PO.FG.IMP` | `460a` |
| `GRN.IMP` | `460` |
| `PRE.PO.FG` | `466r` |
| `PO.FG` | `466` |
| `PRE.GRN` | `467r` |
| `GRN` | `467` |
| `KPH` | `3333` |

### JSON Response Structure

```json
{
    "draw": 1,
    "recordsTotal": 150,
    "recordsFiltered": 45,
    "data": [["<checkbox>", 1, "2026-01-15", "PT ABC", "467.-1.3420", ...]],
    "countSisa": 45,
    "countTotal": 150,
    "diskonBadges": {
        "all": 45, "reguler": 20, "freeproduk": 15, "kombinasi": 10,
        "d1": 30, "d2": 25, "d3": 10, "d4": 5, "d5": 3, "d6": 2, "d7": 15
    },
    "sumFooterGlobal": { "diskon_1_nilai": 5000000, "sisa_diskon": 15000000 },
    "sumFooterPage": { "diskon_1_nilai": 500000 }
}
```

---

## 5. `Transaksi::initKlaimQueue()` — Inisialisasi Antrian Klaim

**File:** [Transaksi.php L6642-L6729](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Transaksi.php#L6642-L6729)

### Alur

```mermaid
flowchart TD
    A["POST dari JavaScript<br/>(checkbox selections)"] --> B["Parse items[] dari POST"]
    B --> C{"Ada items valid?"}
    C -->|Tidak| D["Return error JSON"]
    C -->|Ya| E["Loop validasi: trash_4 != 1"]
    E --> F{"Ada yang dibatalkan?"}
    F -->|Ya| G["Return error: nota diretur"]
    F -->|Tidak| H["Build queueItems array"]
    H --> I["Reset session _TR_3333"]
    I --> J["Simpan ke SESSION klaim_queue_3333"]
    J --> K["Return JSON: redirect → Create/index/3333?queueMode=1"]
```

### Struktur Session Antrian

```php
$_SESSION['klaim_queue_3333'] = array(
    "current_index" => 0,
    "supplier_id"   => 123,
    "supplier_nama"  => "PT ABC",
    "is_freeproduk"  => 0,
    "items" => array(
        0 => array(
            "grn_id"        => 456,
            "nomer"         => "467.-1.3420",
            "sisa"          => 5000000.0,
            "supplier_id"   => 123,
            "supplier_nama" => "PT ABC",
            "is_freeproduk" => 0,
            "status"        => "pending",   // → "completed" setelah save()
            "klaim_nomer"   => "",          // → diisi nomer nota klaim setelah save()
            "klaim_dtime"   => "",          // → diisi timestamp setelah save()
        ),
        1 => array( /* item berikutnya */ ),
    ),
    "total_items" => 5,
    "created_at"  => "2026-01-15 10:30:00",
);
```

---

## 6. `Create::index()` — Halaman Pembuatan Klaim

**File:** [Create.php L47-L1187](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Create.php#L47-L1187)

### Diagram Alur Utama

```mermaid
flowchart TD
    A["Create::index()"] --> B["Load Config + Init"]
    B --> C["Build initMasterValues"]
    C --> D["heInitGates_he_cart()"]
    D --> E["Generate bookingNumber"]
    E --> F{"jenisTr == 3333?"}
    F -->|Tidak| Z["Normal Create Flow"]
    F -->|Ya| G["Antrean Klaim Logic"]
    
    G --> H{"GET jumpQueue?"}
    H -->|Ya| H1["Set current_index, Reset Session, Redirect"]
    
    G --> I{"GET skipQueue?"}
    I -->|Ya| I1["Find Next Pending"]
    I1 --> I2{"Found?"}
    I2 -->|Ya| I3["Set current_index, Redirect"]
    I2 -->|Tidak + hasCompleted| I4["buildQueueSummaryHtml → renderQueueSuccessPage"]
    I2 -->|Tidak + !hasCompleted| I5["unset queue, redirect to Index"]
    
    G --> J{"GET removeQueue?"}
    J -->|Ya| J1["Remove item, reindex array"]
    J1 --> J2["Find next pending or show summary"]
    
    G --> K{"GET cancelQueue?"}
    K -->|Ya| K1["Show summary if hasCompleted, then unset queue"]
    
    G --> L["Auto-load Queue Item"]
    L --> M["loadQueueGrnIntoSession()"]
    M --> N["Build Floating Queue Widget HTML"]
    N --> O["Render Create Form + Widget"]
```

### Queue Command Parameters

| GET Parameter | Fungsi |
|---|---|
| `queueMode=1` | Aktifkan mode antrian |
| `jumpQueue={idx}` | Loncat ke item antrian tertentu |
| `skipQueue=1` | Lewati item aktif, pindah ke berikutnya |
| `removeQueue={idx}` | Keluarkan item dari antrian |
| `cancelQueue=1` | Batalkan seluruh antrian |

---

## 7. `Create::loadQueueGrnIntoSession()` — Auto-Load Item ke Session

**File:** [Create.php L1190-L1481](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Create.php#L1190-L1481)

### Langkah-langkah

```mermaid
flowchart TD
    A["loadQueueGrnIntoSession()"] --> B["1. Reset items/items2/items2_sum"]
    B --> C["2. Set Branch = PUSAT (DC), cabangID=-1"]
    C --> D["3. Set Supplier dari parameter antrian"]
    D --> E["4. Set Nota/GRN aktif: pihakMainID, refs"]
    E --> F["5. Lookup PPN Vendor dari Registry GRN"]
    F --> G["6. Load stock_locker_diskon (MdlLockerStockDiskonVendor)"]
    G --> H{"isFreeProduk?"}
    H -->|Ya| H1["Filter: produk_id=7, jumlah>0"]
    H -->|Tidak| H2["Filter: produk_id IN (0..6,8), nilai>0"]
    H1 --> I["Build items[] dan items2[]"]
    H2 --> I
    I --> J["7. Build items2_sum"]
    J --> K["8. Preset Form Elements via heFetchElement_modul()"]
    K --> L["9. Run ValueGate::buildValue()"]
```

### Detail Session Items yang Dibangun

Setiap item diskon dari `stock_locker_diskon` dimuat ke session dengan struktur:

```php
$_SESSION[$cCode]['items'][$key_header] = array(
    "handler"        => "kompensasiharga/_processSelectNota",
    "id"             => $row_id,       // ID locker stock diskon
    "refID"          => $grnId,        // ID transaksi GRN
    "jml"            => $itemJml,      // Qty (biasanya 1)
    "harga"          => $itemHarga,    // Sisa diskon value
    "subtotal"       => $jml * $harga,
    "diskon_id"      => $produk_id,    // 1-8 (slot diskon)
    "diskon_nama"    => $nama,         // Nama slot diskon
    "nama"           => $nomer,        // Nomer nota GRN
    "transaksi_id"   => $grnId,
    "ppnFactor"      => $finalPpnVendor,
    // ... field lainnya
);
```

---

## 8. `Create::save()` — Queue Completion After Save

**File:** [Create.php L5379-L5430](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Create.php#L5379-L5430)

### Alur Penyelesaian Queue Setelah Simpan

```mermaid
flowchart TD
    A["save() berhasil"] --> B{"Queue aktif?"}
    B -->|Tidak| Z["Normal success redirect"]
    B -->|Ya| C["Mark items[currIdx].status = 'completed'"]
    C --> D["Simpan klaim_nomer dan klaim_dtime"]
    D --> E["Cari item pending berikutnya"]
    E --> F{"Ada pending?"}
    F -->|Ya| G["Set current_index = nextIdx"]
    G --> H["Redirect ke Create/index/3333?queueMode=1"]
    F -->|Tidak| I["queueCompleted = true"]
    I --> J["buildQueueSummaryHtml()"]
    J --> K["unset SESSION klaim_queue_3333"]
    K --> L["SweetAlert2: Proses Antrian Sukses"]
    L --> M["Redirect ke Transaksi/Index/3333"]
```

---

## 9. Helper Functions

### `buildQueueSummaryHtml()` 
**File:** [Create.php L11419-L11512](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Create.php#L11419-L11512)

Membangun HTML tabel ringkasan hasil antrian klaim:
- Deteksi multi-supplier (kolom supplier tambahan jika mixed)
- Tampilkan No. GRN, No. Nota Klaim, Nilai Klaim, Status per item
- Header card: total completed, total nilai klaim

### `renderQueueSuccessPage()`
**File:** [Create.php L11514-L11577](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Create.php#L11514-L11577)

Halaman standalone sukses dengan:
- Panel Bootstrap `panel-success`
- SweetAlert2 popup otomatis
- Tombol "Kembali ke Daftar Klaim"

---

## 10. Utility Functions

### `cekSaldoDiskonGrn()` dan `cekSaldoDiskonGrnCache()`
**File:** [Transaksi.php L6731-L6805](file:///w:/everest_opname28sep/application/modules/kompensasiharga/controllers/Transaksi.php#L6731-L6805)

| Function | Tabel Target | Fungsi |
|---|---|---|
| `cekSaldoDiskonGrn($kiriman)` | `_rek_pembantu_subpiutangsuppliertrans_cache` | Reset debet=0 untuk array of extern_id |
| `cekSaldoDiskonGrnCache($kiriman)` | `_rek_pembantu_subpiutangsuppliertrans_cache` | Update debet per supplier/transaksi/diskon_id |

---

## 11. Peta Tabel Database yang Digunakan

| Tabel | Kegunaan |
|---|---|
| `transaksi` | Master transaksi (GRN jenis=467, Klaim jenis=3333) |
| `stock_locker_diskon` | Locker fisik sisa diskon per GRN per slot |
| `_rek_pembantu_subpiutangsuppliertrans_cache` | Cache subpiutang supplier (fallback) |
| `__rek_pembantu_subpiutangsuppliertrans__1010020030` | Mutasi buku pembantu piutang supplier |
| `transaksi_data_registries` | Registry values (main, ppnFactor, dll) |

---

## 12. Config Key: `viewKlaimSupplier`

**File:** [coTransaksiUi.php L3515-L3605](file:///w:/everest_opname28sep/application/modules/kompensasiharga/config/coTransaksiUi.php#L3515-L3605)

```
viewKlaimSupplier
├── enabled: true
├── jenisSource: ["467"]          ← Jenis GRN yang ditampilkan
├── historyFields                 ← Kolom DataTable
│   ├── dtime → "date"
│   ├── suppliers_nama → "supplier/vendor"
│   ├── nomer → "Nomer GRN"
│   ├── po → {step:2, key:"nomer"} ← Nomer PO dari ids_his
│   ├── oleh_nama → "person"
│   ├── diskon_1_nilai..diskon_8_nilai
│   └── keterangan
├── historySumFields              ← Footer sum
├── historyKlaimFields            ← Mapping nilai_diklaim per slot
├── pairedModel
│   ├── comName: ComRekeningPembantuPiutangSupplierDetailTransMain
│   └── filter: [periode=forever, rekening=1010020030]
├── addPairedModel
│   └── mdlName: MdlSupplier
├── addPairedModelLocker
│   ├── mdlName: MdlLockerStockDiskonVendor
│   └── filter: [jenis=diskon, state=active]
├── addTabHeaderLimit: 10
└── labelAlias
    ├── 3 → "belum diklaim"
    ├── 4 → "klaim sebagian"
    └── 5 → "sudah klaim (habis)"
```

---

## 13. Flow End-to-End: Antrian Klaim Diskon

```mermaid
sequenceDiagram
    actor User
    participant UI as "DataTable Klaim"
    participant TR as "Transaksi.php"
    participant CR as "Create.php"
    participant DB as "Database"
    
    User->>UI: Centang beberapa GRN checkbox
    User->>UI: Klik "Proses Klaim Terpilih"
    UI->>TR: POST initKlaimQueue() {items[], mode}
    TR->>DB: Validasi trash_4 != 1 per GRN
    TR-->>UI: JSON {status:success, redirect:Create/index/3333?queueMode=1}
    UI->>CR: GET Create/index/3333?queueMode=1
    
    loop Setiap Item Antrian
        CR->>CR: loadQueueGrnIntoSession()
        CR->>DB: Load stock_locker_diskon untuk GRN aktif
        CR-->>User: Render Create Form + Floating Widget
        User->>CR: Submit klaim (Transaksi/validate → Create/save)
        CR->>DB: INSERT transaksi (jenis=3333)
        CR->>CR: Mark items[idx].status = 'completed'
        CR->>CR: Find next pending item
        
        alt Ada pending berikutnya
            CR-->>User: Auto-redirect ke item berikutnya
        else Semua selesai
            CR->>CR: buildQueueSummaryHtml()
            CR-->>User: SweetAlert2 "PROSES ANTRIAN SUKSES"
        end
    end
    
    User->>UI: Klik "Kembali ke Daftar Klaim"
```

---

## 14. linkMenu di Index

Config `linkMenu` menampilkan 2 sub-link di halaman index:

| # | Link | Label |
|---|---|---|
| 1 | `Create/index/3344` | TAMBAHAN DISKON |
| 2 | `Create/index/3333` | REALISASI KLAIM KE SUPPLIER |

Jenis transaksi yang ada di modul ini:
- **`3333`** — Realisasi Klaim ke Supplier (main)
- **`3344`** — Tambahan Diskon oleh Supplier (Rebate/mendapat barang)
