# Dokumentasi & Rencana Implementasi: Proporsi Diskon & Premi Komponen Paket (Sales Package)

Dokumen ini mendokumentasikan analisis sistem berjalan serta rencana perubahan agar pembagian proporsi diskon/premi komponen pada transaksi **Sales Pre Order Packed (1582)** dapat dilakukan secara manual oleh user namun tetap tervalidasi.

---

## 1. Analisis Alur Berjalan (Otomatis)
Saat ini, sistem memproses penambahan produk tipe paket di halaman `penjualan/Create/index/1582` secara otomatis dengan alur berikut:
1. **Total Harga Komponen Awal ($subtotal\_nett1$):**
   Sistem mengalikan harga dasar masing-masing komponen (`harga_komponen`) dengan jumlah komponen tersebut dalam paket (`qty_komponen`), lalu menjumlahkannya untuk seluruh komponen dalam paket:
   $$\text{Subtotal Netto Awal} = \sum (\text{Harga Komponen} \times \text{Qty Komponen})$$
2. **Perhitungan Selisih Persentase Jual Paket vs Komponen ($persen\_selisih$):**
   Sistem membandingkan harga jual paket ($subtotal$) dengan total harga komponen dasar ($subtotal\_nett1$):
   $$\text{Selisih Persentase} = \left( \frac{\text{Harga Jual Paket}}{\text{Total Netto Awal Komponen}} \right) - 1$$
   * Jika bernilai **negatif**, dianggap sebagai **Diskon Paket** (misal: $-67.45\%$).
   * Jika bernilai **positif**, dianggap sebagai **Premi Paket**.
3. **Alokasi Otomatis:**
   Persentase selisih ini diterapkan secara merata dan otomatis ke setiap komponen paket di backend (`_processSelectProductPaket.php`) untuk menghasilkan `Price (Net)` per komponen sehingga totalnya seimbang dengan harga paket utama.

---

## 2. Rencana Perubahan: Pengeditan Manual & Validasi
Agar pengguna dapat menyesuaikan proporsi diskon/premi per komponen sendiri, langkah-langkah implementasinya adalah:

### Langkah 2.1: Mengaktifkan Kolom Input pada Komponen (Konfigurasi UI)
Menambahkan konfigurasi `shoppingCartEditableFields2` pada berkas `config/coTransaksiUi.php` modul `penjualan` untuk langkah pembuatan (step 1):
```php
"shoppingCartEditableFields2" => array(
    1 => array(
        "disc_percent", // Input diskon persen per komponen
        "disc",         // Input nominal diskon per komponen
        "premi_percent",// Input premi persen per komponen
        "premi",        // Input nominal premi per komponen
        "nett1",        // Input harga net per komponen (Universal)
    ),
)
```
```
*Catatan: Direkomendasikan menggunakan kolom universal `nett1` (Price Net) karena otomatis menghitung selisih sebagai diskon jika nilainya di bawah bruto, atau premi jika di atas bruto.*

### Langkah 2.2: Implementasi Logika Resiprokal (Bolak-Balik) di Kolom Komponen
Semua kolom pengeditan pada komponen harus bersifat resiprokal satu sama lain. Logika perhitungannya dikonfigurasi melalui event keyup/change sebagai berikut:

1. **Jika Input `disc_percent`:**
   * `disc = (disc_percent / 100) * harga`
   * `nett1 = harga - disc`
   * `premi = 0` dan `premi_percent = 0` (reset premi jika ada diskon)
2. **Jika Input `disc` (nominal):**
   * `disc_percent = (disc / harga) * 100`
   * `nett1 = harga - disc`
   * `premi = 0` dan `premi_percent = 0`
3. **Jika Input `premi_percent`:**
   * `premi = (premi_percent / 100) * harga`
   * `nett1 = harga + premi`
   * `disc = 0` dan `disc_percent = 0` (reset diskon jika ada premi)
4. **Jika Input `premi` (nominal):**
   * `premi_percent = (premi / harga) * 100`
   * `nett1 = harga + premi`
   * `disc = 0` dan `disc_percent = 0`
5. **Jika Input `nett1` (Price Net):**
   * **Kondisi A (nett1 < harga):** `disc = harga - nett1`, `disc_percent = (disc / harga) * 100`, `premi = 0`, `premi_percent = 0`.
   * **Kondisi B (nett1 >= harga):** `premi = nett1 - harga`, `premi_percent = (premi / harga) * 100`, `disc = 0`, `disc_percent = 0`.

---


### Langkah 2.2: Menangani Input Perubahan Data (AJAX)
Setiap kali input nilai pada komponen diubah oleh user, sistem mengirimkan request AJAX ke server untuk memperbarui data session sementara:
`$_SESSION[$cCode]['items2']`

### Langkah 2.3: Validasi Real-Time di Tampilan (Client-Side)
Menggunakan JavaScript di browser untuk memantau kesesuaian nilai:
1. Menjumlahkan total harga bersih komponen secara *real-time*:
   $$\text{Total Netto Komponen} = \sum (\text{Price Net}_i \times \text{Qty}_i)$$
2. Membandingkannya dengan **Harga Jual Paket Utama**.
3. Jika tidak seimbang (tidak cocok), tampilkan pesan peringatan merah di UI dan kunci tombol **Save/Submit** (di-disable).

### Langkah 2.4: Validasi Pengunci di Server (Server-Side)
Sebelum data draf disimpan ke database MySQL atau diproses ke tahap persetujuan berikutnya (di `Create.php` dan `FollowUp.php`), lakukan validasi backend:
```php
$cCode = $this->cCode;
$total_komponen = 0;

