# 🛠️ RENCANA & ANALISIS DETAIL PERBAIKAN CODEBASE HULU - HILIR DISKON SUPPLIER

**Sistem:** Everest ERP (CodeIgniter 3.1.8 / Wiredesignz HMVC / PHP 5.6)  
**Database Analisis:** `192.168.5.17` (`run_everest_modul`) & Codebase `everest_opname28sep`  
**Target Modul:** `pembelian` (466 & 467) & `kompensasiharga` (3333 & Monitoring)  
**Status:** Dokumen Analisis Teknis & Rencana Detail Perbaikan  

---

## 1. Peta File & Arsitektur yang Terlibat

Berdasarkan investigasi end-to-end terhadap 2.308 GRN bermasalah dan 49 klaim diskon yang menggantung, berikut adalah daftar file yang harus diperbaiki:

```
w:\everest_opname28sep\
├── application/
│   ├── models/
│   │   ├── Preprocs/
│   │   │   └── PreSyncDiskonPembelian.php               [PILAR 1 - HULU]
│   │   └── Coms/
│   │       └── ComRekeningPembantuPiutangSupplierDetailTransItem.php [PILAR 2 - HILIR]
│   └── modules/
│       ├── pembelian/
│       │   └── config/
│       │       └── coTransaksiCore.php                  [PILAR 1 - HULU]
│       └── kompensasiharga/
│           ├── config/
│           │   └── coTransaksiUi.php                    [PILAR 2 - HILIR]
│           └── controllers/
│               └── Transaksi.php                        [PILAR 2 - HILIR]
└── tools/ (Script Rekonsiliasi Database PHP 5.6 CLI)
    ├── patch_restore_stock_locker_diskon.php            [PILAR 3 - DB PATCH]
    ├── patch_normalize_grn_overdebit.php                [PILAR 3 - DB PATCH]
    └── patch_reconcile_49_klaim_mutasi.php              [PILAR 3 - DB PATCH]
```

---

## 2. Analisis & Rencana Perbaikan Detail: PILAR 1 (Hulu - Modul Pembelian)

### 2.1 File: `application/models/Preprocs/PreSyncDiskonPembelian.php`

