# KONTEKS FITUR: CHECKBOX SELEKSI & BAR REKAPITULASI PADA DAFTAR KLAIM KE SUPPLIER (MODUL KOMPENSASI HARGA)

Dokumen ini mencatat seluruh kronologi, analisis arsitektur, implementasi teknis yang telah selesai, serta peta rencana lanjutan (*roadmap*) terkait fitur seleksi nota pada menu **"DAFTAR KLAIM KE SUPPLIER"** (Transaksi `3333`, modul `kompensasiharga`).

---

## 1. Latar Belakang & Kronologi Masalah

### A. Insiden Percobaan Awal (Otomatisasi Antrean yang Mencemari Manual)
1. Pada pengujian awal, dibangun sistem otomatisasi antrean klaim (*queue*) yang mencoba mengisi form transaksi `3333` secara otomatis per nota GRN.
2. **Akar Masalah (*Root Cause*):**
   - Otomatisasi tersebut menggunakan sesi global `$_SESSION['klaim_queue']`.
   - Di dalam controller inti `Create.php` dan `_processPihakMain.php`, terdapat injeksi pembacaan sesi tersebut yang memaksakan ID supplier dan mengeksekusi *iframe auto-select* GRN.
   - Dampaknya: pengguna yang membuka transaksi pembuatan manual `3333` (`Create/index/3333`) mendapati form terkunci pada data supplier antrean lama, data GRN termuat paksa, dan transaksi manual tidak dapat digunakan secara wajar.

### B. Keputusan Reset ke Titik Nol (*Ground Zero*)
1. Developer menginstruksikan untuk **kembali ke titik nol** dengan menormalkan seluruh alur transaksi manual.
2. Seluruh hook otomatisasi dicabut dari berkas transaksi inti:
   - `Create.php` (`index()`, `preview()`, `save()`).
   - `_processPihakMain.php` (`select_2()`).
   - `_selectorPihak.php` & `_selectorPihakMain.php` (konfirmasi SweetAlert dikembalikan).
   - View `transaksi.php` (banner antrean dicabut).
3. Transaksi manual `3333` terkonfirmasi pulih 100% normal, bersih, dan stabil.

### C. Strategi Sandbox di Modul Salinan
1. Untuk mencegah risiko pada modul aktif, Developer menyiapkan salinan modul bersih di folder `application/modules/kompensasiharga_ori`.
2. Diputuskan untuk menerapkan **Opsi 1** (Fitur Checkbox UI & Bar Ringkasan / Rekapitulasi Data) terlebih dahulu di modul salinan.
3. Setelah seluruh pengujian (termasuk perbaikan bug konflik DataTables) berhasil, Developer me-*rename* modul `kompensasiharga_ori` menjadi modul utama **`kompensasiharga`**.

---

## 2. Arsitektur & Status Implementasi Saat Ini (Status Quo - Opsi 1 Selesai)

Saat ini modul aktif `application/modules/kompensasiharga` telah memiliki fitur seleksi interaktif dengan status **stabil**:

### Berkas yang Terlibat
1. **[Transaksi.php](file:///w:/everest_16sep/application/modules/kompensasiharga/controllers/Transaksi.php)**
   - Fungsi `viewKlaimSupplierIndex()`:
     Menyertakan atribut numerik pada array `$tmp` baris tabel:
     - `$tmp['id'] = $row->id;` (ID nota GRN)
     - `$tmp['suppliers_id'] = $row->suppliers_id;`
     - `$tmp['nomer_clean'] = $row->nomer;` (Nomor nota bersih)
     - `$tmp['sisa_diskon_raw'] = isset($row->sisa_diskon) ? $row->sisa_diskon : 0;`

2. **[transaksi.php](file:///w:/everest_16sep/application/modules/kompensasiharga/views/transaksi.php)**
   - Blok `case "viewKlaimSupplierIndex":`:
     - **Bar Ringkasan Akumulasi (`#batch_klaim_action_bar`):**
       Muncul saat supplier tertentu dipilih (`$pihakDataSelected > 0`). Menampilkan badge `Terpilih: X Nota` dan `Total Sisa Diskon: Rp Y` secara dinamis.
     - **Tombol Batalkan Pilihan:** Muncul otomatis jika ada centang aktif untuk mereset seluruh pilihan.
     - **Kolom Checkbox Header:** Checkbox master `#check_all_grn_klaim` untuk memilih seluruh nota yang memiliki sisa diskon $> 0$.
     - **Kolom Checkbox Baris Data:** Checkbox `.check_grn_klaim` aktif untuk nota bersisa diskon $> 0$, dan otomatis `disabled` jika sisa diskon $\le 0$.
     - **State JavaScript (`window.selectedKlaimGrnMap`):** Pilihan nota disimpan di memori objek JS, sehingga status centang tetap terjaga saat berpindah halaman paginasi DataTables, saat sorting, maupun saat pencarian data.

---

## 3. Catatan Teknis Bug Sorting DataTables & Solusinya

### Gejala Masalah
Saat checkbox *Check All* diklik, event klik merambat (*bubbling*) ke elemen `<th>` DataTables, memicu fungsi pengurutan kolom (*column sorting*) dan *redraw* tabel. Hal ini membatalkan atau mereset centang massal.

### Solusi 3 Lapis yang Diterapkan:
1. **HTML Layer:** Menyematkan `onclick='event.stopPropagation();'` langsung pada tag `<input type='checkbox' id='check_all_grn_klaim'>`.
2. **DataTables Configuration Layer:**
   ```javascript
   order: [[ 1, 'asc' ]], // Default sort ke kolom No (indeks 1), bukan kolom checkbox
   columnDefs: [
       { orderable: false, targets: 0 } // Matikan sorting khusus kolom ke-0 (checkbox)
   ],
   ```
3. **CSS Layer:**
   Menyembunyikan icon panah sorting DataTables (`:before` & `:after`) pada kolom pertama (`th:first-child`, `th.no-sort`).
4. **jQuery Layer:** Menambahkan `e.stopPropagation()` pada seluruh event listener `click` dan `change` untuk checkbox master dan checkbox baris.

---

## 4. Rencana Lanjutan di Masa Depan (Next Steps)

Ketika fitur ini hendak dilanjutkan ke tahap pemrosesan transaksi otomatis (Opsi 2 / Batch Action), berikut pedoman teknis yang wajib diperhatikan:

### A. Karakteristik Arsitektur Transaksi 3333
- Transaksi `3333` (*Kompensasi Harga*) didesain secara relasional mengikat **1 Nota GRN** (`pihakMainID`) per transaksi.
- Tabel relasi `stock_locker_diskon` mencatat pemotongan saldo diskon per kombinasi `(transaksi_id, supplier_id, produk_id)`.

### B. Rekomendasi Pendekatan untuk Aksi Batch / Antrean:
1. **Opsi Sequential Queue Terisolasi (Direkomendasikan):**
   - Jika pengguna memilih $N$ nota GRN lalu menekan "Proses Klaim Terpilih":
   - **JANGAN** menggunakan sesi global liar yang merusak form manual.
   - Gunakan tabel antrean sementara di database / MongoDB atau token unik di sesi dengan URL spesifik:
     `Create/index/3333?queue_token=XYZ&step=1`
   - Form manual `Create/index/3333` (tanpa parameter `queue_token`) harus tetap **100% steril dan independen**.
2. **Opsi Multi-GRN dalam 1 Nota Transaksi (Perubahan Masif):**
   - Jika bisnis menginginkan banyak nota GRN digabung ke dalam 1 nomor transaksi `3333`:
   - Diperlukan perombakan pada skema `transaksi_values` dan tabel perantara agar keranjang belanja dapat menampung multi-`pihakMainID`. Hal ini memerlukan audit mendalam pada jurnal akuntansi dan modul pencairan kompensasi.

---

## 5. Optimasi Initial Load Time Halaman (Opsi 2 - Selesai Diimplementasikan)

### Latar Belakang Masalah Performa
Saat halaman `kompensasiharga/Transaksi/index/3333?gr=cGVtYmVsaWFu` pertama kali dibuka, sistem memuat seluruh riwayat GRN tanpa filter supplier (`sid == 0`), mencakup **5.355 baris data**. Hal ini menyebabkan:
- Transfer payload HTML raksasa (~6-8 MB).
- Browser macet/membeku (*freeze*) selama 5-8 detik untuk mem-parse 5.355 tag `<tr>` dan inisialisasi DataTables.
- 5.160 baris di antaranya adalah data arsip yang sisa diskonnya sudah Rp 0.

### Solusi yang Diimplementasikan
1. **Default State (`show=ready`):**
   - Halaman pertama kali dibuka secara otomatis hanya memuat nota yang berstatus **"Siap Diklaim"** (`sisa_diskon > 0`, sekitar ~195 baris).
   - Ukuran payload HTML terpangkas 96% (turun dari ~8 MB menjadi ~250 KB).
   - Browser merender tabel secara instan (< 400 milidetik) tanpa pembekuan tab.
   - Tombol toolbar DataTables aktif pada `Siap Diklaim (195)`.
2. **On-Demand Reload untuk Semua Data (`show=all`):**
   - Tombol `Tampilkan Semua Data (5355)` tetap tersedia.
   - Saat diklik, tombol melakukan AJAX on-demand reload (`#klaimList.load(...)` dengan parameter `show=all`) disertai animasi loading HoldOn.
   - User tetap memiliki kendali penuh untuk melihat seluruh 5.355 data arsip kapan pun dibutuhkan.
3. **Sinkronisasi Dropdown Supplier:**
   - Pemilihan supplier pada dropdown `#supplier_klaim_select` otomatis meneruskan status `$showMode` yang sedang aktif.

---

## 7. Optimasi Latensi 3 Kontainer Loading Index (`Transaksi/index/3333` - Selesai Diimplementasikan)
Saat halaman `kompensasiharga/Transaksi/index/3333` dibuka, terdapat 3 kontainer AJAX vertikal:
1. `#undoneList` (`viewUndoneItemsIndex`)
2. `#klaimList` (`viewKlaimSupplierIndex`)
3. `#historyList` (`History/showData`)

### Masalah yang Ditemukan
- **PHP Session Locking:** Ketiga request AJAX memiliki `PHPSESSID` yang sama. Secara default, PHP mengunci file session sehingga Apache/PHP mengeksekusi ketiganya secara serial/antrean. Request lambat menahan request lainnya.
- **Artificial Delay:** Terdapat `setTimeout(..., 1000)` buatan di JavaScript sebelum masing-masing `.load()`.

### Solusi yang Diimplementasikan (18 September 2026):
1. **Eliminasi Delay Buatan ([Transaksi.php:4320-4348](file:///w:/everest_16sep/application/modules/kompensasiharga/controllers/Transaksi.php#L4320-L4348)):**
   - Menghapus pembungkus `setTimeout(..., 1000)` pada ketiga loader JS (`viewundoneList`, `viewhistoryList`, `viewklaimList`).
   - Panggilan `.load()` kini langsung dieksekusi seketika spinner loading dirender, memangkas 1.000 ms (1 detik) latensi tunggu.
2. **Optimasi Query Database / SQL Direct Filter ([Transaksi.php:6020-6065](file:///w:/everest_16sep/application/modules/kompensasiharga/controllers/Transaksi.php#L6020-L6065)):**
   - Menangkap parameter `$showMode = isset($_GET['show']) && in_array($_GET['show'], array('ready', 'all')) ? $_GET['show'] : 'ready';`.
   - Menghitung total transaksi riwayat secara instan menggunakan `$this->db->from("transaksi")->...->count_all_results()` untuk mengisi `$countTotalAll`.
   - **Mode Default (`show=ready`):**
     - Melakukan query seleksi cepat ke tabel cache `_rek_pembantu_subpiutangsuppliertrans_cache` dengan filter `rekening='1010020030'`, `periode='forever'`, dan `HAVING SUM(debet) > 0` (serta filter `extern3_id` jika supplier dipilih).
     - Menggunakan array ID transaksi yang siap diklaim (`$readyTransIds`) untuk membatasi lookup `transaksi.id` via `$this->db->where_in("transaksi.id", $readyTransIds)`.
     - Jika tidak ada nota bersisa diskon, proses lookup dan subquery 5 tabel berikutnya di-bypass secara total (`$tmpHist = array()`), memangkas beban server secara drastis.
   - **Mode On-Demand (`show=all`):**
     - Query mengambil seluruh data riwayat arsip tanpa filter `readyTransIds` saat pengguna menekan tombol *"Tampilkan Semua Data"*.
   - Meneruskan variabel `"showMode" => $showMode` dan `"countTotalAll" => $countTotalAll` ke view `transaksi.php`.

---

## 9. Perbaikan Data Flow Checkbox Seleksi Nota (18 September 2026 - Selesai)
- **Akar Masalah:**
  - Pada `Transaksi/viewKlaimSupplierIndex`, variabel baris `$row->id`, `$row->suppliers_id`, `$row->nomer`, dan `$row->sisa_diskon` tidak disuntikkan ke dalam `$tmp` di `Transaksi.php`.
  - Akibatnya, view mengecek `if ($grn_id > 0)` menghasilkan `false` dan selalu mencetak strip `-`.
- **Solusi yang Diimplementasikan:**
  1. [Transaksi.php:6521](file:///w:/everest_16sep/application/modules/kompensasiharga/controllers/Transaksi.php#L6521): Menyuntikkan `$tmp['id'] = $row->id;`, `$tmp['suppliers_id'] = $row->suppliers_id;`, `$tmp['nomer_clean'] = $row->nomer;`, dan `$tmp['sisa_diskon_raw'] = isset($row->sisa_diskon) ? $row->sisa_diskon : 0;`.
  2. [views/transaksi.php](file:///w:/everest_16sep/application/modules/kompensasiharga/views/transaksi.php): Mendukung pembacaan nilai numerik murni `sisa_diskon_raw` untuk kalkulasi counter badge dan disable state, serta memperbarui cakupan `check_all_grn_klaim` dan `clearAllKlaimSelection` agar mendukung seluruh halaman DataTables via `table.$()`.
- **Hasil:**
  - Checkbox baris kini ter-render secara benar di browser.
  - Saat supplier belum dipilih (`sid == 0`), checkbox muncul berstatus `disabled` dengan tooltip petunjuk *"Pilih supplier tertentu dahulu"*.
  - Saat supplier dipilih (`sid > 0`), nota bersisa diskon menjadi aktif (`enabled`) dan terhubung dinamis dengan Bar Rekapitulasi (`#batch_klaim_action_bar`).

---

## 10. Blueprint Kelanjutan: Pemrosesan Data Terpilih (Sequential Queue Terisolasi / Opsi 3)

Pada diskusi tanggal 18 September 2026, telah disepakati bahwa langkah tindak lanjut setelah checkbox seleksi dan optimasi database selesai adalah mempersiapkan **Pemrosesan Data yang Sudah Dipilih**.

### A. Kondisi State Saat Ini
- Data nota terpilih saat ini tersimpan di memori JavaScript browser pada variabel objek:
  `window.selectedKlaimGrnMap = { [grn_id]: { selected: true/false, sisa: 1500000, nomer: "..." } }`.
- Status centang tersinkronisasi antar-halaman DataTables via `syncCheckboxUI()`.
- Bar Rekapitulasi (`#batch_klaim_action_bar`) saat ini baru memiliki tombol *"Batalkan Pilihan"* dan belum memiliki tombol eksekusi transaksi.

### B. Keputusan Pendekatan: Opsi 3 (Sequential Queue Terisolasi)
Dipilih pendekatan **Sequential Queue Terisolasi** karena mempertahankan karakteristik arsitektur inti transaksi 3333 (**1 Transaksi Kompensasi = 1 Nota GRN**) tanpa merombak jurnal akuntansi, sekaligus mencegah terulangnya insiden pencemaran form manual.

### C. Prinsip "Tembok Api" Isolasi (Anti-Pencemaran Form Manual)
1. **Zero Global Session:** Dilarang keras menggunakan sesi global tanpa token (seperti `$_SESSION['klaim_queue']`).
2. **Tokenized Queue:** Setiap antrean batch wajib memiliki token unik:
   `$queueToken = 'KQ_' . my_id() . '_' . time() . '_' . substr(md5(uniqid()), 0, 6);`
3. **Pemisahan Jalur Total:** Form manual `Create/index/3333` (tanpa parameter `queue_token`) tetap **100% steril dan independen**. Kode antrean hanya aktif jika parameter `?queue_token=XYZ` disertakan secara eksplisit.

### D. Rencana Alur Kerja Teknis End-to-End
```
[UI Bar Rekapitulasi]
  → Klik tombol "Proses Klaim Terpilih"
  → AJAX POST ke Controller: Transaksi/initKlaimQueue
      ↓
[Backend: Validasi Cepat & Pembentukan Token]
  → Validasi Saldo Riil di tabel cache & cek locker (Fail-Fast)
  → Simpan antrean sementara ke MongoDB (sesuai BP 3.11) / Sesi Bertoken
  → Return token antrean & daftar item
      ↓
[UI: Runner Antrean (Sequential AJAX Loop)]
  → Loop AJAX per Nota: Transaksi/processQueueItem
      ↓
  [Nota 1 Sukses] → delay 300ms → [Nota 2 Sukses] → ... → [Selesai]
      ↓
[Laporan Rekapitulasi Hasil Batch & Auto-Refresh Tabel]
```

1. **Tombol Aksi UI:**
   Menyematkan tombol `<button type='button' id='btn_proses_batch_klaim' class='btn btn-sm btn-success'><i class='fa fa-play'></i> Proses Klaim Terpilih</button>` di Bar Rekapitulasi saat `count > 0`.
2. **Handshake & Validasi Riil (`Transaksi/initKlaimQueue`):**
   - Validasi saldo riil di `_rek_pembantu_subpiutangsuppliertrans_cache` (`debet > 0`) untuk mencegah data basi di browser pengguna.
   - Validasi apakah ada nota yang sedang di-lock pengguna lain di `stock_locker_diskon`.
   - Simpan struktur antrean (daftar item, status `pending`, token, supplier_id, user_id).
3. **Eksekusi Berantai Terkendali (`Transaksi/processQueueItem`):**
   - Dieksekusi per nota via AJAX bertahap (bukan 1 loop PHP masif) guna mencegah PHP execution timeout dan session locking.
   - Diberikan jeda interval kecil (~300 ms) antar-request agar database engine MariaDB sempat melakukan commit dan pelepasan row lock.
4. **Penanganan Kegagalan Parsial (*Partial Failure*):**
   - Jika nota ke-$i$ gagal, sistem **tidak me-rollback** nota yang sudah berhasil sebelumnya.
   - Nota yang gagal ditandai status `failed` dengan alasan spesifik, lalu antrean lanjut ke nota berikutnya.
   - Hasil akhir menampilkan laporan transparan (daftar nota sukses beserta nomor transaksi 3333 yang terbit, dan daftar nota gagal jika ada).
   - Menjalankan `clearAllKlaimSelection()` dan auto-reload `#klaimList`.

### E. Catatan Keputusan UX yang Perlu Dipilih di Sesi Berikutnya:
Sebelum eksekusi penulisan kode antrean dimulai, perlu diputuskan salah satu dari 2 format UX:
- **Format A (Full Auto AJAX Runner dengan Modal Dialog):**
  Pengguna klik *"Proses Klaim Terpilih"*, muncul modal progress bar. Sistem memproses nota 1 per 1 secara otomatis di latar belakang hingga 100% selesai.
- **Format B (Step-by-Step Review / Wizard):**
  Pengguna dibawa ke halaman review per nota satu demi satu, memeriksa angka kompensasi, lalu menekan *"Simpan & Lanjut ke Nota Berikutnya"*.

---
*Dokumen ini diperbarui pada: 18 September 2026 — Rangkuman implementasi optimasi backend database dan blueprint teknis Sequential Queue Terisolasi (Opsi 3) telah dicatat lengkap untuk dilanjutkan pada sesi kerja berikutnya.*