if (isset($_SESSION[$cCode]['items2'])) {
    foreach ($_SESSION[$cCode]['items2'] as $pID => $subItems) {
        foreach ($subItems as $komponen) {
            $total_komponen += ($komponen['nett1'] * $komponen['qty']);
        }
    }
}

$total_paket = 0;
if (isset($_SESSION[$cCode]['items'])) {
    foreach ($_SESSION[$cCode]['items'] as $itemUtama) {
        $total_paket += $itemUtama['subtotal'];
    }
}

// Cek kesesuaian nilai dengan toleransi pembulatan desimal kecil (0.01)
if (abs($total_komponen - $total_paket) > 0.01) {
    echo "<script>alert('Validasi Gagal: Total netto komponen (Rp " . number_format($total_komponen) . ") tidak sama dengan harga jual paket (Rp " . number_format($total_paket) . "). Mohon sesuaikan diskon/premi komponen Anda.'); history.back();</script>";
    exit();
}
```
Dengan validasi ganda ini, integritas transaksi paket di database tetap terjamin meskipun pengisian nilai komponen dilakukan secara manual oleh user.

---

## 3. Pembaruan & Koreksi Tambahan (Hasil Uji & Perbaikan)

Berikut adalah beberapa perbaikan dan peningkatan fitur tambahan yang telah diimplementasikan selama proses pengembangan:

### 3.1 Perbaikan Bug Persentase Desimal Awal (0.674 vs 67.4%)
* **Masalah:** Saat paket pertama kali dipilih, kolom `disc(%)` menampilkan format pecahan desimal (seperti `0.674`) alih-alih nilai persentase murni (`67.4%`).
* **Penyebab:** Pada blok inisiasi awal komponen di [_processSelectProductPaket.php](file:///z:/san_cik/application/modules/penjualan/controllers/_processSelectProductPaket.php), nilai `disc_percent` dan `premi_percent` tidak dikalikan `100` (disimpan dalam bentuk pecahan desimal).
* **Solusi:** Diubah agar dikalikan 100 secara konsisten di semua blok alokasi persentase awal.

### 3.2 Perbaikan Bug Tombol Continue Tidak Terkunci (Iframe Scoping)
* **Masalah:** Meskipun peringatan merah ketidakcocokan proporsi muncul, tombol **Continue SALES PRE ORDER PACKED** tetap aktif dan dapat diklik.
* **Penyebab:** 
  1. JavaScript di [shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/views/shoppingCart.php) menargetkan tombol `#tic_lanjut` di level `top` (parent window). Namun, pada langkah paket ini, tombol yang mengontrol kelanjutan transaksi adalah **`#btnProcess`**.
  2. Tombol tersebut berupa tag jangkar (`<a>`), bukan `<button>`, sehingga pemanggilan `.prop('disabled', true)` diabaikan oleh HTML/browser.