#### A. Titik Masalah (Root Cause):
Pada fungsi `_prevalueDoskonRelasi()` ([PreSyncDiskonPembelian.php#L327](file:///w:/everest_opname28sep/application/models/Preprocs/PreSyncDiskonPembelian.php#L327)):
```php
$m->addFilter("transaksi_id='$po_id'");
$m->addFilter("reference_id='$referenceID'");
$m->addFilter("nilai>'10'");
$tmp = $m->lookUpAll()->result();
```
Ketika PO induk memiliki nilai `0` di tabel `stock_locker_pre_diskon` (kasus `PO.-1.1849`), pencarian menghasilkan kosong.  
Akibatnya pada baris 276–284:
```php
else {
    cekHere("diskon: $diskon_key_id, sudah 0 di pre-locker diskon");
    $key_reset = "diskon_$diskon_key_id" . "_nilai";
    $_SESSION[$cCode][$src_key][$pID][$key_reset] = 0;
    $_SESSION[$cCode][$src_key][$pID]["sub_" . $key_reset] = 0;
    // ...
}
```
Sistem secara destruktif mereset `diskon_1_nilai = 0`, sehingga Diskon 1 dieksklusikan dari `items4_sum`. Slot Diskon 1 tidak pernah dibuat di `stock_locker_diskon` saat GRN (`467.-1.2496`) disimpan.

#### B. Rencana Perbaikan Kode (PHP 5.6 Safe):
1. **Fallback ke Plafon Diskon PO / Registry:**  
   Jika filter `nilai > 10` di `stock_locker_pre_diskon` tidak menghasilkan data, sistem tidak boleh langsung mengeksekusi reset diskon ke 0. Sistem harus memeriksa apakah item barang pada transaksi PO referensi memang memiliki nilai diskon supplier (dari tabel `transaksi_data_registry` PO atau master diskon supplier).
2. **Perlindungan Array `items4_sum`:**  
   Jika persentase diskon supplier (`diskon_persen > 0`) ada pada PO, hitung nilai hak diskon proporsional terhadap quantity GRN yang sedang diterima:
   $$\text{diskon\_nilai} = \frac{\text{diskon\_persen}}{100} \times \text{harga\_satuan\_po}$$
   sehingga slot Diskon 1 tetap terbentuk di `stock_locker_diskon`.

---

### 2.2 File: `application/modules/pembelian/config/coTransaksiCore.php`

#### A. Titik Masalah (Root Cause):
Pada konfigurasi transaksi `466` step `467` ([coTransaksiCore.php (pembelian)](file:///w:/everest_opname28sep/application/modules/pembelian/config/coTransaksiCore.php)):
1. **Duplikasi Debet (Double Posting):**  
   * **Master Komponen [98] & [99]:** Menggunakan `main['diskon_nilai_total']` untuk mendebet COA `1010020030`.
   * **Detail Komponen [3], [4], [5], [6]:** Masing-masing mendebet COA `1010020030` menggunakan `items4_sum['sub_diskon_nilai']`.  
   Eksekusi simultan ini menyebabkan saldo debet pada rekening pembantu membengkak $2\times$ hingga $4\times$ lipat (ditemukan pada 2.308 dokumen GRN).
2. **Penerimaan Parsial Mendebet Utuh 1 PO:**  
   Pada penerimaan parsial (seperti 50 unit Sharp dari 189 unit PO), `main['diskon_nilai_total']` menyalin angka total diskon seluruh PO (Rp 24.628.767) dan didebetkan ke 1 dokumen GRN parsial, alih-alih proporsional Rp 12.076.285.

#### B. Rencana Perbaikan Konfigurasi:
1. **Pemisahan Peran Master vs Detail:**  
   * Buku pembantu transaksi GRN (`_rek_pembantu_subpiutangsuppliertrans_cache` dan `__rek_pembantu_subpiutangsuppliertrans__1010020030`) hanya boleh didebet oleh Detail Komponen `[5]` (`RekeningPembantuPiutangSupplierDetailTransItem`) yang bersumber dari `items4_sum` (murni berdasarkan barang yang diterima secara fisik pada GRN tersebut).
   * Menghilangkan duplikasi debet berulang pada komponen detail lainnya atau memastikan pemetaannya saling eksklusif (tidak saling menumpuk).
2. **Penghitungan Nilai Master Proporsional:**  
   Di ValueGate / Preprocessor `pembelian`, variabel `diskon_nilai_total` pada level `main` untuk GRN parsial wajib dihitung dari akumulasi `sub_diskon_nilai` barang yang diterima pada GRN tersebut saja:
   $$\text{main['diskon\_nilai\_total']} = \sum (\text{items4\_sum['sub\_diskon\_nilai']})$$

---

## 3. Analisis & Rencana Perbaikan Detail: PILAR 2 (Hilir - Modul Kompensasi Harga)

### 3.1 File: `application/modules/kompensasiharga/config/coTransaksiUi.php`

#### A. Titik Masalah (Root Cause):
Pada baris 2610 ([coTransaksiUi.php#L2610](file:///w:/everest_opname28sep/application/modules/kompensasiharga/config/coTransaksiUi.php#L2610)):
```php
"pihakNameMainDiskonIdSelector" => "id",
"pihakNameMainDiskonIdProcessor" => "id",
```
Karena `pihakModelMain` adalah `MdlSupplierDiskon`, kolom `id` menghasilkan ID jenis diskon (`1` s/d `8`).  
Ketika form dikirim, `main['pihakMainID']` terisi angka `2` (ID Diskon 2), bukan ID transaksi GRN (`34662`).

#### B. Rencana Perbaikan Kode:
Kunci konfigurasi selector dan processor agar mengambil kolom identitas transaksi GRN yang valid:
```php
// START OF COMPLETE REPEATED LOGIC
"pihakNameMainDiskonIdSelector" => "transaksi_id",
"pihakNameMainDiskonIdProcessor" => "transaksi_id",
// END OF COMPLETE REPEATED LOGIC
```

---

### 3.2 File: `application/modules/kompensasiharga/controllers/Transaksi.php` (Monitoring DataTable)

#### A. Titik Masalah (Root Cause):
Pada fungsi `viewKlaimSupplierDataAjax()` baris 6345–6385:
```php
if ($locker_val == 0) {
    // Fallback ke _rek_pembantu_subpiutangsuppliertrans_cache
    $sisa_diskon = $debet_cache - $total_klaim;
}
```
Ketika baris locker hilang (kasus GRN 2496 Diskon 1), sistem fallback ke tabel cache rekening pembantu yang memuat angka over-debit (Rp 24,6 Jt), menghasilkan angka bengkak Rp 20.185.738 di layar user.

#### B. Rencana Perbaikan Kode:
1. **Formula Tunggal Bebas Anomali:**  
   DataTable tidak boleh melakukan fallback mentah ke saldo debet cache yang rentan over-debit ganda.
2. **Formula Baku DataTable:**  
   $$\text{Diskon Supplier} = \text{Plafon Diskon Riil GRN (dari Registry items4\_sum atau Locker Fisik Awal)}$$
   $$\text{Total Klaim} = \sum (\text{Mutasi Kredit Klaim yang Sah})$$
   $$\text{Sisa Diskon} = \text{Diskon Supplier} - \text{Total Klaim}$$
3. **Penyelarasan Tampilan:**  
   Dengan formula matematis ini, pada GRN 2496 angka yang tampil di kolom *Diskon Supplier* akan selalu tertib Rp 12.076.285,83 (atau Rp 7.633.256,37 untuk Diskon 1) dan *Sisa Diskon* menjadi akurat.

---

### 3.3 File: `application/models/Coms/ComRekeningPembantuPiutangSupplierDetailTransItem.php`

#### A. Titik Masalah (Root Cause):
1. **Silent Failure pada Balance Protection:**  
   Ketika user mengajukan klaim atas nota yang saldonya di cache sudah bernilai `0` (akibat klaim ganda atau salah nota), pemeriksaan `accountBalanceProtections` menyebabkan komponen mengembalikan `false` atau tidak mengeksekusi insert ke `__rek_pembantu_subpiutangsuppliertrans__1010020030`, sementara GL jurnal umum dan pemotongan locker tetap berjalan. Inilah penyebab 43 klaim pada 22 Juli 2026 tidak memiliki mutasi kredit.
2. **Ketergantungan Parameter `extern_id`:**  
   Komponen sangat bergantung pada `static['extern_id']`. Jika controller mengirimkan `pihakMainID` yang salah, komponen gagal menemukan cache pembantu yang relevan.

#### B. Rencana Perbaikan Kode:
1. Tambahkan validasi fail-fast di awal controller `Create.php` / `FollowUp.php` sebelum `$this->db->trans_start()`:
   * Periksa apakah sisa piutang diskon nota GRN yang dipilih masih mencukupi nilai klaim yang diinput.
   * Jika tidak mencukupi, tolak transaksi di form input (jangan biarkan transaksi terbentuk setengah jalan di jurnal umum tanpa buku pembantu).
2. Tambahkan pencatatan error log eksplisit jika terjadi kegagalan posting mutasi pembantu.

---

## 4. Analisis & Rencana Perbaikan Detail: PILAR 3 (Rekonsiliasi Database Produksi)

Untuk memulihkan integritas data pada database `192.168.5.17`, akan disiapkan 3 script CLI mandiri (PHP 5.6) yang dijalankan satu kali (*one-time patch*):

### 4.1 Script: `tools/patch_restore_stock_locker_diskon.php`
* **Tujuan:** Menemukan transaksi GRN `467` yang memiliki hak diskon pada registry atau PO, namun baris diskonnya hilang di `stock_locker_diskon` (seperti Diskon 1 pada `GRN.-1.2496`).
* **Mekanisme Kerja:**
  1. Baca `transaksi_data_registry` untuk GRN target.
  2. Ekstrak data `items` dan hitung nilai diskon supplier yang belum terbit di `stock_locker_diskon`.
  3. Lakukan `INSERT` baris yang hilang ke `stock_locker_diskon` dengan status `active`, nilai sesuai proporsional GRN, dan kolom audit yang lengkap.

### 4.2 Script: `tools/patch_normalize_grn_overdebit.php`
* **Tujuan:** Menormalkan saldo debet pada 2.308 GRN yang mengalami over-debit ($2\times$ hingga $4\times$ lipat atau membebankan total PO) pada tabel `_rek_pembantu_subpiutangsuppliertrans_cache`.
* **Mekanisme Kerja:**
  1. Loop seluruh GRN aktif yang teridentifikasi memiliki selisih antara `_rek_pembantu_subpiutangsuppliertrans_cache` dengan plafon riil GRN.
  2. Hitung nilai plafon hak diskon riil GRN:
     $$\text{Plafon Riil} = \sum (\text{nilai fisik diskon barang GRN})$$
  3. Update `debet` di tabel cache untuk periode `forever`, `tahunan`, `bulanan`, dan `harian` agar sama dengan nilai plafon riil yang benar.
  4. Sesuaikan nilai `saldo_debet` dan `debet_akhir` agar selaras secara akuntansi.

### 4.3 Script: `tools/patch_reconcile_49_klaim_mutasi.php`
* **Tujuan:** Menuntaskan 49 transaksi klaim `3333` yang tidak memiliki mutasi kredit di `__rek_pembantu_subpiutangsuppliertrans__1010020030`.
* **Mekanisme Kerja:**
  1. Klasifikasikan 49 klaim ke dalam 2 kelompok:
     * **Kelompok A (Klaim Sah / Belum Tercatat Kredit):** Klaim yang sah dan memang memotong locker, tetapi jurnal pembantunya tertinggal.
     * **Kelompok B (Klaim Duplikat / Phantom):** Klaim yang diajukan atas GRN yang saldonya sudah pernah lunas sebelumnya (seperti kasus duplikasi klaim pada 22 Juli 2026).
  2. Untuk **Kelompok A:** Bentuk entri mutasi kredit susulan di `__rek_pembantu_subpiutangsuppliertrans__1010020030` sesuai tanggal transaksi aslinya (`fulldate`), dan update saldo kredit di tabel cache.
  3. Untuk **Kelompok B:** Berikan penandaan audit khusus atau buatkan jurnal koreksi pembalik (*reversal*) agar buku besar dan buku pembantu kembali *balance*.

---

## 5. Protokol Uji & Keselamatan Eksekusi (Safety Rules)

Sesuai aturan ketat codebase (`AGENTS.md`):
1. **PHP 5.6 Syntax Strictness:**  
   * Tidak boleh menggunakan shorthand array `[]`, null coalescing `??`, atau arrow function `fn()`.
   * Wajib menggunakan `array()`, `isset() ? :`, dan konvensi CI3.
2. **Protokol Backup Tabel:**  
   Sebelum menjalankan script rekonsiliasi data, tabel-tabel berikut wajib di-backup ke tabel shadow di database:
   * `CREATE TABLE _bak_20260929_stock_locker_diskon AS SELECT * FROM stock_locker_diskon;`
   * `CREATE TABLE _bak_20260929_rek_cache AS SELECT * FROM _rek_pembantu_subpiutangsuppliertrans_cache;`
   * `CREATE TABLE _bak_20260929_rek_mutasi AS SELECT * FROM __rek_pembantu_subpiutangsuppliertrans__1010020030;`
3. **Transaction Wrapping:**  
   Setiap operasi batch DB wajib dibungkus dengan `$this->db->trans_start()` dan `$this->db->trans_complete()`.
4. **Dry-Run Mode:**  
   Seluruh script migrasi wajib memiliki parameter `--dry-run` untuk menampilkan simulasi data yang akan diubah sebelum eksekusi commit nyata.

---

## 6. Urutan Langkah Eksekusi (Step-by-Step Execution Sequence)

```
┌────────────────────────────────────────────────────────┐
│ Langkah 1: Penguncian & Perbaikan Codebase Hulu-Hilir │
│ (Mencegah timbulnya transaksi rusak baru)              │
│ - coTransaksiUi.php (Selector transaksi_id)            │
│ - PreSyncDiskonPembelian.php (Anti-reset diskon)       │
│ - coTransaksiCore.php pembelian (Anti-overdebit)       │
│ - Transaksi.php kompensasiharga (DataTable formula)    │
└───────────────────────────┬────────────────────────────┘
                            │
                            ▼
┌────────────────────────────────────────────────────────┐
│ Langkah 2: Dry-Run Script Rekonsiliasi Database        │
│ (Memverifikasi angka sebelum modifikasi data)          │
│ - Dry-run patch_normalize_grn_overdebit.php            │
│ - Dry-run patch_restore_stock_locker_diskon.php        │
│ - Audit verifikasi 49 klaim menggantung                │
└───────────────────────────┬────────────────────────────┘
                            │
                            ▼
┌────────────────────────────────────────────────────────┐
│ Langkah 3: Eksekusi Rekonsiliasi Data Database         │
│ (Pembersihan data historis 2024 - 2026)                │
│ - Backup tabel shadow                                  │
│ - Eksekusi normalisasi debet GRN & restorasi locker    │
│ - Rekonsiliasi 49 mutasi kredit klaim                  │
└───────────────────────────┬────────────────────────────┘
                            │
                            ▼
┌────────────────────────────────────────────────────────┐
│ Langkah 4: Validasi & Pengujian UI                     │
│ - Buka DataTable Transaksi: pastikan angka sisa benar  │
│ - Test input transaksi klaim baru: mutasi kredit pas   │
└────────────────────────────────────────────────────────┘
```
