# Sintesis Modul Pembelian, Penerimaan, Pembayaran, dan Biaya

**Ruang lingkup:** Rantai dari hulu (Permintaan/Purchase Order) sampai hilir (Settlement & Beban), beserta refactoring controller, tata kelola biaya, serta fitur Item Hold/Reject.
**Workspace:** `new_san_variant` (path operasional `w:/new_san_variant`)
**Standar acuan:** *Single Variant Standard & Dual-Write Rollout*
**Basis dokumen:** 20 file markdown di `W:\_md_result\file_md_san` (total ±144.386 byte)
**Rentang tanggal dokumen:** analisis blueprint 2026-07-29; pembaruan checklist biaya 2026-04-08; dokumen refactoring & UAT tidak mencantumkan tanggal.

---

## 1. Ringkasan Eksekutif

1. **Ekosistem ini bukan satu modul, melainkan rantai yang berbagi satu kernel tabel.** Semua modul — `pembelian`, `pembelianimport`, `pembelianjasa`, `pembelianprojek`, `penerimaan`, `penerimaanprojek`, `pembayaran`, `biaya`, `pettycast` — memakai `Modul_Controller.php` dengan pola identik: `URI segment(4)` → `$this->jenisTr`, `URI segment(1)` → `$this->modul`, session namespace `$cCode = "_TR_" . $this->jenisTr`, empat file konfigurasi (`coTransaksiUi`, `coTransaksiCore`, `coTransaksiLayout`, `coTransaksiValues`), dan satu set tabel yang sama.
2. **Table map yang dipakai hampir seluruh modul:** `transaksi`, `transaksi_data`, `transaksi_values`, `transaksi_data_values`, `transaksi_sign`, `transaksi_extstep`, `transaksi_registry`. Tabel `transaksi_registry` berfungsi sebagai snapshot ber-`blobEncode()` (serialized + base64) pada kolom `main` dan `tableIn_master`; `transaksi_sign` menyimpan tanda tangan approval per step.
3. **Nomor `jenisTr` yang paling konsisten antar-dokumen adalah `489` (Pelunasan Hutang Pembelian / AP Settlement)** — muncul di `blueprint_pembelian.md` sebagai "Pelunasan Hutang Pembelian" dan di `blueprint_pembayaran.md` sebagai "Pelunasan Hutang Pembelian (Accounts Payable Payment)", konsisten dengan pemisahan domain: pembelian membukukan hutang, pembayaran melunasinya.
4. **Ada kontradiksi fundamental pada arti `466`.** `blueprint_pembelian.md` menyebut `466` = *Uang Muka Pembelian*, sedangkan `workflow_pembelian.md` dan `uat_pembelian.md` menyebut `466` = *FG Purchasing* dengan rantai status `466r` (Pre-PO) → `466` (PO) → `467` (GRN) → `111` (PPN Masukan). Yang kedua lebih mutakhir dan lebih terverifikasi karena menyebut kode `467` dan `111` yang sama sekali absen dari daftar `jenisTr` blueprint. Detail di Bagian 9.
5. **Inti eksekusi semua modul terkonsentrasi di `FollowUp::doFollowup()`**, dengan pola lima langkah yang seragam: (1) update status `transaksi` → `completed`/naik step, (2) panggil `ComJurnal` untuk jurnal akuntansi, (3) dual-write stok fisik, (4) entry FIFO HPP, (5) log ke `transaksi_registry`.
6. **Komponen shared yang wajib ada di setiap penerimaan barang:** `ComLockerStockDualWrite::pair()` untuk `stock_locker` + `stock_locker_variant` (dengan sentinel `variant_id=1` bila `variant_id=0`), `ComFifoProdukJadi` / `ComFifoProdukJadiVarian` untuk batch HPP per varian, dan `LockerValue` untuk nilai pajak/kewajiban hutang.
7. **Refactoring "Thin Controller – Fat Service" sudah dijalankan dan hasilnya terdokumentasi.** `Create.php` turun dari ±9.480 menjadi **1.135 baris** (−88%) dan `FollowUp.php` dari **23.691 menjadi 7.694 baris** (−67%) — angka aktual ini lebih besar dari proyeksi blueprint (±400 dan ±900 baris), yang berarti work item cleanup belum tuntas.
8. **Guardrail harga sudah masuk produksi pada scope 2 dari 5.** `priceDeltaValidator` aktif untuk transaksi `466` dengan threshold default **10%**, reason field `note` per item (fallback `keterangan` main), `onlyVariant = true`, plus panel *Set Harga Global Produk Varian* pada cart mode `GLOBAL`. Fase H3 (popup info harga), H4 (data quality), H5 (UAT+hardening) belum dikerjakan.
9. **Keamanan modul `biaya`/`pettycast` bergerak pada P0–P1 dengan acuan 2026-04-08**: `csrf_protection = true`, endpoint mutasi `GET`→`POST`, HMAC/timestamp/replay-protection pada `API/ModulConnect.php`, penghapusan bypass login `AutoPostingBiaya`, dan `trans_start()` sebelum query `FOR UPDATE` di `FollowUp::doFollowup`. Item P2–P3 (deprecate `__*`, pecah controller, regression checklist) masih terbuka.
10. **Fitur Item Hold vs Reject adalah satu-satunya fitur yang punya skema DDL lengkap dan audit trail.** Tabel `transaksi_data_item_reject_log` dengan kolom `tipe_aksi` (`reject`/`hold`), `qty_reject`, `alasan`, `dtime_resolved`, dan `status`. Namun dokumen ini dideklarasikan untuk modul `penjualan`, bukan `pembelian`.

---

## 2. Peta Modul

### 2.1 Modul Aggar (Procurement Chain)

| Area | Numerik / Codec | Fungsi Utama | Dokumen Sumber |
|---|---|---|---|
| Pembelian Lokal / PO | `461`, `460`, `463`, `463`(indent), `1463`, `1763`, `3463` | PO reguler, tunai langsung, indent/varian, komposit, konsinyasi; HPP Awal + PPN + alokasi ongkos kirim | `blueprint_pembelian.md` |
| Pembelian — Uang Muka & Pelunasan | `466`, `489` | DP pembelian, pelunasan hutang pembelian | `blueprint_pembelian.md` |
| Retur Pembelian | `961` (stok), `1961` (non-stok) | Retur dengan pengurangan stok vs retur non-stok | `blueprint_pembelian.md` |
| Alokasi Ongkos / Freight | `487` | Alokasi biaya angkut ke HPP tiap varian | `blueprint_pembelian.md`, `blueprint_pembelianimport.md`, `blueprint_pembayaran.md` |
| FG Purchasing (operasional) | `466` → `466r` → `467` → `111` | Pre-PO → PO resmi → GRN → PPN Masukan | `workflow_pembelian.md`, `uat_pembelian.md` |
| Variant Cutover | `881`, `7881` | Pre-processor FIFO varian; bug data flow `items2_sum` | `blueprint_create_save_step1.md` |
| Pembelian Impor | `461`, `466`, `487`, `961` | Commercial Invoice, DP valas, landed cost, retur impor | `blueprint_pembelianimport.md` |
| Pembelian Jasa | `463`, `1463`, `1763` | Tagihan subkontrak, retur jasa, Service PO; PPh 23/21 | `blueprint_pembelianjasa.md` |
| Pembelian Proyek | `3463` | Material & jasa proyek → WIP | `blueprint_pembelianprojek.md` |
| Penerimaan (AR) | `749`, `2749`, `1749`, `7499` | Pelunasan piutang, DP penjualan, kas/bank lain-lain, kliring giro | `blueprint_penerimaan.md` |
| Penerimaan Proyek | `749`, `7488`, `2749`, `7499` | Termin proyek, Progress Billing Certificate, retensi | `blueprint_penerimaanprojek.md` |
| Pembayaran (AP) | `489`, `462`, `487`, `4447`, `682` | Pelunasan AP, utang giro/biaya, pajak & retensi | `blueprint_pembayaran.md` |
| Biaya & Supplies | `677`, `675`, `676`, `762`, `2675` | Beban operasional, supplies varian, gaji, akrual | `blueprint_biaya.md` |
| Biaya (urutan step aktual) | `2676`, `7762`, `677`, `2677`, `1674`, `675`, `2675`, `1675`, `4675`, `1677`, `762`, `9982`–`9985`, `9922`, `119`, `676`, `2762`, `742`, `743`, `2674` | Transisi state 2-step berbasis `coTransaksiUi.php` | `urutan-step.md` |
| Petty Cash | `671`, `672`, `771` | Voucher kas kecil, replenishment, plafon | `blueprint_pettycast.md` |