* **Solusi:** JavaScript diperbarui agar secara adaptif menonaktifkan tombol di level dokumen saat ini maupun level `top` parent window. Khusus untuk tag `<a>` `#btnProcess`, penonaktifan dilakukan dengan menambahkan kelas Bootstrap `.disabled` (tampilan buram) dan mematikan klik melalui CSS `pointer-events: none`.

### 3.3 Saling Mengunci Input Diskon & Premi (Mutual Exclusion)
* **Pola Kerja:** Di [shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/views/shoppingCart.php), ditambahkan logika JavaScript `toggleInputLocker`. 
* **Aturan:** 
  * Jika kolom diskon (`disc_percent`/`disc`) menerima fokus/input, maka kolom premi (`premi_percent`/`premi`) pada baris yang sama akan otomatis diset `0` dan dinonaktifkan (`disabled`).
  * Sebaliknya, jika kolom premi yang diisi, kolom diskon akan langsung dinonaktifkan.
  * Inisialisasi awal juga dilakukan saat memuat halaman untuk langsung mendeteksi kolom mana yang harus dikunci berdasarkan nilai yang sudah ada di session.

### 3.4 Validasi Batas Maksimum Diskon (Safety Guard)
* **Frontend:** Pada input diskon nilai (`disc`), ditambahkan pengaman `onkeyup` agar tidak bisa diisi melebihi harga bruto barang (`harga_ori`). Nilai diskon persen (`disc_percent`) dibatasi maksimal 100%.
* **Backend:** Ditambahkan validasi pemotongan (*clamping*) nilai serupa di controller `recordItemColumn2` pada [_shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/controllers/_shoppingCart.php) sebagai pengaman ganda database.

### 3.5 Fitur Penyesuaian Pembulatan Otomatis (Auto-Adjust Rounding)
* **Aksi:** Di dalam kotak peringatan merah ketidakcocokan proporsi di [shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/views/shoppingCart.php), ditambahkan tombol **"Sesuaikan Pembulatan Otomatis"**.
* **Cara Kerja:** Menghubungi endpoint AJAX `autoAdjustRoundingAjax()` di [_shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/controllers/_shoppingCart.php) yang mendeteksi selisih total desimal (misal Rp10), mendistribusikannya ke komponen pertama yang belum diedit manual oleh user, menghitung ulang total secara global via `ValueGate`, dan melakukan reload cart secara langsung. Peringatan merah akan langsung hilang dan tombol continue kembali aktif secara otomatis.
* **Peningkatan Pesan Notifikasi (User-Friendly Alert):** Mengubah pesan kesalahan pencocokan harga komponen dengan harga paket utama dari bahasa Inggris menjadi bahasa Indonesia murni yang ramah pengguna, lengkap dengan simbol rupiah (`Rp`) dan nominal selisih angka secara *real-time* untuk mempermudah pemahaman pengguna.
* **Perlindungan Data Manual (Edit Preservation):**
  * Ketika user mengedit komponen secara manual via `recordItemColumn2()`, sistem menandai komponen tersebut dengan flag `is_edited = true` di session.
  * Saat proses perataan selisih otomatis berjalan, sistem secara pintar mencari komponen pertama yang **belum pernah diedit manual** (`is_edited != true`) untuk menyerap selisih pembulatan tersebut. Jika semua baris sudah diedit manual, sistem akan meletakkan selisih di baris komponen terakhir untuk menjaga keutuhan input user.
* **Penanganan Tabrakan AJAX (Race Condition Fix):**
  * **Masalah:** Jika user mengetik angka di input lalu langsung mengklik tombol pembulatan otomatis, event click membatalkan request penyimpanan blur input yang sedang berjalan di latar belakang, sehingga status `is_edited` gagal terkirim dan baris yang baru saja diketik berubah kembali.
  * **Solusi:** Di [shoppingCart.php](file:///z:/san_cik/application/modules/penjualan/views/shoppingCart.php), fungsi `autoAdjustRounding()` diperbarui menggunakan pengecekan `$.active > 0` untuk memastikan semua request AJAX penyimpanan aktif selesai terlebih dahulu (dengan penundaan rekursif 200ms) sebelum memicu proses perataan selisih.


