# 📑 Dokumentasi Audit & Solusi: Duplikasi Terbit Project (Multi-Cabang)

Dokumen ini berisi analisis mendalam terkait temuan data project yang terbit ganda pada modul `master_project` dan `penjualanproject`, serta cetak biru (*blueprint*) solusi teknis Tahap 2 untuk mencegah terulangnya insiden serupa.

---

## 1. Identifikasi Kasus & Data Temuan

Berdasarkan data operasional pada tabel **Semua Project (`project_produk`)**:
- **Nomor Order Referensi:** `SO 588spo.1.1478.59` (`SO.PJ.1.1478.59`)
- **Nama Project:** `MAGGIORE SIGNATURE EAST`
- **Spesifikasi (Nomor PO):** `PO260700049`
- **Konsumen:** `PT. PARAMOUNT ENTERPRISE INTERNATIONAL`
- **Cabang:** `CABANG PINANG`

### Perbandingan Kedua Baris Data:
| Kolom Data | Baris 1 (ID 143) | Baris 2 (ID 137) |
| :--- | :--- | :--- |
| **Waktu Create** | **07 Aug 2026 14:30** | **07 Aug 2026 14:31** *(1 menit kemudian)* |
| **Transaksi ID (`Trid`)** | **`0`** *(Gantung / Tanpa Transaksi)* | **`900,215`** *(Terkunci ke Sales Order)* |
| **Nomer Order** | **Kosong / `-`** | **`SO.PJ.1.1478.59`** |
| **Status Operasional** | Data Master Mandiri (Orphan Draft) | Data Project Resmi Terikat Transaksi |

---

## 2. Analisis Akar Masalah (Root Cause Analysis)

1. **Jalur Pembuatan Ganda (Dual Entry Path):**
   - Pada pukul **14:30**, petugas menginput data project baru melalui menu Master Data (`Data/add/ProdukProject`). Data ini tersimpan dengan status awal `transaksi_id = 0` (ID 143).
   - Pada pukul **14:31**, saat petugas membuka modul pembuatan Sales Order Project (`588spo`), alih-alih memilih data project ID 143 yang telah dibuat, petugas menekan tombol tambah baru / menginput ulang dari dalam form transaksi, sehingga sistem meng-insert baris baru (ID 137) dan komponen `ComProjectSales` langsung mengikatkan nomor transaksi `Trid = 900215`.

2. **Ketiadaan Isolasi `cabang_id` pada Selector Transaksi:**
   - Pada `application/modules/penjualanproject/config/coTransaksiUi.php`, aturan penyaring selector (`selectorFilters`) hanya:
     ```php
     "selectorFilters" => array(
         "customer_id=pihakID",
         "transaksi_id=.0",
     ),
     ```
   - Ketiadaan filter `cabang_id` berpotensi menyebabkan proyek berstatus `transaksi_id = 0` dari cabang lain ikut muncul jika memiliki customer yang sama (kebocoran data lintas cabang).

3. **Ketiadaan Validasi Unik Komposit di Model:**
   - Pada `MdlProdukProject`, aturan validasi bawaan hanya mengecek `nama` dan `kategori` (required). Tidak ada mekanisme pengecekan duplikasi kombinasi `cabang_id + customer_id + spek (Nomor PO)`, sehingga sistem mengizinkan entri duplikat dengan nama dan spesifikasi PO yang identik.

---

## 3. Cetak Biru Solusi Teknis (Tahap 2 Blueprint)

### Langkah 1: Isolasi Konteks Cabang pada Selector Transaksi Project
* **Berkas Terdampak:** `application/modules/penjualanproject/config/coTransaksiUi.php`
* **Tujuan:** Memastikan modal pemilih (*selector*) project hanya menampilkan draft project (`transaksi_id = 0`) yang dibuat pada cabang aktif pengguna yang sedang login.
* **Perubahan Konfigurasi:**
  ```php
  "selectorFilters" => array(
      "customer_id=pihakID",
      "cabang_id=placeID",     // Mengunci isolasi project hanya milik cabang aktif
      "transaksi_id=.0",
  ),
  ```

---

### Langkah 2: Validasi Duplikasi Unik Komposit pada `MdlProdukProject`
* **Berkas Terdampak:** 
  - `application/modules/master_project/models/Mdls/MdlProdukProject.php`
  - `application/models/Mdls/MdlProdukProject.php`
* **Tujuan:** Menolak pembuatan data master project baru jika kombinasi Cabang + Konsumen + Nomor PO (`spek`) sudah pernah terdaftar dan berstatus aktif.
* **Implementasi Kode (Kompatibel PHP 5.6 & Query Binding):**
  ```php
  // START OF COMPLETE REPEATED LOGIC
  public function checkDuplicateProject($cabangId, $customerId, $spek, $excludeId = 0)
  {
      $sql = "SELECT id, nama, spek FROM project_produk 
              WHERE cabang_id = ? 
                AND customer_id = ? 
                AND spek = ? 
                AND id != ? 
                AND status = 1 
                AND trash = 0 
              LIMIT 1";
      return $this->db->query($sql, array($cabangId, $customerId, $spek, $excludeId))->row();
  }
  // END OF COMPLETE REPEATED LOGIC
  ```

---

### Langkah 3: Penguatan Penguncian Data pada `ComProjectSales`
* **Berkas Terdampak:** `application/models/Coms/ComProjectSales.php`
* **Tujuan:** Memastikan saat Sales Order disahkan (`open`), penguncian baris (`FOR UPDATE`) dan pembaruan `transaksi_id` / `transaksi_no` memverifikasi `cabang_id` agar integritas data antar-cabang tetap terjaga.
* **Prinsip:** Menggunakan Query Binding dan transaksi database (`$this->db->trans_start()` / `$this->db->trans_complete()`).

---

## 4. Status Pelaksanaan & Solusi Perbaikan UAT (Completed)
- **Tahap 1 (Data Cleansing):** Dikelola secara mandiri oleh Pengguna (menonaktifkan baris gantung ID 143 dan memastikan operasional berjalan pada ID 137).
- **Tahap 2 (Pencegahan Sistemik & Penyempurnaan UAT):**
  1. **Isolasi Cabang pada Selector:** `cabang_id=placeID` terpasang di `penjualanproject/config/coTransaksiUi.php` dan `master_project/config/coTransaksiUi.php`.
  2. **Validasi Duplikasi Form Input:** `protected $validateData = array("customer_id", "spek");` diaktifkan pada `MdlProdukProject.php` untuk memblokir form submit ganda pada `Data::addProcess()`.
  3. **Early Validation Transaksi:** Validasi `checkDuplicateProject()` dipasang pada `Create::save()` di `master_project` dan `penjualanproject`.
  4. **Penanganan Edit Project Tanpa Duplikasi:** `ComProdukProject::pair()` dan `ComProjectSales::cekPreValue()` disesuaikan agar mengeksekusi `UPDATE` pada project yang sudah ada (bukan `INSERT` baris baru) saat proses transaksi/edit berlangsung.


### Langkah 4: Keamanan Arsitektur Backend & Kepatuhan Indexing
* **Berkas Terdampak:** MdlProdukProject.php dan coTransaksiUi.php (master_project).
* **Tujuan:**
  1. Mengunci bypass cabang_id di level basis Model (ddData override) sehingga injeksi backend dari controller mana pun tetap tertolak.
  2. Mengganti operator <> menjadi > pada konfigurasi UI untuk menghindari kelemahan Full Table Scan pada index MariaDB.
* **Status:** Selesai Diimplementasikan.