### 2.2 ModulTurunan & Helper

| Area | Numerik / Codec | Fungsi Utama | Dokumen Sumber |
|---|---|---|---|
| Multi-Invoice AR Matcher | `_processSelectNota.php` | Match piutang outstanding, pelunasan parsial/multi-faktur | `blueprint_penerimaan.md` |
| Multi-Invoice AP Matcher | `_processSelectNota.php`, `_processSelectNota2.php` | Match hutang outstanding untuk 1 bukti bayar | `blueprint_pembayaran.md` |
| Milestone AR Matcher | `_processSelectNota.php` | Termin proyek + retensi (contoh 5%) | `blueprint_penerimaanprojek.md` |
| Freight & Price Exception | `_processSelectProductException.php` | Alokasi freight-in & selisih harga ke HPP varian | `blueprint_pembelian.md` |
| Landed Cost Engine | `_processSelectProductException.php` | FOB + Freight + Insurance + Bea Masuk per varian | `blueprint_pembelianimport.md` |
| Service Tax Engine | `_processSelectProductPpn.php` | PPN 11%, PPh 23/21, direct COA | `blueprint_pembelianjasa.md` |
| Project Costing | `_processPihak.php`, `_processSelectProduct.php` | Bind `projekID` → WIP | `blueprint_pembelianprojek.md` |
| Supplies Variant | `_processSelectSupplies.php` | `resolveRequestItemIdentity()`, cart key `supplies:{id}:{variant_id}` | `blueprint_biaya.md` |
| Payroll Expense | `_processSelectRekeningGaji.php` | Alokasi gaji per divisi, PPh 21 | `blueprint_biaya.md` |
| Petty Cash Ceiling | `_processSelectPlafonPettycash.php` | Batas plafon per cabang/kasir | `blueprint_pettycast.md` |
| Item Selector | `_selectorItem/selectItem` + `MdlProdukPerSupplier` | Batasi barang sesuai supplier | `workflow_pembelian.md` |
| Price History | `MdlHargaProdukPerSupplier` | Harga beli default dari histori | `workflow_pembelian.md` |
| Overdue Pass | `MdlOverDuePass` | Otorisasi pelunasan lewat tenggat kredit | `blueprint_pembayaran.md` |
| Item Hold/Reject Log | `transaksi_data_item_reject_log` | Audit trail reject/hold + auto-resolve | `DOKUMENTASI_FITUR_ITEM_HOLD_DAN_REJECT_FOLLOWUP.md` |

---

## 3. Alur Pembelian End-to-End

### 3.1 Stage 1 — Pre-Purchase Order (Draf)

- **Aktor:** Staff Purchasing, user group `c_purchasing`.
- **URL:** buka `pembelian/Create/index/466`, simpan via POST `pembelian/Create/save/466`.
- **Input:** `pihakID` (supplier), nomor faktur supplier, `placeID`/`gudangID` (cabang/gudang penerima), item + qty.
- **Rantai selector:** `_selectorItem/selectItem` memanggil `MdlProdukPerSupplier` agar hanya barang terasosiasi supplier terpilih yang tampil; endpoint `_processSelectProduct/select` dipicu dinamis untuk mengisi harga default dari `MdlHargaProdukPerSupplier`.
- **Kalkulasi:** `coTransaksiCore` menetapkan `ppn = (ppnFactor * harga) / 100`; `grand_total = nett`.
- **Persistensi:** record baru di `transaksi` dengan tipe `466r`, `status = 0`; snapshot ke `transaksi_registry` kolom `main` + `tableIn_master` via `blobEncode()`.
- **Dampak stok: 0%.** Tidak ada perubahan stok maupun booking.
- **Label UI:** "PRE PURCHASE PO - Pending Approval" (merah).

### 3.2 Stage 2 — PO Resmi (Otorisasi)

- **Aktor:** Supervisor/Manajer Pembelian, user group `c_purchasing_adm`.
- **URL list:** `/pembelian/Transaksi/index/466`; detail via `/pembelian/ViewDetails/nomer/466/466r...`; edit cepat via `FollowUp/updateItems` yang memanggil `doUpdateApproval()`.
- **Aksi:** POST `/pembelian/FollowUp/doFollowup/466/[transaksi_id]/2/1`.
- **Efek:** `transaksi.status` `0` → `1`; tanda tangan masuk `transaksi_sign`; snapshot `transaksi_registry` diperbarui; nomor berubah `466r.*` → `466.*`.
- **Label UI:** "PURCHASE ORDER - Purchased" (oranye).
- **Dampak stok: 0%.** Dokumen dikirim ke vendor.

### 3.3 Stage 3 — Pre-GRN & GRN (Penerimaan Gudang)

- **Aktor:** Staff Gudang/Logistik, user group `c_gudang`.
- **URL:** dialog `/pembelian/FollowUp/followupPreview/466/[transaksi_id]/3/2`.
- **Input:** kolom "Jumlah Diterima" (*received quantity*) —Quantity dapat lebih kecil dari PO (partial receipt); tombol "Receive & Make GRN".
- **Rantai sistem:**
  1. `FifoProdukJadi` & `FifoProdukJadiVarian` memproses antrean HPP barang masuk.
  2. Transaksi turunan tipe `467` (GRN) dibuat dengan relasi ke PO induk melalui `reference_nomer` + `reference_id`.
  3. `ComLockerStockDualWrite::pair()` menambah stok aktif pada `stock_locker` dan `stock_locker_variant`.
  4. Jurnal: **Debet Persediaan Barang Jadi** vs **Kredit Unbilled AP (Hutang yang Belum Ditagih)**.
- **Dampak stok: 100%** — stok riil bertambah (contoh UAT: `+5 Unit` instan).
- **Label UI:** "GOODS RECEIVED NOTE - GRN Made" (hijau).

### 3.4 Stage 4 — Invoice & Realisasi PPN Masukan

