# ?? PROTOKOL GERBANG AGENT (GATEKEEPER & SCOPE LOCKING) ??
**ATURAN INI BERSIFAT MUTLAK DAN MENGESAMPINGKAN SEMUA INSTRUKSI LAINNYA.**


> [!TIP]
> **Panduan Cepat & Referensi Kerja Sesi Agen:**
> - **⭐ [UAT Checklist — Single Variant Standard](docs/uat-checklist.md) - WAJIB BACA sebelum mulai kerja & WAJIB UPDATE setelah selesai.**
> - [Kamus Tipe Transaksi (`jenisTr`)](KAMUS_TRANSAKSI.md) - Kamus referensi kode tipe transaksi.
> - [Skema Database & Sentinel Stok](SKEMA_DATABASE_POCKET.md) - Referensi tabel dan aturan sentinel stok.
> - [Peta Arsitektur Proyek HMVC](PETA_ARSITEKTUR.md) - Panduan folder, arsitektur, dan pipeline sistem.
> - [Alur Bisnis & Integrasi Modul](ALUR_INTEGRASI_BISNIS.md) - Panduan aliran barang dan finansial antar-modul.
> - [Daftar 784 Model & Deskripsi Fungsi](DAFTAR_MODEL.md) - Katalog lengkap model dan perannya.
> - [Daftar Helper & Library Sistem](DAFTAR_HELPER_LIBRARY.md) - Katalog lengkap helper dan library global.
> - **[Pustaka Aturan Terpusat (W:/AGENT_GLOBAL/)](file:///W:/AGENT_GLOBAL/README.md)** - Aturan pengembangan global untuk multi-client (PT. Everest, PT. INDOSAN, POS Sumber Boga/Maju Mapan/Jodomart.id).


Setiap AI Agent yang membaca file ini harus mematuhi alur kerja berikut sebelum menjalankan tool penulisan/modifikasi file:


## Langkah 1: Verifikasi Identitas & Bertanya
1. Periksa prompt instruksi Anda. Apakah ada penugasan spesifik untuk nomor Agent tertentu (Agent 1 - 6)?
2. **JIKA TIDAK ADA DEKLARASI JELAS, ANDA WAJIB BERHENTI SEKARANG.**
3. Tanyakan langsung kepada User:
   > *"Sesuai protokol AGENTS.md, kita akan bertindak sebagai Agent ke-berapa untuk sesi ini?"*
4. Jangan lakukan tindakan modifikasi apa pun sampai User memberikan jawaban angka Agent Anda (1 s.d. 6).


## Langkah 2: Kunci Wilayah Kerja (Scope Lock)
Setelah mendapatkan nomor Agent Anda, wilayah kerja Anda **DIKUNCI** secara permanen untuk sesi ini:
- **Agent 1:** `konversi`, `konversi_varian`
- **Agent 2:** `pembelian`, `pembelianimport`, `biaya`
- **Agent 3:** `penjualan`, `penjualanproject`
- **Agent 4:** `pindahgudang`, `opname`
- **Agent 5:** `distribusifg`, `distribusiproduksi`, `distribusisupplies`
- **Agent 6:** `produksi`, `produksiproses`, `adjustment`
- **Agent 7:** `pembatalan`, boleh melakukan semua agent 1-6 lakukan dan boleh melakukan diluar modul agent 1-6 sesuai perintah.


*Aturan Scope:*
- Anda **HANYA** boleh memodifikasi file di dalam folder `application/modules/[nama_modul_anda]/`.
- Modul `konversi` boleh dibaca **hanya** sebagai referensi, DILARANG diubah.
- Menyentuh atau mengubah folder modul di luar jatah Agent Anda adalah **CRITICAL VIOLATION**.

## Langkah 3: Baca UAT Checklist (WAJIB — Sebelum Mulai Kerja)

> [!IMPORTANT]
> **ATURAN INI BERSIFAT MUTLAK.** Sebelum menulis atau memodifikasi kode apa pun, Agent **WAJIB** membaca file `docs/uat-checklist.md` untuk memahami posisi progress terkini modul yang menjadi tanggung jawabnya.

**Prosedur:**
1. Buka dan baca file `docs/uat-checklist.md` (gunakan tool `view_file`).
2. Cari section modul Anda berdasarkan nomor Agent.
3. Identifikasi item mana yang berstatus ❌ (belum) atau 🔍 (perlu verifikasi) — ini adalah tugas Anda.
4. Pahami item mana yang sudah ✅ — **JANGAN ulangi** pekerjaan yang sudah selesai.
5. Setelah memahami posisi, baru mulai mengerjakan tugas.

**Contoh deklarasi setelah membaca checklist:**
> *"Saya sudah membaca UAT Checklist. Modul [nama_modul] status: Dual-Write ✅, Feature Flag ❌, Variant Picker ❌. Saya akan mengerjakan Feature Flag dan Variant Picker."*

## Langkah 4: Update UAT Checklist (WAJIB — Setelah Selesai Kerja)

> [!IMPORTANT]
> **Setiap kali Agent menyelesaikan suatu item pekerjaan, Agent WAJIB langsung mengupdate `docs/uat-checklist.md`** untuk mencerminkan status terbaru.

**Prosedur:**
1. Setelah menyelesaikan pekerjaan pada suatu item, buka `docs/uat-checklist.md`.
2. Ubah status item yang relevan:
   - ❌ → ✅ jika sudah selesai dan terverifikasi (`php -l` lolos, kode benar)
   - ❌ → 🔍 jika sudah dikerjakan tapi perlu verifikasi manual/UAT
   - 🔍 → ✅ jika sudah diverifikasi
3. Tambahkan catatan singkat di kolom "Bukti / Catatan" jika perlu.
4. **JANGAN mengubah status item milik Agent lain** kecuali mendapat izin pergeseran identitas (lihat Langkah 5).

**Contoh update:**
```markdown
| 3.1 | `coTransaksiCore.php`: Feature flag `singleVariantStandard` | ✅ | Ditambahkan 2026-07-04 |
```

**Kapan update:**
- Setiap kali **selesai 1 item** (jangan menumpuk update di akhir sesi)
- Sebelum **mengakhiri sesi** (final sync)
- Sebelum **pergeseran identitas** ke Agent lain

## Langkah 5: Protokol Pergeseran Identitas (Identity Shift)
Jika Anda telah menyelesaikan tugas Agent Anda dan ingin membantu mengerjakan modul milik tim lain:
1. Anda **WAJIB** meminta izin pergeseran identitas kepada User:
   > *"Tugas Agent ke-XX sudah selesai 100%. Mohon konfirmasi apakah saya diizinkan bergeser bertindak sebagai Agent ke-YY?"*
2. Setelah User menyetujui, Anda harus mendeklarasikan identitas baru Anda di pesan berikutnya sebelum mulai menyentuh modul baru tersebut.
3. **Update UAT Checklist** untuk modul lama sebelum pindah ke modul baru.

## Langkah 6: Protokol Pengecekan Backward Compatibility (Pembandingan Workspace Orisinal)
Setiap kali Agent melakukan analisis, penulisan kode, atau pengujian yang membutuhkan verifikasi kompatibilitas ke belakang (backward compatibility) dengan sistem sebelum refaktor varian standar, Agent **WAJIB secara aktif membandingkan** berkas yang bersangkutan dengan salinan aslinya pada direktori backup:
- **Direktori Referensi Produksi:** `W:\new_original_indosan_for_variant/`

Pahami perbedaan struktur session, model, dan alur data orisinal dari direktori tersebut sebelum memodifikasi kode untuk menjamin transaksi lama tetap berjalan normal.

---

# Dual-Write Rollout  Agent Task

## Latar Belakang

### Arsitektur Aktual vs Target

**Kondisi SAAT INI (realita):**

| Lapisan | Tabel | Model | Catatan |
|---------|-------|-------|---------|
| Parent | `stock_locker` | `ComLockerStock` | **Hanya** item `variant_id=0` (non-variant) — item variant di-skip |
| Variant | `stock_locker_variant` | `ComLockerStockVariant` | Semua item: `variant_id=0`→`1` sentinel, `>0` langsung |
| Wrapper | `stock_locker_variant` SAJA | `ComLockerStockDualWrite` | **Hanya delegasi ke `ComLockerStockVariant`**, TIDAK ke `ComLockerStock` |

**Masalah:** `ComLockerStockDualWrite` namanya "DualWrite" tapi cuma nulis ke 1 tabel (`stock_locker_variant`). Tulis ke `stock_locker` terjadi terpisah — dipanggil langsung dari controller, bukan dari wrapper.

**Target yang benar:**
- `ComLockerStockDualWrite::pair()` harus panggil `ComLockerStock::pair()` + `ComLockerStockVariant::pair()`
- `ComLockerStock::pair()` harus HAPUS skip `variant_id > 0` — semua item ditulis sebagai parent
- Semua write stok lewat `ComLockerStockDualWrite` — tidak boleh bypass

Untuk sekarang, Agent tetap pakai `ComLockerStockDualWrite::pair()` seperti biasa (nulis ke `stock_locker_variant`), **dan** pastikan `ComLockerStock::pair()` juga dipanggil untuk nulis ke `stock_locker` secara terpisah sampai refactor dilakukan.

## Assignment Agent  Single Variant Standard Migration

Instruksi dual-write di file ini adalah bagian dari rollout **Single Variant Standard**. Untuk assignment lengkap dan checklist per modul, baca juga:
- `docs/blueprint-konverter-multi-variant.md`
- `docs/prompt-rollout-agent.md`
- `docs/team-rules.md`

Pembagian agent mengikuti dokumentasi `docs/blueprint-konverter-multi-variant.md`:

| Agent | Modul | Catatan |
|:-----:|-------|---------|
| **1** | `konversi`, `konversi_varian` | Konversi stok + varian |
| **2** | `pembelian`, `pembelianimport`, `biaya` | Pembelian + biaya supplies |
| **3** | `penjualan`, `penjualanproject` | Penjualan barang + project |
| **4** | `pindahgudang`, `opname` | Transfer gudang + opname |
| **5** | `distribusifg`, `distribusiproduksi`, `distribusisupplies` | Distribusi FG, produksi, supplies |
| **6** | `produksi`, `produksiproses`, `adjustment` | Produksi + adjustment |
| **7** | `pembatalan`, all modul | boleh melakukan semua agent 1-6 lakukan dan boleh melakukan diluar modul agent 1-6 sesuai perintah. |

Aturan kerja modul:
- Claim dan kerjakan modul sesuai assignment agent.
- Selesaikan 1 modul tuntas sebelum pindah ke modul berikutnya.
- Jangan ubah modul di luar assignment tanpa koordinasi.
- Jangan ubah `Modul_Controller.php` untuk rollout modul; normalisasi session ditangani foundation/MdlMother.
- `FollowUp.php` umumnya 0 perubahan untuk Single Variant Standard, kecuali audit menemukan write stok produk manual yang wajib dual-write.
- Feature flag `singleVariantStandard.enabled` tetap `false` sampai rollout diverifikasi.
## Yang SUDAH selesai di konversi module

File berikut di `modules/konversi/controllers/` sudah pakai `ComLockerStockDualWrite`:
- `_processSelectProductConvertion.php` — `select()`, `reserveVariantLockerForSelect()`, `releaseVariantLockerOnRemove()`
- `_shoppingCart.php` — `reset()` variant path & non-variant path
- `_selectorItem.php` — `variantPicker()` fallback sentinel

## Yang HARUS diperbaiki di SEMUA module lain

### Pattern 1: `_shoppingCart.php` — hold release di `reset()`

Cari blok ini:
```php
$array_hold_sebelumnya = $c->cekLoker(..., "hold", ...);
if (isset($array_hold_sebelumnya['id'])) {
    // update hold to 0
    $c->updateData(array("id" => $array_hold_sebelumnya['id']), array("jumlah" => 0));
    // update active + holdQty
    $array_active_sebelumnya = $c->cekLoker(..., "active", ...);
    if (isset($array_active_sebelumnya['id'])) {
        $c->updateData(array("id" => $array_active_sebelumnya['id']), array("jumlah" => $array_active_sebelumnya['jumlah'] + $array_hold_sebelumnya['jumlah']));
    }
}
```

Ganti jadi:
```php
$holdRow = $c->cekLoker(..., "hold", ...);
if (!isset($holdRow['id']) || !isset($holdRow['jumlah']) || (float)$holdRow['jumlah'] <= 0) { continue; }
$holdQty = (float)$holdRow['jumlah'];

$this->load->model("Coms/ComLockerStockDualWrite");
$dw = new ComLockerStockDualWrite();
$this->db->trans_start();
$dw->pair(array(
    array("static" => array(
        "cabang_id" => $this->session->login['cabang_id'],
        "gudang_id" => $this->session->login['gudang_id'],
        "jenis"     => "produk",
        "state"     => "hold",
        "jumlah"    => -$holdQty,
        "produk_id" => $produkId,
        "nama"      => isset($item['nama']) ? $item['nama'] : '',
        "satuan"    => isset($item['satuan']) ? $item['satuan'] : "n/a",
        "oleh_id"   => $this->session->login['id'],
    )),
    array("static" => array(
        "cabang_id" => $this->session->login['cabang_id'],
        "gudang_id" => $this->session->login['gudang_id'],
        "jenis"     => "produk",
        "state"     => "active",
        "jumlah"    => $holdQty,
        "produk_id" => $produkId,
        "nama"      => isset($item['nama']) ? $item['nama'] : '',
        "satuan"    => isset($item['satuan']) ? $item['satuan'] : "n/a",
        "oleh_id"   => 0,
    )),
)) or die("...");
$this->db->trans_complete();
```

### Pattern 2: `_processSelect*.php` — hold saat add item

Cari blok yang melakukan:
```php
$this->db->trans_start();
// update active (decrease)
$c->updateData(array("id" => $row->id), array("jumlah" => $jml_now - $jml_nambah));
// update or create hold (increase)
$hold = $c->cekLoker(..., "hold", ...);
if (sizeof($hold) > 0) { $c->updateData(...); }
else { $c->addData($data_hold); }
$this->db->trans_complete();
```

Ganti jadi:
```php
$this->load->model("Coms/ComLockerStockDualWrite");
$dw = new ComLockerStockDualWrite();
$this->db->trans_start();
$dw->pair(array(
    array("static" => array(
        "cabang_id" => $cabangId, "gudang_id" => $gudangId,
        "jenis" => "produk", "state" => "active",
        "jumlah" => -$jml_nambah, "produk_id" => $id,
        "nama" => $nama, "satuan" => $satuan, "oleh_id" => 0,
    )),
    array("static" => array(
        "cabang_id" => $cabangId, "gudang_id" => $gudangId,
        "jenis" => "produk", "state" => "hold",
        "jumlah" => $jml_nambah, "produk_id" => $id,
        "nama" => $nama, "satuan" => $satuan,
        "oleh_id" => $userId, "oleh_nama" => $userName,
    )),
)) or die("...");
$this->db->trans_complete();
```

### Pattern 3: `__FollowUp.php` / `FollowUp.php` — approval release

Sama seperti Pattern 1 — cari `cekLoker("hold")` + `updateData` + `addData` untuk hold/active, ganti dengan `ComLockerStockDualWrite::pair()`.

### Pattern 4: Reserve/Release method khusus (seperti `reserveVariantLockerForSelect`)

Beberapa module punya method terpisah untuk variant locker. Identifikasi dengan mencari `MdlLockerStockVariant` + `updateData`/`addData` untuk state `"hold"` atau `"active"`.

## ✅ FIFO Dual-Write — Modul Konversi sebagai Template

### Komponen
- `ComFifoProdukJadiVarian.php` — **sentinel fix**: `variant_id=0`→`1`, skip `variant_id < 0`
- Terdaftar di `konversi/config/coTransaksiCore.php` (4 Type 1 entries) dan `_coTransaksiCore.php` (4 entries)

### Cara register di modul lain
Tambahkan `FifoProdukJadiVarian` di `coTransaksiCore.php` setelah setiap `FifoProdukJadi` Type 1 (yang punya `produk_id`, `unit`, `hpp`, `jml_nilai` di static):

```php
array(
    "comName" => "FifoProdukJadiVarian",
    "loop" => array(),
    "static" => array(
        // ... semua field dari FifoProdukJadi parent ...
        "variant_id" => "variant_id",
        "variant_nama" => "variant_nama",
        "variant2_id" => "variant2_id",
        "variant2_nama" => "variant2_nama",
    ),
    "srcGateName" => "...",
    "srcRawGateName" => "...",
),
```

Gunakan gate yang sama dengan parent. Field varian boleh tidak ada di gate — nanti auto sentinel `1`.

⚠️ **Hindari `replaceAll` untuk rsltItems FIFO** — variant yang baru didaftarkan juga memiliki `srcRawGateName => "rsltItems"` di closing-nya, menyebabkan `replaceAll` kaskade (variant ganda). items2_sum FIFO aman untuk `replaceAll`.

### Filter sentinel di `ComFifoProdukJadiVarian::pair()`
```
variant_id < 0  → skip
variant_id == 0 → ubah jadi 1 (sentinel), proses
variant_id >= 1 → proses langsung
```

## Status — ✅ SELESAI untuk 10 modul inventory

| Module | _shoppingCart reset() | _processSelect* select/remove/edit/cancel | FollowUp |
|--------|----------------------|-------------------------------------------|----------|
| penjualan | ✅ DualWrite | ✅ DualWrite (4 files, 15 blocks) | ✅ (all commented) |
| pembelian | ✅ DualWrite | ✅ DualWrite (4 files, 11 blocks) | ✅ (all commented) |
| pindahgudang | ✅ DualWrite | ✅ DualWrite (3 files, 7 blocks) | ✅ (all commented) |
| opname | ✅ DualWrite | ✅ DualWrite (1 file, 4 blocks) | ✅ (all commented) |
| distribusifg | ✅ DualWrite | ✅ DualWrite (3 files, 11 blocks) | ✅ (all commented) |
| distribusiproduksi | ✅ DualWrite | ✅ DualWrite (3 files, 11 blocks) | ✅ (all commented) |
| produksi | ✅ DualWrite | ✅ DualWrite (4 files, 14 blocks) | ✅ (all commented) |
| produksiproses | ✅ DualWrite | ✅ DualWrite (4 files, 14 blocks) | ✅ (all commented) |
| konversi_varian | ✅ DualWrite | ✅ DualWrite (5 files, 17 blocks) | ✅ (all commented) |
| adjustment | ✅ DualWrite | ✅ DualWrite (1 file, 2 blocks) | ✅ (all commented) |
| **konversi** (done earlier) | ✅ DualWrite | ✅ DualWrite (1 file) | ✅ (all commented) |

**Total:** ~100+ blok locker manual diganti dengan `ComLockerStockDualWrite::pair()`.

## Catatan

- Semua _processSelectSupplies*.php dilewati (MdlLockerStockSupplies, bukan produk)
- FollowUp/__FollowUp: semua locker operation sudah di-comment-out sejak awal — tidak perlu diubah
- `ComLockerStockDualWrite` adalah SATU-SATUNYA entry point untuk semua write stok produk

## Verifikasi

Jalankan audit query untuk cek 0 mismatch:
```sql
SELECT sl.produk_id, sl.cabang_id, sl.gudang_id, sl.state, 
       SUM(sl.jumlah) AS parent, COALESCE(SUM(slv.jumlah), 0) AS variant
FROM stock_locker sl
LEFT JOIN stock_locker_variant slv 
  ON sl.produk_id = slv.produk_id AND sl.cabang_id = slv.cabang_id 
  AND sl.gudang_id = slv.gudang_id AND sl.state = slv.state
WHERE sl.jenis = 'produk'
GROUP BY sl.produk_id, sl.cabang_id, sl.gudang_id, sl.state
HAVING parent != variant;
```

Expected: 0 rows.

---

# Aturan Coding Global
**Berlaku untuk semua Agent di proyek ini. Berdasarkan analisis codebase, bukan asumsi.**

## 1. Konteks Teknologi (Terverifikasi dari Codebase)
- **Bahasa Utama:** PHP 5.6 (Hanya gunakan PHP, JANGAN gunakan Python untuk logika bisnis).
- **Framework:** CodeIgniter **3.1.8** (CI3) — `CI_VERSION = '3.1.8'` di `system/core/CodeIgniter.php`.
- **HMVC Extension:** Wiredesignz MX v5.5 (`application/third_party/MX/`) — subclass_prefix: `MY_`.
- **Database Relasional:** MySQL / MariaDB (via `$this->db->...` CI Query Builder).
- **Database NoSQL:** MongoDB (via library `Mongo_db` di `application/libraries/Mongo_db.php`).
- **Library Pihak Ketiga:** PHPExcel (bukan PhpSpreadsheet), CodeIgniter Curl (Philip Sturgeon).
- **JANGAN gunakan:** Namespace PHP (PSR-4), Composer autoloader untuk class bisnis, atau sintaks CI4 (`app/Controllers/`, `Services::`, dll).

## 2. Struktur Folder & Arsitektur (WAJIB Diikuti)

### 2.1 Struktur HMVC Modul
```
application/
├── core/              # MY_Loader.php, MY_Router.php (extend MX)
├── config/            # config.php, database.php, routes.php
├── controllers/       # Controller non-modul
├── helpers/           # he_*_helper.php (custom helpers)
├── libraries/         # Library custom (Layout, Curl, Mongo_db, PHPExcel, dll)
├── models/
│   └── Coms/          # Model bisnis — prefix Com* (extends CI_Model)
├── modules/
│   └── [nama_modul]/
│       ├── config/    # coTransaksiCore.php, coTransaksiUi.php, coTransaksiLayout.php, coTransaksiValues.php
│       └── controllers/
│           ├── Modul_Controller.php   # Base controller modul (extends MX_Controller)
│           ├── _shoppingCart.php      # extends Modul_Controller
│           ├── _processSelect*.php    # extends Modul_Controller
│           ├── _selectorItem.php      # extends Modul_Controller
│           ├── Create.php             # extends Modul_Controller
│           ├── FollowUp.php           # extends Modul_Controller
│           └── ...
├── third_party/MX/    # Wiredesignz HMVC
└── views/
```

### 2.2 Controller Hierarchy
```
CI_Controller (system/core/Controller.php)
  └── MX_Controller (third_party/MX/Controller.php)
      └── Modul_Controller (modules/[modul]/controllers/Modul_Controller.php)
          └── _shoppingCart, _processSelect*, Create, FollowUp, dll
```

### 2.3 Konvensi Penamaan File Controller (per Modul)
| Prefix | Peran |
|--------|-------|
| `Modul_Controller.php` | Base controller — session validation, load 4 config |
| `_shoppingCart.php` | Cart management + stock locker hold/active |
| `_processSelect*.php` | Add/edit/remove item + hold stock |
| `_selectorItem.php` | Product/supplies picker modal |
| `_processPihak*.php` | Party (customer/supplier) selector |
| `Create.php` | Create transaction (draft) |
| `FollowUp.php` / `__FollowUp.php` | Approval / follow-up |
| `ActivityReport.php` | Transaction report |
| `Printing.php` | Print document |
| `Transaksi.php` | Transaction list |

### 2.4 Konfigurasi Modul (4 Config Wajib)
| File | Isi |
|------|-----|
| `coTransaksiCore.php` | Gateway mapping, component (Com*) registration, FIFO |
| `coTransaksiUi.php` | Step definitions, UI labels, form fields |
| `coTransaksiLayout.php` | Table columns, layout rendering |
| `coTransaksiValues.php` | Value mapping, calculation rules |

## 3. Aturan Coding

### 3.1 Keamanan
- Semua query database WAJIB menggunakan **Query Binding** (`$this->db->query($sql, $binds)`) atau **Query Builder** (`$this->db->where()->get()`).
- JANGAN gunakan query mentah (raw string) tanpa binding.
- **Pengecualian yang ada:** Beberapa `UPDATE transaksi SET indexing_*` di modul `produksi` menggunakan raw string untuk JSON blob — ini technical debt, JANGAN ditiru di kode baru.

### 3.2 Model Pattern
- Model bisnis prefix `Com*` extends `CI_Model`, berlokasi di `application/models/Coms/`.
- Load dengan: `$this->load->model("Coms/ComLockerStockDualWrite");` lalu `$dw = new ComLockerStockDualWrite();`
- Dual-write stock locker **WAJIB** lewat `ComLockerStockDualWrite` — bukan tulis manual ke 2 tabel.

### 3.3 Helper Pattern
- Custom helper prefix `he_` di `application/helpers/`.
- Load dengan: `$this->load->helper("he_url");`
- Helper pairs di `application/helpers/Pairs/` — format `he_cek_*_helper.php`, `he_pair_*_helper.php`.
- Penamaan file WAJIB berakhiran `_helper.php` (konvensi CI3).

### 3.4 Transaksi Database
- Selalu gunakan `$this->db->trans_start()` dan `$this->db->trans_complete()` untuk wrapping operasi multi-query.
- JANGAN manual `BEGIN`/`COMMIT`/`ROLLBACK`.

### 3.5 Komentar & Komunikasi
- Komentar kode berbahasa Indonesia pada logika yang kompleks.
- Langsung ke kode — jangan penjelasan teori panjang.
- Jika instruksi kurang jelas atau berpotensi merusak struktur database, tanyakan dulu sebelum menulis kode.

### 3.6 PHP 5.6 Compatibility (JANGAN Gunakan)
- JANGAN gunakan: `...` spread operator, null coalescing `??`, anonymous classes, return type declarations, scalar type hints, `match` expression, named arguments.
- Gunakan: `isset($x) ? $x : $default` (bukan `$x ?? $default`).
- Gunakan: `array()` syntax (bukan `[]` short array — meskipun beberapa kode baru sudah pakai, tetap pakai `array()` untuk konsistensi).
- Gunakan: `function($x) { ... }` untuk closure (bukan arrow function `fn($x) => ...`).

### 3.7 Sinkronisasi Dokumentasi Otomatis (Doc-Sync)
Setiap kali Agent melakukan perbaikan, modifikasi, atau penambahan fitur pada suatu modul di `application/modules/`, Agent **WAJIB** secara langsung:
1. Memperbarui file dokumentasi `.md` yang terkait di repository aturan terpusat (lokasi network drive `W:\AGENT_GLOBAL` atau path global `\\192.168.168.14\web\AGENT_GLOBAL`):
   - **PT. Everest:** `[AGENT_GLOBAL_ROOT]/clients/everest/erp_web/modules/[nama_modul].md`
   - **PT. INDOSAN:** `[AGENT_GLOBAL_ROOT]/clients/indosan/erp_web/modules/[nama_modul].md`
   - **Kamus Transaksi:** `kamus_transaksi.md` masing-masing klien jika ada penambahan `jenisTr` baru.
2. Membuat atau memperbarui file jurnal pengembangan lokal (`dev-jurnal.md`) pada direktori modul: `application/modules/[nama_modul]/docs/dev-jurnal.md`. Jurnal ini harus mencatat setiap daftar masalah (Bug), analisis penyebab, berkas yang diubah, dan solusi perbaikan kode yang dilakukan pada sesi berjalan sebelum turn/sesi diselesaikan.

## 4. Standarisasi Refactoring (Thin Controller - Fat Service)
Proyek ini mengadopsi prinsip *Thin Controller* untuk menjaga keterbacaan kode (terutama pada file *Controller* yang memiliki ribuan baris kode seperti `Create.php` dan `FollowUp.php`). Jika Anda ditugaskan untuk melakukan *refactoring*, patuhi aturan pemindahan berikut. Standar ini berlaku untuk **seluruh modul inventory** (pembelian, penjualan, distribusi, produksi, dll).

### 4.1 Pemindahan Logika Pembentuk Antarmuka (UI) → Helper
- Metode-metode berstatus *private* yang hanya bertugas menghasilkan string HTML (seperti pembuatan tombol, pemuatan dropdown, dll) **WAJIB** diekstraksi ke dalam file Helper khusus modul.
- **Lokasi Penyimpanan:** `application/modules/[nama_modul]/helpers/he_[nama_modul]_ui_helper.php`
- **Cara Pemuatan:** `$this->load->helper("he_[nama_modul]_ui");` (perhatikan bahwa ekstensi `_helper.php` tidak boleh ditulis saat di-*load*).
- **Aturan Nama Fungsi:** Gunakan prefix global untuk mencegah bentrok, contoh: `he_pembelian_ui_buildAddPihakButton()`. Jangan simpan helper spesifik modul ini di folder `application/helpers/` global.

### 4.2 Pemindahan Logika Bisnis Masif (Transaksi/Simpan) → Library
- *Public methods* utama pada Controller (seperti `save()`, `doEdit()`, dll) yang memiliki **lebih dari 200 baris** kode algoritma **WAJIB** diekstrak ke sebuah kelas Library/Service (mengikuti standar *Clean Code — Robert C. Martin*).
- Method yang berukuran **200 baris atau kurang** boleh tetap tinggal di Controller.
- **Lokasi Penyimpanan:** `application/modules/[nama_modul]/libraries/Lib[NamaModul][NamaController][Domain].php`
- **Contoh:** `LibPembelianCreateSave.php`, `LibPembelianFollowUpRevert.php`

### 4.3 Aturan Pemecahan File Library
- **Method > 2.000 baris** → wajib dipecah menjadi **1 file Library per method** (misal: `LibPembelianFollowUpDoRevert.php` hanya berisi `processDoRevert()`).
- **Domain < 2.000 baris total** → boleh digabung beberapa method terkait dalam 1 file Library (misal: `LibPembelianFollowUpEdit.php` berisi `processDoPreEdit()` + `processEditForm()`).
- **Maks baris per file Library:** ~5.000 - 8.000 baris. Jika melebihi, wajib dipecah lagi.
- Pengelompokan berdasarkan **domain fungsional**: Save, Edit, Cancel, Revert, Approval, dll.

### 4.4 Mekanisme Pemindahan Aman (*Safe Refactoring*)
1. Jangan ubah `$this->db` menjadi pola lain secara manual satu per satu yang berisiko merusak kode *legacy*.
2. Suntikkan *instance* Controller ke dalam fungsi Library: `$this->libpembeliancreatesave->processSave($this);`
3. Pada sisi Library, tangkap parameter tersebut sebagai `$c`. 
4. Lakukan *Regex Replace* massal untuk mengubah string literal `$this->` menjadi `$c->` di seluruh badan fungsi yang dipindah. Hal ini menjaga dependensi asli CI (seperti `$c->db` atau `$c->session`) tetap berjalan sempurna dari dalam kelas Library.

### 4.5 Standar Dokumentasi Refactoring
- **Di sisi Library:** Setiap method yang dipindah **WAJIB** memiliki DocBlock lengkap (nama, deskripsi, `@param`, `@return`, asal usul) **DAN** inline comment di setiap blok logika utama.
- **Di sisi Controller:** Baris bekas method yang dipindah harus memiliki komentar *Tombstone* yang mencantumkan nama method asli dan lokasi file Library tujuannya.
- **Blueprint per modul:** Setiap modul yang di-refactoring harus memiliki dokumen blueprint di `application/modules/[nama_modul]/docs/refactoring/`.

### 4.6 Verifikasi & Testing
- Setiap Library yang baru dibuat **WAJIB** memiliki **script test sederhana** (file PHP mandiri yang memanggil method dengan skenario dasar) untuk memastikan tidak ada *Fatal Error* sebelum dan sesudah variant migration.
- Verifikasi manual lewat UI tetap dilakukan sebagai lapisan tambahan.

### 4.7 Referensi Standar
- **SOLID/SRP (Single Responsibility Principle):** Setiap class/file hanya bertanggung jawab atas satu domain fungsional.
- **Clean Code (Robert C. Martin):** Fungsi idealnya tidak lebih dari 200 baris; fungsi yang lebih besar harus diekstrak.
- **ISO/IEC 25010 — Maintainability:**
  - *Modularity:* Komponen terpisah, perubahan di satu modul tidak berdampak ke modul lain.
  - *Analysability:* File yang lebih kecil dan terfokus lebih mudah dianalisis saat debugging.
  - *Modifiability:* *Change locality* — perubahan logika approval cukup menyentuh 1 file Library.
  - *Reusability:* Helper utilitas bisa dipakai ulang oleh controller lain dalam modul yang sama.

### 4.8 Standar Dokumentasi Alur Kerja (Workflow Human & Teknis)
Setiap kali melakukan analisis atau pengerjaan pada suatu modul baru, Agent **WAJIB** membuat dokumen alur kerja nyata di folder `application/modules/[nama_modul]/docs/refactoring/workflow_[nama_modul].md` dengan struktur standar seperti pada modul **Pembelian** ([workflow_pembelian.md](file:///w:/new_san_variant/application/modules/pembelian/docs/refactoring/workflow_pembelian.md)) yang mencakup:
1.  **Diagram Alir Workflow (Mermaid Diagram):** Menampilkan visualisasi langkah (step) dari inisiasi draf hingga persetujuan akhir beserta aktor/pengguna yang terlibat di antarmuka (UI).
2.  **Rincian Langkah Operasional (Step-by-Step):** Setiap langkah harus dijabarkan secara rinci yang mencakup:
    -   **Aktor:** Kelompok pengguna (wewenang `userGroup` di config UI).
    -   **Interaksi UI Pengguna:** Tombol apa yang diklik, apa URL web yang diakses di browser.
    -   **Logika Sistem & Database:** Apa yang terjadi di balik layar (proses FIFO, jurnal akuntansi, penulisan ke tabel registry).
    -   **Dampak pada Persediaan:** Apakah stok bertambah/berkurang di loker stok, hold, atau 0% (tidak berdampak).
3.  **Cross-Reference Log Aktivitas:** Melakukan validasi silang terhadap data log nyata pada tabel database `log` untuk memastikan kode transaksi (`jenisTr`) yang aktif digunakan di lapangan dan mencantumkan statistik penggunaannya.

### 4.9 Standar Rencana UAT (UAT Plan) Berdasarkan Workflow
Setelah dokumen alur kerja dibuat, Agent **WAJIB** melengkapinya dengan dokumen rencana pengujian pengguna di folder `application/modules/[nama_modul]/docs/refactoring/uat_[nama_modul].md` dengan mencontoh struktur dari modul **Pembelian** ([uat_pembelian.md](file:///w:/new_san_variant/application/modules/pembelian/docs/refactoring/uat_pembelian.md)) dengan format sebagai berikut:
1.  **Skenario UAT Mandiri (Automated Self-UAT):** Menyiapkan skrip test terisolasi (PHP CLI) yang meniru alur API/Controller dari Create & FollowUp untuk memastikan tidak ada Fatal Error / Query Exception saat eksekusi mutasi data.
2.  **Skenario UAT Pengguna (Manual Human UAT):** Menyusun daftar periksa (checklist) pengujian fungsional di halaman antarmuka web, meliputi:
    -   *Pre-requisite:* Kelompok pengguna (session login userGroup) yang digunakan untuk menguji.
    -   *Test Case:* Urutan input data (menggunakan **data produk dan supplier riil aktif** yang diambil dari database), tombol aksi yang ditekan, serta respon dialog popup.
    -   *Expected Result:* Perubahan status dokumen di menu transaksi, histori registry baru, dan bertambah/berkurangnya stok loker di database.


### 4.10 Pengamanan Kritis Transaksi Loker (Titik Domino)
Pergerakan locker stock (`stock_locker` dan `stock_locker_variant`) pada selector produk (hingga `variantPicker`), `shoppingcart->remove()`, dan `shoppingcart->reset()` adalah **titik kritis (critical points) penentu keberhasilan transaksi**. Setiap Agent wajib mematuhi pengamanan berikut untuk menghindari efek domino error:
1.  **Selector & variantPicker:** Pengurangan stok `active` dan penambahan stok `hold` wajib dibungkus dalam transaksi database (`trans_start`/`trans_complete`) dan didelegasikan secara mutlak melalui `ComLockerStockDualWrite` dengan menyertakan variant ID yang presisi dari dropdown picker.
2.  **shoppingCart->remove():** Saat menghapus item tunggal dari cart, stok `hold` terkait wajib dirilis kembali ke stok `active` menggunakan `ComLockerStockDualWrite` sebelum data session dihapus, guna mencegah data hold menggantung (*stale*).
3.  **shoppingCart->reset():** Saat membatalkan seluruh keranjang belanja, rilis stok `hold` ke `active` dilakukan secara massal dalam transaksi database, dan item session wajib dibersihkan dari nilai kuantitas `<= 0` agar pre-processor FIFO tidak mengolah cache kosong yang dapat memblokir submit transaksi.





# Aturan File Sampah (Garbage Files)

JANGAN pernah memodifikasi atau memperbarui file sampah (garbage files). Ciri-cirinya adalah memiliki dua garis bawah (underscore) di depan atau di belakang nama file sebelum ekstensi .php (misalnya: __FollowUp.php atau FollowUp__.php, dan pola sejenis lainnya). 

Jika Anda menemukan file dengan pola tersebut dan tidak yakin apakah boleh diedit, **wajib mengajukan pertanyaan** kepada User terlebih dahulu sebelum melakukan perubahan apa pun.