- **Aktor:** Staff Finance/Pajak, user group `c_finance`.
- **URL:** `/pembelian/FollowUp/followupPreview/466/[transaksi_id]/4/3`; aksi "Approve PPN Masukan" + input nomor seri faktur pajak.
- **Rantai `preProcessor` `467` → `111` di `coTransaksiCore.php`:**
  1. `LockerValue` pertama: catat `ppn in` pada status aktif loker nilai.
  2. `LockerValue` kedua: catat kewajiban `piutang pembelian` = total tagihan − PPN.
  3. `transaksi.status` → `3` (final).
- **Jurnal:** Debet Unbilled AP + Debet PPN Masukan → Kredit Hutang Dagang (AP).
- **Hasil akhir:** transaksi tertutup logistik & akuntansi, masuk antrean jadwal pembayaran (modul `pembayaran`, `jenisTr` `489`).

### 3.5 Stage 5 — Settlement

- **Modul:** `pembayaran`, matcher `_processSelectNota.php` + `_processSelectNota2.php` menampilkan seluruh outstanding AP per `pihakID`, menerima pelunasan parsial maupun multi-nota dalam satu bukti bayar.
- **Eksekusi `FollowUp::doFollowup()`:** AP rebalancing di `transaksi_values`, jurnal **Debet Hutang Dagang** vs **Kredit Kas/Bank/Utang Giro** + Potongan Pelunasan, cek `MdlOverDuePass` bila lewat toleransi kredit, lalu log `transaksi_registry`.
- **Fitur khas:** Giro Mundur (status cair/tolak), early settlement discount, `4447` untuk pembayaran potongan pajak & retensi.

### 3.6 Stage 6 — Penerimaan Kas (Sisi Debit)

`blueprint_penerimaan.md` mendokumentasikan cermin dari `pembayaran`: `FollowUp::doFollowup()` melakukan AR rebalancing di `transaksi_values`, jurnal **Debet Kas/Bank/Giro** vs **Kredit Piutang Penjualan** + Diskon Pelunasan (D). Varian proyek (`blueprint_penerimaanprojek.md`) menambah layer retensi: jurnal **Debet Kas/Bank + Debet Piutang Retensi** vs **Kredit Piutang Termin Proyek**, dengan uang muka proyek dipotong sebagai elemen D.

---

## 4. Refactoring Create & FollowUp

### 4.1 Profil dan Peta Method

`Create.php` — baseline ±9.480 baris, 11 method:

| # | Method | ± Baris | Domain | Target |
|---|--------|---------|--------|--------|
| 1 | `__construct()` | 38 | Inisialisasi | Tetap di Controller |
| 2 | `index()` | 511 | UI/Form | partly → `he_pembelian_ui_helper.php` |
| 3 | `preview()` | 532 | Simpan/Preview | `LibPembelianCreateSave` |
| 4 | `save()` | — | Simpan/Eksekusi | `LibPembelianCreateSave` ✅ |
| 5 | `viewIncompleteStepAntarCabang()` | 175 | Laporan | Evaluasi (<200) |
| 6 | `recordColumn()` | 80 | Utilitas | Tetap |
| 7 | `editPreview()` | 302 | Edit/Preview | `LibPembelianCreateEdit` |
| 8 | `doEdit()` | 2.445 | Edit/Eksekusi | `LibPembelianCreateEdit` |
| 9 | `preCancelPackingPreview()` | 273 | Batal/Preview | `LibPembelianCreateCancel` |
| 10 | `doPreCancelPacking()` | 2.387 | Batal/Eksekusi | `LibPembelianCreateCancel` |
| 11 | `swapFrom()` | 162 | Utilitas | Tetap |

`FollowUp.php` — baseline **23.691 baris**, 24 method (22 public, 2 private); terbesar `doRevert()` 5.481 baris. Domain teragregasi: `doRevert()` 5.481, `doFollowup()` 5.018, `followupPreview()` 2.867, `doCancelPacking()` 2.582, `doRevertAll()` 1.484, `followupCancelPackingPreview()` 1.979, `followupCancelPackingPrePreview()` 594, `preCancelPacking()` 455, `doPreEdit()` 442, `editForm()` 405, `index()` 622, `shopingCart()` 114, `doScan()` 93, `doUpdateApproval()` 74, `followupPreviewMobile()` 44, `doResetMobile()` 27, `clearContent()` 23, `parseDashIds()` 22 (private), `lookupGroupedQtyByJenisProduk()` 51 (private), `viewIncompleteStepAntarCabang()` 175, `updateItems()` 18, `viewIncomplete()` 5.

### 4.2 Aturan Pengelompokan (SRP)

- **Prinsip:** *Thin Controller – Fat Service* (AGENTS.md §4), SRP/SOLID, ISO/IEC 25010.
- **Pasangan tak terpisah:** method eksekusi (`save`, `doEdit`, `doFollowup`, `doRevert`) + method preview-nya (`preview`, `editPreview`, `followupPreview`, `followupPrePreview`) — selalu berubah bersama → satu file untuk *change locality*.
- **Ambang batas Create:** method < 200 baris tetap di Controller; file Library maks ±5.000–8.000 baris; domain < 1.000 baris digabung ke domain terdekat.
- **Ambang batas FollowUp:** method > 2.000 baris → 1 file per method; domain < 2.000 baris per method boleh digabung.
- **Penamaan:** `Lib[Modul][Controller][Domain].php`; helper `he_pembelian_*_helper.php`.

### 4.3 Hasil Aktual vs Proyeksi

| File | Awal | Proyeksi blueprint | Hasil aktual (`evaluasi_refactoring.md`) | Selisih |
|------|------|--------------------|--------------------------------------|--------|
| `Create.php` | ±9.480 | ±400 | **1.135** | +735 |
| `FollowUp.php` | 23.691 | ±900 | **7.694** | +6.794 |

Komponen aktual yang berhasil dibuat: `LibPembelianCreateSave.php`, `LibPembelianCreateEdit.php`, `LibPembelianCreateCancel.php`, `he_pembelian_followup_helper.php`, `LibPembelianFollowUpFollowupPreview.php`, `LibPembelianFollowUpDoFollowup.php`, `LibPembelianFollowUpDoRevert.php`, `LibPembelianFollowUpDoCancelPacking.php`, `LibPembelianFollowUpRevertAll.php`, `LibPembelianFollowUpCancelPreview.php`, `LibPembelianFollowUpEdit.php`.

### 4.4 Aturan Data Flow Wajib (dari `blueprint_create_save_step1.md`)

Modul yang dibahas adalah **`variant_cutover`**, bukan `pembelian`, dengan `jenisTr` **881** dan **7881**. Rantai data create→save:

```
items (session, dari POST form)
  → PreProcessor FifoProdukJadi / FifoProdukJadiVarian
    (membaca resultParams dari coTransaksiCore.php)
  → rsltItems (hanya punya field dari resultParams)
  → inject ke items2_sum  (Create.php baris ~1544–1557)
  → fillValues_he_value_builder (mapping ke tableIn)
  → session disimpan ke DB
```

**Gejala:** `items2_sum` tidak punya `label`, `produk_kode`, `satuan`, `variant_id`, `sku`; akibatnya step 2 (follow-up approval) menulis `transaksi_data` dengan `produk_id=0`, `produk_nama=''`, `produk_kode=''`, `produk_label=''`, `satuan=''`, `variant_id=0`, `variant_sku=''`.

**Root cause #1 — `resultParams` tidak lengkap di `application/modules/variant_cutover/config/coTransaksiCore.php`:** blok 881 (baris 203–219) dan 7881 (baris 946–962) hanya memetakan `id → produk_id`, `nama → nama`, `name → nama`, `hpp → hpp`, `jml → qty`, `qty → qty`.

**Root cause #2 — konfigurasi dibaca dari file yang salah:** `Create.php` baris 1446 membaca `$this->configCore[$this->jenisTr]['preProcessor'][$jenisTrTarget]['detail']`, yaitu **`coTransaksiCore.php`**, bukan `coTransaksiValues.php` (yang dibaca `fillValues` helper). Penambahan field sebelumnya di `coTransaksiValues.php` karena itu tidak efektif untuk create→save.

**Perbedaan 881 vs 7881:** komponen `FifoProdukJadi` (config di `coTransaksiCore:188`) vs `FifoProdukJadiVarian` (config di `coTransaksiCore:931`). Keduanya punya `resultParams` sama terbatas.

**Status perbaikan:**

| Fix | Isi | Status | Lokasi |
|-----|-----|--------|--------|
| #1 resultParams 881 | Tambah `label`, `produk_kode`, `satuan`, `variant_id`, `sku` | ✅ | `coTransaksiCore.php:212-216` |
| #1 resultParams 7881 | Sama | ✅ | `coTransaksiCore.php:960-964` |
| #2 Enrichment | Loop `$_SESSION[$cCode]['items2_sum']`, lookup `produk` (`id, nama, kode, label, satuan, sku`) dan fallback `produk_variant` untuk `variant_id`, di antara pre-processor dan `fillValues` | ✅ | `Create.php:1594-1626` |
| #3 Sinkronisasi config | Pindahkan/sinkronkan isi `coTransaksiValues` → `coTransaksiCore` | ⏳ Belum | — |

**Kriteria verifikasi:** entry varian di `items2_sum` punya `id` = `produk_id` varian serta `label`/`produk_kode`/`satuan`/`variant_id`/`sku` terisi; di step 2, `transaksi_data` punya `produk_id ≠ 0`, `valid_qty = jml`, `variant_id` dan `variant_sku` terisi.

**Protokol wajib dari `evaluasi_refactoring.md`:**
- **Backup lokal sesaat:** sebelum memodifikasi controller utama, duplikasi menjadi `[NamaFile].php.bak_[fase_refactoring]` (contoh `FollowUp.php.bak_f7`) di direktori yang sama, lalu **hapus segera** setelah lolos `php -l`.
- **JANGAN pakai file `__*`** (`__FollowUp.php`, `__Create.php`) sebagai basis — itu cadangan statis tahun 2024 yang tidak sinkron dengan kode berjalan (kehilangan `updateItems()` dan `doUpdateApproval()`). Gunakan `FollowUp_asli.php`.
- **Metodologi:** *Regex Line-Boundary Slicing* via script (bukan replace teks manual) → potong body → ganti `$this->` → `$c->` hanya di blok yang dipindah → tulis delegasi:
  ```php
  public function namaFungsi()
  {
      $this->load->library("LibPembelian[NamaController][Domain]");
      $this->libpembelian[controller][domain]->process[NamaFungsi]($this);
  }
  ```
- **Verifikasi wajib:** `php -l path/to/file.php`.
- **Uji validasi 4 langkah:** Dry Run (cart) → Preview Validation → Submission Test (approval bertahap di FollowUp) → Revert/Cancel Test (stok kembali benar).

---

## 5. Modul Biaya & Petty Cash

### 5.1 Modul `biaya` (Biaya & Supplies)

**Domain:** pengeluaran biaya operasional, pembelian & pemakaian supplies/bahan penolong bervarian, penggajian, PPh 21/23/4(2).

**Komponen kunci:**
- `_processSelectSupplies.php` → `resolveRequestItemIdentity()` memetakan `supplies_id` + `variant_id`, cart key **`supplies:{id}:{variant_id}`**, dual-write via **`ComLockerStockSuppliesDualWrite`**.
- `_processSelectRekeningGaji.php` → alokasi komponen gaji per divisi + PPh 21 + tunjangan.
- `Taxes.php` → PPh Pasal 23, PPh 4(2) atas sewa/jasa.
- `FollowUp::doFollowup()` → `ComJurnal` (Beban D / Kas-Bank-Hutang K), `ComLockerStockSuppliesDualWrite::pair()`, `transaksi_registry`.

### 5.2 Modul `pettycast` (Kas Kecil)

**Domain:** `671` Pengeluaran Kas Kecil, `672` Pengisian Kembali (Imprest Replenishment), `771` Pembentukan/Perubahan Plafon.
`_processSelectPlafonPettycash.php` menerima ID Cabang, batas plafon, kasir penanggung jawab, lalu memastikan akumulasi pengeluaran tidak melebihi plafon sebelum replenishment. `FollowUp::doFollowup()` mengurangi sisa dana `671` atau menambah dari Bank Utama untuk `672`, jurnal **Beban Operasional Kas Kecil (D) vs Kas Kecil/Kas Utama (K)**.

### 5.3 Status Keamanan — `biaya` (`revisi_biaya_opt.md`, update 2026-04-08)

| Item | Status | Detail |
|------|--------|--------|
| P0.1 Hardening `API/ModulConnect.php` | ✅ | `api_update_bridge`, `api_update_masterBridge`, `api_masterBridgePL` GET→POST; HMAC+timestamp+replay protection; hapus `Access-Control-Allow-Origin: *`; matikan `$forceDebug = 1` & `display_errors`; tolak tanpa signature `401/403` |
| P0.2 Deserialization aman | ✅/◐ | Parser aman terpusat; `blobDecode()` dilarang untuk request user; sisa: format JSON + signature bila tetap encoded |
| P0.3 SQL injection | ◐ | Cast `(int)` di endpoint prioritas + mitigasi di `MdlMother`; sisa: whitelist `addParams`, hentikan concatenation di join/where |
| P0.4 Otorisasi | ◐ | Bypass login `AutoPostingBiaya` dihapus; API eksternal whitelist service account; tahap 1 validasi `stepCodeByEmployeeID` + place pada `_processSelect*`/`_shoppingCart`; sisa: wajib *return stop*, validasi place untuk semua endpoint |
| P0.5 CSRF | ✅/◐ | `$config['csrf_protection'] = true`; semua AJAX kirim token; API/Webhook prioritas pindah POST & GET ditolak; sisa: audit idempotency key |
| P0.6 Locking & race | ✅ | `trans_start()` **sebelum** query `FOR UPDATE` di `FollowUp::doFollowup`; `bookingNumber` pakai random bytes + timestamp, divalidasi awal & akhir; unique guard counter via `writeMainEntries` + hardening atomic `CustomCounter` |
| P1.1 Boundary transaksi | ◐ | `trans_status()` jadi penentu commit/rollback; `trans_begin` ambigu dibersihkan di `_shoppingCart`; sisa: ganti `die/matiHere` dengan `try/catch` |
| P1.2 Kill-switch/debug | ◐ | `Debug.php` dinonaktifkan via route/env guard; sisa: bersihkan `arrPrint`, `cek*`, `matiHere`, `die` |
| P1.3 Upload image | ✅ | MIME whitelist + ekstensi + ukuran sebelum ke CDN; fallback `curl_exec` timeout; simpan hanya URL tervalidasi (`he_url_helper.php`) |
| P1.4 Bug laten | ✅ | `$id` undefined di `recordImage`; `api_return_biaya_get` return variabel undefined; `index_get` yang `echo(index)`; mismatch class/file (`Create_total.php`, `Taxes.php`); urutan serialisasi `serialize(base64_encode(...))` |
| P1.5 Webhook | ✅ | Hapus `arrPrint($_GET)`; hanya POST; signature verification; rate-limit + request-id log |
| P2.1 File legacy | ☐ | Status `__Create.php`/`__FollowUp.php` belum diputuskan |
| P2.2 Pecah controller | ☐ | Ekstrak registry/persistensi & validasi request; turunkan method >300 baris |
| P2.3 Standardisasi response | ☐ | JSON konsisten (`status`, `message`, `data`) |
| P3.1–P3.2 | ☐ | Regression checklist manual & SOP rollback belum ditulis |

### 5.4 Status Keamanan — `pettycast` (`revisi_biaya.md`)

P0 item 1–3 selesai (`unserialize(base64_decode($_GET[...]))` → `json_decode(..., true)` + whitelist + error terstruktur; SQL injection via Query Builder/binding; guard akses disentralisasi di `Modul_Controller.php`, `Debug.php` dibatasi). P1 item 4–6 selesai (boundary `trans_start`/`trans_complete`/`trans_status`; idempotency key + duplicate guard; locking untuk agregat). **P2 (7–8) dan P3 (9–10) belum dikerjakan** — termasuk penetapan status file `__*`/`*_OLD.php`.

Catatan cakupan: modul `pettycast` punya 5 config, 26 controller, 23 template, 10 views. Modul `biaya` lebih luas: 6 config, 30 controller (+2 API), 13 model, 11 views, 34 template. File lintas-modul wajib ikut direvisi: `application/config/config.php`, `application/config/rest.php`, `application/helpers/he_misc_helper.php`, `application/helpers/he_url_helper.php`, `application/models/Mdls/MdlMother.php`.

### 5.5 Urutan Step Biaya (`urutan-step.md`)

Semua sub-modul memakai pola dua step: `(Draft/Baru)` → `<jenisTr>r` → `<jenisTr>`. Contoh lengkap:

| jenisTr | Deskripsi | Step 1 (otorisasi → label) | Step 2 (otorisasi → label) |
|---------|-----------|---------------------------|---------------------------|
| `2676` | Otorisasi biaya produksi | request for expense (`sys` → `request`) | approve request (`c_finance` → `approved`) |
| `7762` | Pembiayaan supplies (receipt number) | make order (`c_holding` → `pending approval`) | approve biaya (`c_holding` → `approved`) |
| `677` | Biaya usaha | request (`o_kasir` → `request`) | approve request (`o_finance` → `approved`) |
| `2677` | Otorisasi biaya usaha | request (`c_finance` → `request`) | approve request (`c_finance` → `approved`) |
| `1674` | Salary expense | request for salary expense (`c_holding`) | approve request (`c_holding`) |
| `675` | Biaya umum | request for expense (`o_kasir`) | approve request (`o_finance`) |
| `2675` | Otorisasi request biaya umum | request for expense (`c_finance`) | approve request (`c_finance`) |
| `1675` | Biaya umum (pusat) | request for expense (`o_kasir`) | approve request (`o_finance`) |
| `4675` | Biaya (pusat) | request for expense (`c_finance`) | approve request (`c_finance`) |
| `1677` | Biaya usaha (pusat) | request (`o_kasir`) | approve request (`o_finance`) |
| `762` | Pembiayaan supplies | make order (`c_holding`) | approve biaya (`c_holding`) |
| `9982` | Transfer ke marketing expense | make transfer (`c_finance`) | approve transfer expense (`c_finance`) |
| `9983` | Transfer ke general expense | make transfer (`c_finance`) | approve transfer expense (`c_finance`) |
| `9984` | Transfer ke production expense | make transfer (`c_finance`) | approve transfer expense (`c_finance`) |
| `9985` | Transfer marketing ke other expense | make order (`o_finance`) | approve pemindahan (`o_finance`) |
| `9922` | Koreksi biaya ke PPV | koreksi (`c_finance`) | approve koreksi (`c_finance`) |
| `119` | Imbalan jasa | save (`c_purchasing`) | approve request (`c_purchasing_spv`) |
| `676` | Biaya produksi | request for expense (`p_produksi`) | approve request (`p_produksi_spv`) |
| `2762` | Pembiayaan supplies (branch) | make order (`o_gudang`) | approve biaya (`o_gudang`) |
| `742` | Pendapatan lain-lain | save → langsung `742` (`c_finance`) | — (single step) |
| `743` | Biaya lain-lain | save → langsung `743` (`c_finance`) | — (single step) |
| `2674` | Salary expense (pusat) | request for salary expense (`c_holding`) | approve request (`c_holding`) |

---

## 6. Item Hold / Reject pada FollowUp

### 6.1 Masalah Bisnis

Pada order fulfillment bertingkat (SPO → SO → Surat Jalan → Invoice), operator sering tidak memproses seluruh item sekaligus. Dua kasus yang sebelumnya tercampur: **Reject** (item dibatalkan permanen, tidak boleh muncul lagi di outstanding) dan **Hold** (item ditunda, pesanan tetap berlaku, harus tetap outstanding untuk di-follow up di transaksi lanjutan).

### 6.2 Perbandingan Perilaku

| Komponen | Reject (Tolak Permanen) | Hold (Tunda Approval) |
|----------|----------------------|------------------------|
| Label aksi | `1. Tolak & Batalkan Permanen (Reject)` | `2. Tunda / Lepas dari Approval Saat Ini` |
| Sub-teks | *Item TIDAK AKAN muncul lagi di daftar outstanding berikutnya.* | *Item tidak ikut di-approve sekarang, TETAP berstatus outstanding untuk di-approve pada proses berikutnya.* |
| Validasi alasan | Wajib ≥ 10 karakter | Wajib ≥ 10 karakter |
| Penampung session | `$_SESSION[$cCode]['items_rejected']` | `$_SESSION[$cCode]['items_hold']` |
| Panel preview | Merah (`panel-danger`) | Kuning/Oranye (`panel-warning`) |
| Pemulihan | Tombol `Batal Reject` | Tombol `Batal Tunda` |
| Tabel log | `transaksi_data_item_reject_log`, `tipe_aksi='reject'` | `transaksi_data_item_reject_log`, `tipe_aksi='hold'` |
| Status di `transaksi_data` | Ditutup `status = 0`, outstanding hangus | **Tetap aktif `status = 1`** |
| Siklus hidup log | Permanen `status = 1` | `status = 1` → **auto-resolved `status = 0`** saat di-approve di transaksi lanjutan |

### 6.3 Skema Database

Tabel `transaksi_data_item_reject_log` dengan kolom: `id` (PK auto-increment), `transaksi_id` int(11), `transaksi_nomer` varchar(100), `step_asal` varchar(50), `step_tujuan` varchar(50), `tipe_aksi` varchar(20) default `'reject'`, `produk_id` int(11), `produk_kode` varchar(100), `produk_nama` varchar(255), `qty_reject` decimal(12,2) default `0.00`, `alasan` text NOT NULL, `oleh_id` int(11), `oleh_nama` varchar(100), `cabang_id` int(11) default `0`, `cabang_nama` varchar(100), `dtime` datetime NOT NULL, `dtime_resolved` datetime NULL, `status` tinyint(1) default `1`. Engine InnoDB, charset utf8. Index: `idx_transaksi_id`, `idx_produk_id`, `idx_tipe_aksi`, `idx_status`.

### 6.4 Struktur Session & Fungsi

Namespace session: `$cCode = "_TR_" . $this->jenisTr`. Tiap entri `items_rejected`/`items_hold` menyimpan `transaksi_id`, `produk_id`, `tipe_aksi`, `alasan`, serta snapshot undo: `backup_item`, `backup_tableIn_detail`, `backup_tableIn_detail_values`, `backup_items_child`.

Setiap aksi `removeItem()`, `undoRejectItem()`, `undoHoldItem()` wajib menjalankan:

```php
$_SESSION[$cCode]['main']['harga'] = 0;
foreach ($_SESSION[$cCode]['items'] as $id => $iSpec) {
    $_SESSION[$cCode]['main']['harga'] += ($iSpec['jml'] * $iSpec['harga']);
}
$this->load->helper("he_value_builder");
resetValues($this->jenisTr);
fillValues_he_value_builder($this->jenisTr, $fromStep, $intoStep,
    $this->configCoreJenis, $this->configUiJenis, $this->configValuesJenis,
    my_ppn_factor());
```

**Model `application/models/MdlTransaksi.php`:** `recordItemActionLogs(array $items, $actionType = 'reject')` (memastikan kolom tersedia + batch insert), `recordItemRejectLogs(array $rejectItems)`, `recordItemHoldLogs(array $holdItems)`, `resolveItemHoldStatus($transaksiId, $produkIds)`, `closeItemOutstanding($transaksiId, $produkId, $qtyReject = 0)`, `getItemRejectLogsByTransaksi($transaksiId, $produkId = null, $actionType = null)`.

**Controller `application/modules/penjualan/controllers/_followupLiveEdit.php`:** `removeItem()` (tangkap `action` = `reject`/`hold` + `alasan`, simpan snapshot, reset nilai), `undoRejectItem()`, `undoHoldItem()`.

**Controller `application/modules/penjualan/controllers/FollowUp.php`:** `followupPreview()` menyediakan `$itemsRejected`, `$itemsHold`, `$undoRejectItemTarget`, `$undoHoldItemTarget`. `doFollowup()` menyimpan log `items_rejected` → `closeItemOutstanding()` → `resolveItemHoldStatus()`, lalu menyimpan log `items_hold`, lalu mengambil daftar produk ter-approve dan memanggil `resolveItemHoldStatus($transaksiID_reference, $approvedProductIds)`.

**View `application/modules/penjualan/views/transaksi.php`:** modal `window.confirmRemoveItemFollowup` dengan radio button interaktif, label/placeholder dinamis, live character counter `X/10`, validasi min 10 karakter; panel merah & panel kuning masing-masing dengan tombol Batal.

### 6.5 Aturan untuk Developer Next

1. Alur follow-up baru di `penjualan` **atau `pembelian`** harus memanggil `MdlTransaksi::resolveItemHoldStatus()` saat commit berhasil.
2. **Jangan mengubah status di `transaksi_data` saat Hold** — item harus tetap aktif agar query outstanding menemukannya.
3. Validasi alasan utama ada di form modal; `removeItem()` hanya *safety-net* auto-padding di backend.

---

## 7. UAT, Checklist & Standar

### 7.1 UAT Otomatis (Self-UAT)

- **Lingkungan:** database `new_san_variant` di IP `192.168.5.10` (port default), dijalankan dari root `w:/new_san_variant`.
- **Skrip:** `application/modules/pembelian/tests/uat_pembelian_test.php` atau via bootstrap CI CLI.
- **4 skenario & kriteria kelulusan:**
  1. **Draf** — `$c->libpembeliancreatesave->processSave($c)`; lolos bila `transaksi` tipe `466r` `status = 0` dan snapshot `transaksi_registry` (serialized base64) tersimpan.
  2. **PO Step 2** — `$c->libpembelianfollowupdofollowup->processDoFollowup($c)`; lolos bila `status` → `1` dan `transaksi_sign` bertambah.
  3. **GRN Step 3** — lolos bila `transaksi` tipe `467` terbentuk, stok bertambah riil di `stock_locker` + `stock_locker_variant` via `ComLockerStockDualWrite::pair()`, dan antrean HPP FIFO baru terbentuk.
  4. **PPN Step 4** — lolos bila `piutang pembelian` dan `ppn in` aktif di loker nilai.

### 7.2 UAT Manual (Human/QA)

| Skenario | Prasyarat | Data Uji | Hasil Diharapkan |
|----------|-----------|----------|------------------|
| A — Draf Pre-PO | `c_purchasing` | Supplier `ATMI SOLO, PT` → produk `D2-A` (Steel filing cabinet 2 drawer) atau `D4-A` (4 drawer); supplier `BOOIL SAFES CO., LTD` → `B28D`/`B41D` (ALPEN). Qty **5 Unit** | Notifikasi "Transaksi berhasil disimpan"; redirect ke preview PO; status "PRE PURCHASE PO - Pending Approval" merah |
| B — Approval PO | `c_purchasing_adm` | Dokumen `466r.-1.xxxx` | Popup tertutup; status "PURCHASE ORDER - Purchased" oranye; nomor `466r.*` → `466.*` |
| C — GRN | `c_gudang` | PO dari B; qty diterima **5 Unit** | Layar reload; status "GOODS RECEIVED NOTE - GRN Made" hijau; `stock_locker` & `stock_locker_variant` bertambah `+5 Unit` instan |
| D — Otorisasi Pajak | `c_finance` | GRN dari C; nomor seri faktur pajak | Status "Approved"; hutang dagang resmi terbentuk di modul Hutang |

### 7.3 Standar Engineering

- **Runtime:** PHP 5.6 + CodeIgniter 3; hindari sintaks PHP 7+ (`??`, scalar/return type hint, anonymous class).
- **Linting:** `C:\xampp\php\php.exe -l` (blueprint) / `php -l path/to/file.php` (evaluasi).
- **Dokumentasi:** setiap blueprint harus diikuti pembaruan `docs/uat-checklist.md` dan file `dev-jurnal.md`.
- **Aturan eksekusi checklist:** kerjakan berurutan P0→P3 tanpa lompat; satu item = satu patch kecil agar mudah rollback; smoke test minimal `create → followup → history → print` setelah tiap item.
- **Prioritas:** patch P0 sebelum optimasi performa/refactor; setiap perubahan keamanan wajib disertai uji backward-compatibility, dan perubahan kontrak request disiapkan transisi bertahap.

### 7.4 Purchase Price Guardrail (`purchase-price-guardrail-checklist.md`)

Penomoran fase memakai **H1–H5** dan **terpisah** dari `docs/variant-rollout-checklist.md` (Fase 1/2/3 rollout varian).

| Fase | Scope | Status |
|------|-------|--------|
| **H1** Quick Win | Kolom `Delta Price` (`Unit Price - Last Purchase Price`) di shopping cart, kolom `Delta %`, indikator warna naik/turun, read-only | ✅ selesai |
| **H2** Guardrail Deviasi | Parameter threshold deviasi di config; blok transaksi bila deviasi melewati threshold dan alasan belum diisi; berlaku di submit/preview **dan** follow-up. Implementasi: config `priceDeltaValidator` pada transaksi `466`, threshold default **10%**, reason field `note` item (fallback `keterangan` main), `onlyVariant = true`, panel *Set Harga Global Produk Varian* untuk cart mode `GLOBAL` | ✅ selesai |
| **H3** UX Ringan | Popup "Info Harga" per item; histori harga supplier terpilih (5 transaksi terakhir); ringkasan supplier lain (top 3 opsional) | ☐ belum |
| **H4** Data Quality | Flag produk: harga supplier kosong, harga `0`, harga stale (> 90 hari); tautan ke matrix harga supplier | ☐ belum |
| **H5** UAT + Hardening | UAT skenario normal/deviasi/role non-otoritas; tuning pesan, threshold, fallback; finalisasi checklist audit trail | ☐ belum |

Prinsip: gangguan UX minimal, kontrol ketat hanya saat anomali, *role-based enforcement*, dan **tidak mengubah struktur approval ERP yang sudah stabil**.

---

## 8. Temuan Lintas-Dokumen & Kontradiksi

### 8.1 `466` — Uang Muka vs FG Purchasing (kontradiksi paling material)

- `blueprint_pembelian.md`: `466` = **Uang Muka Pembelian**.
- `workflow_pembelian.md` + `uat_pembelian.md`: `466` = **FG Purchasing**; `466r` = Pre-PO; `467` = GRN; `111` = PPN Masukan.
- **Penilaian:** dokumen workflow/UAT lebih mutakhir dan lebih kuat, karena (a) keduanya saling mengonfirmasi, (b) keduanya menyebut `status` aktual di DB (`0`/`1`/`3`) dan label UI berwarna, (c) keduanya menyebut kode `467` dan `111` yang tidak ada di daftar `jenisTr` blueprint sama sekali, (d) guardrail harga H2 secara eksplisit memakai config `priceDeltaValidator` **pada transaksi `466`** dengan `onlyVariant`, yang konsisten dengan `466` = pembelian barang varian Finished Goods.
- **Implikasi:** daftar `jenisTr` pada `blueprint_pembelian.md` perlu diaudit ulang sebelum dipakai sebagai acuan.

### 8.2 Blueprint vs Hasil Aktual Refactoring

Proyeksi `Create.php` ±400 baris vs aktual **1.135**; proyeksi `FollowUp.php` ±900 baris vs aktual **7.694** (gap 6.794 baris). Sumber mana yang berlaku untuk estimasi kerja: **`evaluasi_refactoring.md`** (angka hasil terukur), bukan proyeksi di blueprint. Konsekuensi: work item cleanup belum tuntas — kemungkinan masih ada method yang menetap di controller (mis. `index()` 622 baris dan `viewIncompleteStepAntarCabang()` 175 baris di `FollowUp`; `index()` 511 baris di `Create`).

### 8.3 Semantik `jenisTr` Modul `biaya`: `blueprint_biaya.md` vs `urutan-step.md`

| Kode | `blueprint_biaya.md` (2026-07-29) | `urutan-step.md` (claims derived from `coTransaksiUi.php`) |
|------|-----------------------------------|--------------------------------------------------------------|
| `675` | Pembelian Supplies / Bahan Penolong | **Biaya umum** |
| `676` | Biaya Gaji & Honorarium | **Biaya produksi** |
| `677` | Pengeluaran Biaya Operasional General | **Biaya usaha** |
| `762` | Pembayaran Beban Akrual / Utang Biaya | **Pembiayaan supplies** |
| `2675` | Biaya Dihutangkan | Otorisasi request biaya umum |

**Penilaian:** `urutan-step.md` lebih bermutu karena diturunkan langsung dari file konfigurasi `coTransaksiUi.php` (sumber kebenaran runtime) dan mencakup 22 sub-modul dengan detail tombol, otorisasi, dan label status. `blueprint_biaya.md` menyatukan hanya 5 `jenisTr` dan tidak menyebut user group sama sekali. Perhatikan juga `1674`/`2674` (salary expense) ada di `urutan-step.md` dan di daftar template `revisi_biaya_opt.md` (`1674.html`, `1674r.html`), tapi **tidak** ada di daftar `jenisTr` utama `blueprint_biaya.md`.

### 8.4 Judul vs Isi Checklist Biaya

`revisi_biaya.md` berjudul "Checklist Perbaikan Modul **Biaya** (`pettycast`)" tetapi seluruh path di dalamnya `application/modules/pettycast/...`. `revisi_biaya_opt.md` yang merupakan versi-plebih Optimal dari dokumen tersebut mencakup "Modul Biaya (`biaya`)" dengan path `application/modules/biaya/...`. Jadi keduanya **dua modul berbeda**, bukan dua versi dokumen yang sama — dan penamaan judul yang pertama menyesatkan. Selain itu `revisi_biaya_opt.md` memakai struktur P0.1–P0.6/P1.1–P1.5/P2.1–P2.3/P3.1–P3.2, sedangkan `revisi_biaya.md` memakai penomoran P0 1–3, P1 4–6, P2 7–8, P3 9–10 — **struktur penomoran tidak dapat dipetakan 1:1**.

### 8.5 Kode `jenisTr` yang Digunakan Ganda oleh Modul Berbeda

- `461`: "Pembelian Lokal Reguler / PO" (`blueprint_pembelian.md`) **dan** "Pembelian Impor Reguler" (`blueprint_pembelianimport.md`).
- `463`: "Pembelian Indent / Order Varian" (`blueprint_pembelian.md`) **dan** "Pembelian Jasa / Tagihan Vendor Subkontraktor" (`blueprint_pembelianjasa.md`).
- `1763`: "Pembelian Komposit" (`blueprint_pembelian.md`) **dan** "Order Pembelian Jasa" (`blueprint_pembelianjasa.md`).
- `3463`: "Pembelian Konsinyasi" (`blueprint_pembelian.md`) **dan** "Tagihan Pembelian Material & Jasa Proyek" (`blueprint_pembelianprojek.md`).
- `487`: "Biaya Angkut / Alokasi Biaya Pembelian" (`pembelian`) **dan** "Landed Cost Allocation" (`pembelianimport`) **dan** "Pembayaran Biaya Transaksi & Freight" (`pembayaran`).
- `749`, `2749`, `7499`: muncul identik di `penerimaan` dan `penerimaanprojek` — konsisten (modul proyek mewarisi kode modul induk).

### 8.6 Path Workspace

Blueprint semua modul menunjuk `c:/xampp/htdocs/new_san_variant/...`, sedangkan `evaluasi_refactoring.md`, `workflow_pembelian.md`, dan `uat_pembelian.md` menunjuk `w:/new_san_variant/...`. Untuk instruksi kerja, path yang dipakai adalah **`w:/new_san_variant`**.

### 8.7 Cakungan Modul: `blueprint_create_save_step1.md` Keluar dari Rangkaian `pembelian`

Dokumen itu secara eksplisit bekerja pada modul **`variant_cutover`** dengan `jenisTr` **881** dan **7881**, bukan `pembelian`. Sebaliknya, dokumen fitur Item Hold/Reject mendeklarasikan modul **`penjualan`**; hanya panduan developer poin #1 yang memperluas cakupan ke `pembelian` — sehingga porting fitur tersebut ke `pembelian` **belum terdokumentasi sebagai selesai**.

### 8.8 Kontradiksi Status "Completed & Verified"

Seluruh 9 file blueprint menutup dengan status "🟢 **Pekerjaan Selesai (Completed & Verified)**". Namun `revisi_biaya_opt.md` (lebih granular, 2026-04-08) mencatat banyak item P0–P1 masih `☐`, dan `purchase-price-guardrail-checklist.md` mencatat H3–H5 `☐ belum`. Status "selesai" pada blueprint berarti **selesai sebagai dokumen analisis**, bukan sebagai implementasi fitur.

### 8.9 Fix #3 yang Masih Menggantung

`blueprint_create_save_step1.md` Fix #3 (sinkronisasi `coTransaksiValues` → `coTransaksiCore`) berstatus ⏳ **Belum**. Sampai disinkronkan, konfigurasi bersifat ambigu: developer berikutnya berisiko menambahkan field di file yang tidak dibaca pre-processor runner — persis root cause yang menghambat fix sebelumnya.

---

## 9. Lampiran — Daftar File Sumber

| Nama File | Ukuran (byte) | Topik H1 | Peran |
|-----------|---------------|----------|-------|
| `blueprint_pembelian.md` | 6.905 | BLUEPRINT CETAK BIRU MODUL: PEMBELIAN | Definisi jenisTr 11 kode, arsitektur controller `pembelian`, `_processSelectProductException`, `ComLockerStockDualWrite`, `ComFifoProdukJadiVarian` |
| `blueprint_pembelianimport.md` | 5.656 | BLUEPRINT CETAK BIRU MODUL: PEMBELIAN IMPORT | Landed cost engine, multi-currency (USD/EUR/RMB), PPh 22 Impor, dual-write stok impor |
| `blueprint_pembelianjasa.md` | 4.916 | BLUEPRINT CETAK BIRU MODUL: PEMBELIAN JASA (SERVICE PURCHASING & OUTSOURCING) | Non-inventory direct COA, PPh 23/21 withholding, jurnal Beban Jasa |
| `blueprint_pembelianprojek.md` | 4.775 | BLUEPRINT CETAK BIRU MODUL: PEMBELIAN PROYEK (PROJECT PROCUREMENT) | `projekID` binding, WIP accounting, `3463` |
| `blueprint_create_refactoring.md` | 6.177 | Blueprint Refactoring: Modul Pembelian (Create.php) | Peta 11 method `Create.php`, aturan pemecahan file, 4 fase eksekusi |
| `blueprint_followup_refactoring.md` | 9.703 | Blueprint Refactoring: Modul Pembelian (FollowUp.php) | Peta 24 method `FollowUp.php`, ambang 2.000 baris/method, 8 fase eksekusi |
| `blueprint_create_save_step1.md` | 6.931 | Blueprint: Fix Data Flow Step 1 Create->Save() untuk Variant Cutover (881/7881) | Root cause `resultParams`, konfigurasi `coTransaksiCore` vs `coTransaksiValues`, enrichment `items2_sum` |
| `blueprint_penerimaan.md` | 4.749 | BLUEPRINT CETAK BIRU MODUL: PENERIMAAAN (RECEIPTS & AR SETTLEMENTS) | Multi-invoice AR matcher `_processSelectNota`, AR rebalancing, diskon pelunasan |
| `blueprint_penerimaanprojek.md` | 4.891 | BLUEPRINT CETAK BIRU MODUL: PENERIMAAAN PROYEK (PROJECT BILLING & MILESTONE RECEIPTS) | Milestone billing, retensi, jurnal Piutang Retensi |
| `blueprint_pembayaran.md` | 5.429 | BLUEPRINT CETAK BIRU MODUL: PEMBAYARAN (ACCOUNTS PAYABLE & SETTLEMENTS) | AP matcher ganda, Giro Mundur, `MdlOverDuePass`, settlement discount |
| `workflow_pembelian.md` | 7.469 | Dokumentasi Workflow Human & Teknis Modul Pembelian (FG Purchasing - 466) | Alur 4 langkah end-to-end, URL, user group, status DB, jurnal, chain `466r→466→467→111` |
| `uat_pembelian.md` | 5.654 | Rencana Pengujian Pengguna (UAT Plan) Modul Pembelian (FG Purchasing - 466) | Skenario UAT otomatis + manual, data uji supplier/produk nyata, kriteria kelulusan |
| `evaluasi_refactoring.md` | 6.200 | Evaluasi & Panduan Refactoring Modul Pembelian (Pilot Project) | Hasil aktual (1.135/7.694 baris), protokol backup `.bak_*`, larangan file `__*`, metodologi slicing, uji 4 langkah |
| `revisi_biaya.md` | 11.168 | Checklist Perbaikan Modul Biaya (`pettycast`) | Checklist keamanan P0–P3 modul `pettycast`, 64 file dalam cakupan |
| `revisi_biaya_opt.md` | 20.833 | Checklist Optimal Perbaikan Modul Biaya (`biaya`) | Checklist optimal P0.1–P3.2 modul `biaya`, hardening API, locking, CSRF, 98 file dalam cakupan |
| `blueprint_biaya.md` | 5.402 | BLUEPRINT CETAK BIRU MODUL: BIAYA & SUPPLIES | `ComLockerStockSuppliesDualWrite`, cart key `supplies:{id}:{variant_id}`, gaji, PPh 23/4(2) |
| `blueprint_pettycast.md` | 4.645 | BLUEPRINT CETAK BIRU MODUL: PETTY CASH (KAS KECIL) | Plafon kas kecil, imprest replenishment, jurnal kas kecil |
| `urutan-step.md` | 9.471 | Urutan Step Transaksi Modul Biaya | 22 sub-modul `jenisTr`; transisi state 2-step, tombol, user group, label status dari `coTransaksiUi.php` |
| `purchase-price-guardrail-checklist.md` | 1.978 | Purchase Price Guardrail Checklist (H1-H5) | H1 delta visual, H2 `priceDeltaValidator` threshold 10%, H3–H5 belum |
| `DOKUMENTASI_FITUR_ITEM_HOLD_DAN_REJECT_FOLLOWUP.md` | 11.434 | DOKUMENTASI TEKNIS: FITUR ITEM REJECT & ITEM HOLD (TUNDA) PADA FOLLOW-UP TRANSAKSI | DDL `transaksi_data_item_reject_log`, session `items_rejected`/`items_hold`, 6 method `MdlTransaksi`, auto-resolve |

**Total:** 20 file, ±144.386 byte, ±2.568 baris markdown.