﻿# Produk Varian & Migrasi Variant Cutover pada ERP San

> **Dokumen sintesis kanonik** dari 28 dokumen sumber cluster `san-varian` (folder `W:\_md_result\file_md_san`).
> **Environment yang berlaku di seluruh dokumen:** PHP 5.6 · CodeIgniter 3 · MariaDB 10 · CentOS 7.
> **Rentang tanggal sumber:** 2026-04-23 s.d. 2026-07-27. Dokumen tanpa tanggal tidak dapat diposisikan dan ditandai eksplisit.
> **Cakupan:** master varian, model data, migrasi holding-subidiary, playbook cutover stok, standar mutu ISO 25010, UAT, dan dampak ke modul QM.

---

## 1. Ringkasan Eksekutif

Cluster ini menggambarkan satu-arepak transformasi produk ERP legacy (non-varian) menjadi produk varian-aware, dengan dua pilar arsitektur yang saling bertumpu dan belum sepenuhnya disatukan.

**Pilar 1 — Single Variant Standard.** Menghapus *dual standard* (produk non-varian versus varian) dengan memaksa seluruh produk memiliki `variant_id`, termasuk *default variant* ber-sentinel `variant_id = -1` untuk produk non-varian. Key session diseragamkan menjadi `variant:{produk_id}:{variant_id}`, tabel stok diseragamkan ke `stock_locker_variant`, dan pipeline `rsltItems2` menggantikan pasangan `rsltItems` + `rsltItems2`. Estimasi biaya: sekitar **763 baris kode dihapus**, **100+ file**, **8-12 minggu** (`blueprint-konverter-multi-variant.md`, tanpa tanggal).

**Pilar 2 — Variant Cutover.** Modul transaksional yang memindahkan stok parent ke stok varian secara terkontrol melalui kode transaksi **`881` (cabang)** dan **`7881` (pusat)**, dengan gate `produk_source` / `produk_target`, lifecycle status `DRAFT` → `IN_PROGRESS` → `COMPLETED` → `ROLLED_BACK`, serta guard `parent stock = 0`, `open document blocking`, dan `override_stok_parent`.

### 1.1 Status kanonik menurut tanggal

| Tanggal | Sumber | Status yang dicatat |
|---|---|---|
| 2026-07-27 | `laporan-perbaikan-varian-multi-modul.md` | "100% Selesai di 10 Modul Inventory" (daftar di baris sama berisi 12 nama modul) |
| 2026-07-27 | `roadmap-ui-variant-non-inventory.md` | "ALL 4 CATEGORIES COMPLETED & VERIFIED" pada 6 modul non-inventory + modul laporan |
| 2026-06-02 | `UAT_MERGE_20260602.md` | Merge **204 file** `_backup/` (seluruh modul + shared engine, overwritten DEV→LIVE); **40 test case seluruh kolom masih kosong** |
| 2026-05-22 | `RENCANA_PERBAIKAN_CHECKLIST.md` | Header "sekitar 60% sesuai standar", tabel progress menghitung **8 dari 33 item (24%)** |
| 2026-05-01 | `variant-rollout-checklist.md` | Audit produksi rakitan; 881 masih "UAT pending" |
| 2026-04-27 | `PLAN_MIGRASI_VARIAN_HOLDING_SUBSIDIARY.md` | Master plan 9 fase disetujui, version stamp `variant-f1-20260427` |
| 2026-04-27 | `UAT_PROGRESS_VARIAN_2026-04-27.md` | UAT subsidiary pertama; hanya penjualan varian yang tersedia; dummy seed gagal |

**Kesimpulan eksekutif.** Klaim "100% selesai" per 2026-07-27 berada pada domain *bug fix varian multi-modul inventaris*, **bukan** pada domain kepatuhan `variant_cutover` terhadap ISO 25010. Pengukuran terakhir yang eksplisit untuk domain kedua adalah **24% (8 dari 33 item) per 2026-05-22**, dan tidak ada dokumen setelah itu yang mengukurnya ulang. Karena itu status kanonik per 2026-07-27 adalah: *implementasi varian di jalur transaksi inventaris sudah tuntas, kepatuhan `variant_cutover` terhadap standar ISO 25010 masih pada tahap awal, dan bukti UAT merge 2026-06-02 belum diisi.*

### 1.2 Angka kunci yang wajib dijaga dalam narasi

- **`881`** — kode transaksi cutover varian cabang (branch).
- **`7881`** — kode transaksi cutover varian pusat (center).
- **`881r` / `7881r`** — dokumen target step 1 (request) untuk masing-masing flow.
- **`466` → `467`** — pembelian: PO → GOODS RECEIVED NOTE (GRN) di step 3 modul `pembelian`.
- **`1334` / `334`** — `konversi` pusat/cabang (bukan cutover varian).
- **`776` dan `1671`** — **tidak dispesifikasikan di sumber** 28 file cluster ini. Angka tersebut muncul pada dokumen lain di luar cluster (776 = assembling produksi / master work order produksi proses; 1671 = pettycash pusat) dan **tidak boleh diklaim** sebagai bagian dari varian/cutover.
- **22** akar masalah sistemik, **12** file kritis, **33** item perbaikan ISO, **22** pola bug (1A sampai 5J), **19** kriteria pemeriksaan pra-rilis, **40** test case UAT merge, **28** file sumber.

---

## 2. Konsep: Single Variant Standard vs Variant Cutover

### 2.1 Peta konsep

| Aspek | Single Variant Standard | Variant Cutover |
|---|---|---|
| Dokumen acuan | `blueprint-konverter-multi-variant.md` | `STANDAR_MODUL_VARIANT_CUTOVER_ISO25010.md`, `WORKFLOW_MODUL_VARIANT_CUTOVER.md`, `variant-cutover-playbook.md` |
| Modul | `konversi` (workspace `w:/san_staging/application/modules/konversi`) | `variant_cutover` (successor `konversi_varian`) |
| Tujuan | Menghapus dual standard agar semua produk punya `variant_id` | Memindahkan stok parent ke stok varian dengan gate dan audit |
| Sifat | Refactor arsitektur data dan session | Transaksi operasional plus kontrol kepatuhan |
| Pendekatan | Opsi A: Default Variant Generator + view `stock_unified` | Module-based, config-driven, 2 step approve |
| Skala | 763 baris dihapus, 8-12 minggu, 4 fase | 33 item perbaikan ISO, 24% tercapai per 2026-05-22 |

### 2.2 Prinsip inti Single Variant Standard

> **Setiap produk adalah varian.** Produk tanpa varian eksplisit otomatis memiliki satu varian default dengan `variant_id = -1`.

Implikasi yang ditegaskan sumber:

- Tidak ada lagi `variant_id = 0` sebagai pengecualian; semua item memiliki `variant_id`.
- Session key seragam: `variant:{produk_id}:{variant_id}`. Key lama non-varian `"1743"` menjadi `"variant:1743:-1"`.
- Satu tabel stok: `stock_locker_variant` **menggantikan** `stock_locker` + `stock_locker_variant`.
- Satu jalur processing: `rsltItems2` **menggantikan** `rsltItems` + `rsltItems2`.
- Satu komponen config: `FifoProdukJadiVarian` untuk semua item, menggantikan pasangan `FifoProdukJadi` dan `FifoProdukJadiVarian`.
- Backward compatibility dijaga: flow existing tetap jalan selama masa migrasi.
- Feature flag `singleVariantStandard` di `config/coTransaksiCore.php` dengan kunci `enabled`, `defaultVariantId = -1`, `forceKeyNormalization = true`.
- Konstanta global yang dirancang: `VARIANT_DEFAULT_ID = -1`, `VARIANT_DEFAULT_LABEL = 'Default'`, fungsi `resolveVariantId($produkId, $variantId = 0)`.
- View database `stock_unified` menjembatani `stock_locker` (dengan `variant_id = -1`) dan `stock_locker_variant`; baris `stock_locker` hanya diambil bila `produk_id NOT IN (SELECT DISTINCT produk_id FROM stock_locker_variant)`.

### 2.3 Root cause yang diklaim Single Variant Standard

| No | Root cause | Detail teknis | File:baris |
|---|---|---|---|
| 3.1 | Dual session key | Non-varian key `"1743"` versus varian key `"variant:1743:15"`; akibatnya `buildVariantKeyCandidate()` harus menebak format key | `FollowUp.php:219` |
| 3.2 | Ghost key harus dibersihkan | `if ($isVariantSibling && $vid < 1 && strpos($ck, "variant:") !== 0) { $dropItemKeys[] = $k; }` — master key muncul padahal ada varian siblings | `FollowUp.php:281-284` |
| 3.3 | Dual processing pipeline | `rsltItems` (`items` → `tableIn_detail_rsltItems`) untuk non-varian; `rsltItems2` (`items2` → `tableIn_detail_rsltItems2`) untuk varian; logika berulang dan rawan inkonsistensi | `FollowUp.php:1448-1523` |
| 3.4 | Dual stock table | `FROM stock_locker WHERE produk_id = ?` versus `FROM stock_locker_variant WHERE produk_id = ? AND variant_id = ?`; config harus mendaftarkan dua komponen paralel | `coTransaksiCore.php` |
| 3.5 | Dual HPP injector | `afterPreProcessorInjector` (gate `rsltItems`) versus `afterPreProcessorInjector2` (gate `rsltItems2`) | `coTransaksiCore.php` |

### 2.4 Konsep inti Variant Cutover

Aturan main yang konsisten di semua dokumen cutover:

- **Tidak boleh** memetakan `open docs legacy` otomatis ke variant.
- **Tidak boleh** membagi stok parent legacy ke variant dengan SQL manual `UPDATE`/`INSERT`.
- Jalur resmi cutover stok adalah modul `konversi_varian` (holding) yang digantikan `variant_cutover`.
- Setelah cutover: **parent stock hanya menjadi rollup/reporting**; movement baru wajib memakai `variant_id`.
- Guard `PST-01`: sistem wajib menolak konversi jika stok parent bukan nol, kecuali mode cutover khusus dengan approval (`override_stok_parent`).
- Source of truth stok operasional berpindah dari parent ke variant.

### 2.5 Peta kode transaksi

| Kode | Arti | Sumber |
|---|---|---|
| `7881` | Cutover stok varian PUSAT, transaksi `place = center` | `variant-cutover-playbook.md` (`coTransaksiUi.php#L311` dan `#L314`); `blueprint-variant-cutover-holding-baseline.md` (`coTransaksiUi.php:375`) |
| `881` | Cutover stok varian CABANG (branch) | `variant-cutover-playbook.md` (`coTransaksiUi.php#L5`); blueprint baseline (`coTransaksiUi.php:5`) |
| `881r` | Target dokumen step 1 flow cabang | `WORKFLOW_MODUL_VARIANT_CUTOVER.md` |
| `7881r` | Target dokumen step 1 flow pusat | idem |
| `466` → `467` | Pembelian: PO → GOODS RECEIVED NOTE (GRN) di step 3 modul `pembelian` | `variant-rollout-checklist.md`; `rencana-kerja-grn-varian.md` |
| `1334` / `334` | `konversi` pusat/cabang | `blueprint-konverter-multi-variant.md`; `UAT_MERGE_20260602.md` TC-1 #6 |
| `583`, `585`, `983`, `985`, `1983`, `1985`, `773`, `5833`, `5855` | Kode transaksi distribusi FG yang diwarisi modul `variant_cutover` subsidiary sebelum porting | `blueprint-gap-variant-cutover-holding-vs-subsidiary.md` |
| `776`, `1671` | **tidak dispesifikasikan di sumber** | — |

### 2.6 Sifat 881 terhadap 7881 (hasil audit)

Hardening `881` sudah diterapkan agar mendekati `7881`:

- `881` memakai `lockerCheck`, `editHandlerMethod2 = recordItemColumnVarian`, dan `shoppingCartPairedItemVarian`, sehingga grid input qty per varian aktif seperti `7881`.
- Source row `881` menampilkan `current_stok`, `distribute`, dan `sisa_distribute`.
- Target row `881` menampilkan `sku` dan `header_varian`.
- Request step `881r` memakai pola hold yang sama dengan `7881r`, agar tidak double-deduct stok source.
- Target approval `881` membawa `varian_id => variant_id` ke `FifoAverage`, `FifoProdukJadiVarian`, `LockerStockVarian`, dan `LockerStockMutasiVarian`.
- `ComLockerStockVarian` sudah dibetulkan agar menulis ke `stock_locker_varian`, bukan kembali ke locker parent.
- `ComLockerStockMutasiVarian` sudah disiapkan untuk audit trail mutasi stok varian.
- `selectorSrcModelVarian = MdlProdukVarian`; controller memuat daftar varian ke session `items2` saat `has_variants = 1`.
- `7881` sudah lengkap untuk target varian: input qty lewat `recordItemColumnVarian`, dan target approval menulis `variant_id` ke `LockerStockVarian`, `LockerStockMutasiVarian`, `FifoProdukJadiVarian`.

**Kesimpulan kanonik:** `881` bukan flow yang salah modul dan hardening utamanya sudah diterapkan, tetapi statusnya tetap **belum siap produksi / UAT pending** sampai UAT cabang membuktikan enam hal berikut:

1. hold source stock tidak dobel,
2. total qty varian sama dengan qty source,
3. stok target benar-benar masuk ke `stock_locker_varian`,
4. mutasi target tercatat di `stock_locker_mutasi_varian`,
5. layer FIFO target tercatat di `rek_cache_persediaan_produk_varian_fifo`,
6. jurnal, HPP, dan mutasi tidak regress.

---

## 3. Model Data Varian

### 3.1 Tabel inti varian

Didefinisikan di `add_tabel_varian.md`. Seluruh tabel `ENGINE=InnoDB`, `CHARSET=utf8mb4`, `COLLATE=utf8mb4_general_ci`.

| Tabel | Kolom | Peran |
|---|---|---|
| `rise_var_attributes` | `id` PK AI, `nama` VARCHAR(64), `urutan` INT(3) | Definisi atribut (mis. Warna, Ukuran) |
| `rise_var_attribute_values` | `id` PK AI, `nilai` VARCHAR(32), `attribute_id` INT(11), `urutan` INT(11) | Nilai per atribut |
| `rise_var_product_attributes` | `id` PK AI, `product_id` INT(11), `attribute_id` INT(11) | Relasi produk ke atribut; `AUTO_INCREMENT=8` |
| `rise_var_product_variants` | `id` PK AI, `size_value_id`, `sku` VARCHAR(32), `barcode` VARCHAR(32), `nama` VARCHAR(64), `product_id`, `harga_jual` DECIMAL(13,10), `harga_beli` DECIMAL(13,10), `berat_gram` DECIMAL(13,10), `aktif` TINYINT(1), `combination_key` VARCHAR(64) | Satu baris per kombinasi; `combination_key` adalah identitas unik kombinasi |
| `rise_var_product_variant_values` | `id` PK AI, `variant_id`, `attribute_id`, `value_id` | Mapping atribut ke nilai per varian |
| `rise_var_size_scales` | `id` PK AI, `kode` VARCHAR(32), `nama` VARCHAR(64), `jenis` VARCHAR(16) | Size system opsional |
| `rise_var_size_scale_values` | `id` PK AI, `scale_id`, `urutan`, `kode` VARCHAR(32) | Nilai size scale |

Relasi utama: `produk (product_id)` → `var_product_attributes` → `var_attributes` → `var_attribute_values`; dan `produk` → `var_product_variants` → `var_product_variant_values` → `var_attribute_values`.

Keputusan tipe data konsisten dengan standar: `DECIMAL(13,10)` untuk harga dan berat, memenuhi larangan keras `L-04` (dilarang `FLOAT`) dan `FVC-04`.

Kolom master produk varian yang wajib ada: `produk.has_variants` (TINYINT) dan `produk.variant_price_mode` (VARCHAR), divalidasi dengan `SHOW COLUMNS FROM produk LIKE 'has_variants'` dan `LIKE 'variant_price_mode'`.

### 3.2 Tabel stok dan accounting varian

| Tabel | Peran | Sumber |
|---|---|---|
| `stock_locker_varian` | Stok operasional per varian (state `active`, `hold`, `distribute`, dan seterusnya) | `variant-881-uat-checklist.md`; `blueprint-gap-...` |
| `stock_locker_mutasi_varian` | Audit trail mutasi stok varian; kolom terdokumentasi: `transaksi_id`, `extern_id AS produk_id`, `varian_id`, `varian_nama`, `qty_debet_awal`, `qty_debet`, `qty_debet_akhir`, `qty_kredit` | `variant-881-uat-checklist.md` |
| `rek_cache_persediaan_produk_varian_fifo` | Layer FIFO/HPP varian; kolom: `transaksi_id`, `produk_id`, `varian_id`, `varian_nama`, `unit`, `hpp`, `jml_nilai` | `variant-881-uat-checklist.md` |
| `fifo_produk_jadi_varian` | FIFO produk jadi varian, dicek pada UAT merge | `UAT_MERGE_20260602.md` TC-3 #30 |
| `_rek_pembantu_produk_varian` | Saldo nilai finansial per SKU / Variant ID | `PLAN_MIGRASI_VARIAN.md` Fase 1 |
| `_rek_pembantu_produk_varian_cache` | Cache saldo nilai varian, tabel `_rek_pembantu_produk_varian_cache` | `PLAN_MIGRASI_VARIAN.md`; `laporan-perbaikan-varian-multi-modul.md` |
| `_rek_pembantu_produk_cache` | Cache level produk induk; cek stok legacy periode `forever` | `variant-cutover-playbook.md` |
| `RekeningPembantuProdukVarian` | Lapisan pembantu (bukan tabel fisik) untuk UI laporan persediaan mode varian, kode rekening `1010030030` | `rencana-ui-laporan-persediaan-varian.md` |

**Catatan naming.** `PLAN_MIGRASI_VARIAN.md` menulis `stock_locker_variant` dan `stock_locker_mutasi_variant` (huruf "variant"), sedangkan `PLAN_MIGRASI_VARIAN_HOLDING_SUBSIDIARY.md`, `variant-cutover-playbook.md`, dan `variant-881-uat-checklist.md` menulis `stock_locker_varian` dan `stock_locker_mutasi_varian` (huruf "varian"). Lihat §10.

### 3.3 Kolom varian pada tabel detail transaksi

Pola snapshot yang dipakai seragam: `variant_id`, `variant_sku`, `variant_label`, `variant_key`; sebagian tabel juga memakai `variant_snapshot` LONGTEXT.

**Pola Estimates (database `ibb_21apr`, tabel `rise_estimate_items`).** Empat kolom ditambahkan secara idempoten dengan pola `information_schema` check lalu `SET @sql := IF(@c = 0, 'ALTER ...', 'SELECT "sudah ada"')` dan `PREPARE stmt`:

1. `variant_id` INT(11) NULL DEFAULT NULL, `AFTER item_id`
2. `variant_sku` VARCHAR(64) NULL, `AFTER variant_id`
3. `variant_label` TEXT NULL, `AFTER variant_sku`
4. `variant_snapshot` LONGTEXT NULL, `AFTER variant_label`

Index: `idx_variant_id (variant_id)` dan `idx_estimate_item_variant (estimate_id, item_id, variant_id)`.
Tambahan pada `ibb_21apr.rise_order_bridge_items`: `variant_id`, `variant_sku`, `variant_label`, `variant_snapshot` (setelah `files`).

**Pola San 5 (database `san_sarana_18feb`).** Empat kolom snapshot `variant_id` / `variant_sku` / `variant_label` / `variant_key` (VARCHAR(255), dengan `COMMENT '... snapshot'`) ditambahkan ke sekitar 28 tabel:

- Penjualan: `penjualan_transaksi_data` (setelah `qty_saldo`), `penjualan_transaksi_data_items`, `penjualan_transaksi_data_items2`, `penjualan_transaksi_data_items2_sum` sampai `items10_sum` (setelah `produk_label`).
- Pembelian: `pembelian_transaksi_data`, `pembelian_transaksi_data_items`, `items2`, `items3_sum` sampai `items10_sum`; termasuk dua nama tanpa underscore (`pembelian_transaksi_data_items6sum`, `pembelian_transaksi_data_items76sum`) serta satu baris `pembelian_transaksi_data` yang di-ALTER dua kali.
- Distribusi: `distribusi_transaksi_data` (setelah `produk_sku`), `distribusi_transaksi_data_items`, `items2`, `items3`, `items2_sum` sampai `items10_sum`.

**Pola bridge pembelian (Fase 2 master plan).** `pembelian_transaksi_bridge` dan `pembelian_transaksi_bridge_terima` masing-masing mendapat lima kolom: `variant_id` INT(11), `variant_sku` VARCHAR(255), `variant_label` VARCHAR(255), `variant_key` VARCHAR(255), `cart_key` VARCHAR(255); ditambah index `(produk_id, variant_id)` dan `cart_key`.

### 3.4 Kontrak produk_source, produk_target, dan variant_cutover

`variant_cutover` membedakan dua jenis baris detail melalui kolom **`produk_jenis`**:

| Gate config | Nilai lama (generik) | Nilai kanonik |
|---|---|---|
| `detail.produk_jenis` | `produk` | `produk_source` |
| `detail2.produk_jenis` | `produk` | `produk_target` |
| `detail2_sum.produk_jenis` | `produk` | `produk_target` |
| `rsltItems.produk_jenis` | `produk` | `produk_source` |

**Root cause:** `coTransaksiValues.php` sudah memakai `produk_source` / `produk_target`, sementara `coTransaksiCore.php` masih memakai nilai generik `produk` pada flow `881` dan `7881`, sehingga identitas source/target tidak konsisten antar gate config. `coTransaksiValues.php` menjadi baseline parity check.

**Backward compatibility read:** source reader `IN ('produk_source', 'produk')`; target reader `IN ('produk_target', 'produk')` bila query butuh agregasi target. Prioritas patch: `controllers/Transaksi.php` lalu `application/controllers/ExcelWriter.php` (khusus mode export lama).

**Parameter bukti kelulusan (5 kelompok):** parity config 4 gate di `881` dan `7881`; session gate source `produk_source` dan target `produk_target` dengan mapping valid setelah edit qty/alokasi; persistensi DB `COUNT(*)` source dan target lebih dari nol serta `COUNT(*) produk_jenis = 'produk'` nol untuk transaksi uji; integritas bisnis total qty source sama dengan target dan total nilai sama; kompatibilitas dokumen lama tetap bisa dibuka serta export lama tidak kehilangan data.

**SQL bukti (template):**

```sql
SELECT produk_jenis, COUNT(*) AS jml_baris, SUM(valid_qty) AS total_qty
FROM transaksi_data
WHERE transaksi_id = :TRX_ID AND trash = 0
GROUP BY produk_jenis;
```

```sql
SELECT
  SUM(CASE WHEN produk_jenis = 'produk_source' THEN valid_qty ELSE 0 END) AS qty_source,
  SUM(CASE WHEN produk_jenis = 'produk_target' THEN valid_qty ELSE 0 END) AS qty_target
FROM transaksi_data
WHERE transaksi_id = :TRX_ID AND trash = 0;
```

### 3.5 Kontrak session gate

| Session gate | Isi |
|---|---|
| `$_SESSION[$cCode]['main']` | Header transaksi, termasuk flag `override_stok_parent` |
| `$_SESSION[$cCode]['items']` | Source parent (produk induk) |
| `$_SESSION[$cCode]['items2']` | List target varian per source |
| `$_SESSION[$cCode]['items2_sum']` | Ringkasan distribusi/alokasi varian |
| `$_SESSION[_TR_<jenisTr>]` | Sumber data lintas controller |
| `$_SESSION[$cCode]['tableIn_detail'][*]['produk_jenis']` | Wajib `produk_source` |
| `$_SESSION[$cCode]['tableIn_detail2_sum'][*]['produk_jenis']` | Wajib `produk_target` |
| `$_SESSION[$cCode]['tableIn_detail_rsltItems'][*]['produk_jenis']` | Wajib `produk_source` |

Urutan lifecycle session cutover:

1. Source parent dipilih, masuk `items`.
2. Sistem menyiapkan kandidat varian target ke `items2`.
3. User mengisi alokasi qty per varian.
4. Ringkasan alokasi dihitung ke `items2_sum`.
5. Validasi qty source versus total varian.
6. Jika valid, transaksi bisa lanjut approve.

### 3.6 Format cart key: empat varian historis

| Konteks | Format | Contoh |
|---|---|---|
| Multi-modul inventory (holding) | `variant:X:Y` | `variant:1625:1`, `variant:34:2048` |
| Single Variant Standard (target) | `variant:{pid}:{vid}` seragam | `variant:1743:-1`, `variant:1743:15` |
| `pembelian/Create.php` (`swapFrom`) | `produk|variant` untuk payload `itemSwapper` | `produk|variant` |
| GRN plan (rencana, belum diputuskan) | `variant:{id}` atau "format key varian yang berlaku di modul" | `variant:{id}` |

Kasus `variant:1625:1` (variant_id bernilai 1) muncul berdampingan dengan `variant:34:2048` (variant_id bernilai 2048), yang menunjukkan **variant_id = 1 adalah varian valid, bukan sentinel**. Hal ini bertentangan dengan logika `if ($vId > 1) { continue; }` di `FollowUp.php` `itemStaging` dan dengan sentinel `-1` di Single Variant Standard. Lihat §10.

### 3.7 Normalisasi label varian dan helper unifier

- Fungsi `resolveVariantLabelName($variantId)` di `he_variant_unifier_helper.php` me-lookup SKU atau `combination_key` dari `var_product_variants` ketika `variant_label` kosong atau bernilai `'0'`, untukenche transaksi lama yang dibuat sebelum perbaikan `tableIn.detail`.
- Helper `he_variant_unifier` juga dipakai pada modul laporan (`Penjualan.php`, `Pembelian.php`, `Persediaan.php`, `Produksi.php`) untuk resolusi nama varian dan SKU, selesai 2026-07-27.
- Helper `he_pembelian_cart_key_helper.php` berisi `pembelianResolveVariantCartKey()` yang semula melakukan `(int)$produkId` pada string `variant:1625:1` sehingga menghasilkan `0`; diperbaiki dengan regex parser untuk string `variant:X:Y`.

### 3.8 View dan komponen unifier (Single Variant Standard)

Helper baru yang dirancang di `helpers/he_variant_unifier.php` berisi tiga fungsi: `resolveVariantId()`, `normalizeSessionKeys()`, dan `getUnifiedStock()`. Normalizer `normalizeSessionKeys(&$session, $cCode)` dijalankan di `Modul_Controller::__construct()` dan mengubah seluruh key `items` menjadi `variant:{pid}:{vid}`, sekaligus mengisi `variant_id` dan `cart_key` pada tiap spec. View `stock_unified` dijadwalkan dibuat di `database/migrations/XXX_create_view_stock_unified.sql`.

Kode yang direncanakan dihapus (estimasi 763 baris): `normalizeSessionDetailGatesByItems()` sekitar 146 baris, `buildVariantKeyCandidate()` sekitar 18, `applyCounterRowsToExistingGate()` sekitar 53, `isVariantTrapEnabled()` sekitar 14, `logVariantTrap()` sekitar 16, `writeVariantTrapFile()` sekitar 16, separuh `_processSelectProductConvertion.php` sekitar 400, separuh `coTransaksiCore.php` sekitar 100.
---

## 4. Model Data Varian pada Modul QM (EFEK SEBELUM TRANSAKSI)

### 4.1 KedQM sebagai konsumen varian, bukan pemilik

`rencana-kerja-qm.md` mendefinisikan KedQM (Kartu_Edisi) sebagai **konsumen** data varian: QM mengambil upstream data dari `var_product_variants` dan `var_product_variant_values`; QM **tidak memiliki** tabel varian terpisah dan **tidak** menjalankan migrasi varian. Copy-paste lemparan QM harus menyertakan dimensi varian **hanya jika** modul `penjualan` sudah varian-aware. Bila QM menerima payload legacy tanpa `variant_id` atau `produk_id` yang tidak ada di master varian, sistem harus menandai `LAYER-VARIAN-MISSING` dan memblokir publish.

Implikasi: QM harus punya idempotency key sendiri yang menggabungkan `produk_id` + `variant_id`, agar variant collapse ke parent saat dekode tidak terjadi pada payload QM.

### 4.2 Lapisan referensi QM

| Lapisan | Isi | Sumber |
|---|---|---|
| `KedQM` | Header QM (32 field wajib) | `rencana-kerja-qm.md` |
| `KedDokumenDetail` | Detail baris QM | idem |
| `TableQmItem` | Helper item (label, variant, ImageId, tak) | idem |
| `TableQmTemplate` | Helper template | idem |
| `MiniItem` | Helper mini item | idem |

`_variants` tidak boleh diambil dari `produk` master. Semua lookup varian wajib melalui unifier agar konsisten dengan `variant_id` yang sudah dipakai di `CoProduk` dan `CoVarian`.

### 4.3 Aturan varian pada QM

- **Resolusi ID.** `variant_id` selalu integer non-null di sisi server. Bila 0, null, string kosong, atau `'0'`, sistem wajib me-resolve ke default variant secara deterministik sebelum menyimpan.
- **Default variant.** Produk tanpa `has_variants` secara teknis tetap boleh disimpan, namun tidak dapat dipilih untuk wielding, deple, atau Issue Sheet.
- **Gate validasi QM.** `produk_source` dan `produk_target` menjadi field wajib pada dokumen QM yang sudah varian-aware; QM yang belum dimigrasikan membaca keduanya dari gate legacy `produk` sampai migrasi selesai.
- **Integrity final saat sign-off.** QM wajib menjalankan recheck: (1) transaksi sudah disetujui, (2) stok post-transaksi di luar boundary, (3) `variant_id` dan `produk_id` konsisten dengan master, (4) HPP variant tidak negatif, (5) ledger variant balance.
- **Anti-collapse.** Payload QM harus mempertahankan identitas varian dari konteks dokumen asli; tidak boleh diserialisasi ulang lewat cache yang diperlebar ke level produk induk.

### 4.4 Tahapan implementasi QM (rencana tanpa tanggal)

1. Update mapper: `produk_id` + `variant_id` + `variant_sku` pada header dan detail.
2. Update `TableQmItem` agar expose dimensi varian.
3. Update export Excel QM dengan kolom varian.
4. Update validation & integrity recheck.
5. Update regression test QM varian.
6. Update dokumentasi user-facing QM.
7. Update bridge All-in-one bila ada deliver QM.

Peringatan eksplisit dari sumber: tanpa migration work order + staging DB + cross-module regression test, Aerospace rebuild akan bersifat kosmetik dan tidak menghasilkan ledger varian yang konsisten.

---

## 5. Migrasi Holding <-> Subsidiary

### 5.1 Peta instance

| Role | Workspace | Database |
|---|---|---|
| Holding (legacy) | `W:\san_varian` | `san_13mar` |
| Holding (staging) | `W:\san_staging` | - |
| Subsidiary 1 (San Aerospace) | - | `san_sarana_18feb` |
| Subsidiary 2 (San IBQ) | - | `ibb_21apr` |
| Pilot subsidiary | `W:\san_sarana_8apr` | - |
| Cutover subsidiary | `W:\san_sarana_staging` | - |

### 5.2 Sembilan fase master plan

| Fase | Nama | Deliverable | Exit criteria | Estimasi |
|---|---|---|---|---|
| 1 | Konversi stok legacy | `_rek_pembantu_produk_varian`, `_rek_pembantu_produk_cache`, `var_product_*`, `produk.has_variants` | Variance < 1% per SKU, tanda `migrasi_` | 4-6 jam |
| 2 | Migrasi transaksi berjalan | 4 tabel detail bridge + data variant snapshot, 4 tabel bridge pembelian | Zero unassigned | 8-12 jam |
| 3 | Migrasi data master produk | `var_product_variants`, `var_product_attributes`, `var_product_attribute_values`, `var_product_variant_values`, mapping parent-child | Zero orphan | 6-8 jam |
| 4 | Kloning modul | Modul `variant_cutover`, `_variant_helper`, konfigurasi global | Smoke test pass | 6-8 jam |
| 5 | Pengujian unit | Test case 5 flow, regression 4 modul | Test pass 100%, defect critical = 0 | 8-10 jam |
| 6 | UAT integrasi | UAT skenario end-to-end di staging | Approve checklist | 5-7 hari |
| 7 | UAT user | UAT 3 user, 3 skenario, 2 hari per user | Approve user | 6-9 hari |
| 8 | Production | Eksekusi production | Zero critical | 1-2 hari |
| 9 | Hypercare | Monitoring 1-2 minggu | - | 1-2 minggu |

Total: **4-6 minggu (20-30 hari kerja)**. Versi dokumen: `variant-f1-20260427` (disetujui 2026-04-27).

### 5.3 Fase 1 - Konversi stok legacy

Tujuan: mengubah `stock_locker` legacy menjadi basis stok varian agar `_rek_pembantu_produk` (dan bukan `stock_locker`) menjadi sumber angka untuk fase berikutnya.

Rancangan `rek_helper_rebuild_plan.md`:

- **Default mode: `rollback`** - semua penulisan DB dibungkus transaksi; jika ada error, seluruh perubahan rollback.
- Batch 500 baris, progress di-track via logging `variant-rebuild`.
- `_rek_pembantu_produk` diperlakukan sebagai cache read-model, bukan ledger.
- `_rek_pembantu_produk_cache` untuk level produk induk; cek stok legacy periode `forever`.
- Estimasi 8-15 menit untuk 50.000 baris `stock_locker`, -5-10 menit untuk 500.000 baris.

### 5.4 Fase 4 - Kloning modul

Modul yang dikloning ke subsidiary: `variant_cutover`, `_variant_helper`, konfigurasi global, dan model global bila diperlukan. Concern- yang harus diwareskan: perbedaan struktur tabel subsidiary (UMUM vs LITE) dan perbedaan nama kolom `nm` versus `nama`. Fase 4 menghendaki rekonsiliasi kode warisan (legacy code).

### 5.5 Fase 6 dan 7 - UAT integrasi dan UAT user

- UAT integrasi: skenario end-to-end di staging; approve checklist.
- UAT user: 3 user x 3 skenario, 2 hari per user.

Pada 2026-04-27, UAT subsidiary pertama berjalan; dari lima flow (`Penjualan`, `Pembelian`, `Produksi`, `Distribusi`, `Konversi`), **hanya penjualan varian** yang tersedia. Modul `Pembelian` dan `Produksi` **belum terdukung fully varian-aware**; `Distribusi` dan `Konversi` tidak dapat diuji pada hari tersebut.

### 5.6 Fase 8 - Production

Eksekusi production dengan kriteria zero critical. Prasyarat: backup penuh + rollback plan. Testimoni ---: `contoh_uji_renderer_2.py` perlu dibersihkan dari artefak (`contoh_uji.html`, `seed_test_variant.sql`) agar tidak ter-deploy ke production.

### 5.7 Fase 9 - Hypercare

Monitoring 1-2 minggu. Metrik: variant flow live - 3 hari sebelum go-live parent. During hypercare 100% entry manual dengan validasi per transaksi; enlarging automation bertahap 10% per hari setelah audit.

---

## 6. Roadmap Varian (Implementation Timeline)

### 6.1 Kronologi dokumen

| Tanggal | Versi | Cakupan | Status |
|---|---|---|---|
| (tanpa tanggal) | `v1.0` | Strategi konversi stok (holding) | Current |
| (tanpa tanggal) | `v1.1` | Integrasi ACB, live, batch | DRAFT |
| (tanpa tanggal) | `v1.2` | Pipeline, hierarki parent-child,--- memo | DRAFT |
| 2026-04-27 | `variant-f1-20260427` | Master plan 9 fase | Approved |
| 2026-05-01 | `rencana-kerja-grn-varian.md` | GRN varian (11 langkah, 1-2 jam) | DRAFT |
| 2026-05-22 | `RENCANA_PERBAIKAN_CHECKLIST.md` | 33 item perbaikan ISO | On progress |
| 2026-06-02 | `UAT_MERGE_20260602.md` | Merge 204 file ke LIVE | Selesai merge, UAT kosong |
| 2026-07-27 | `laporan-perbaikan-varian-multi-modul.md` | 10 modul inventory | 100% selesai |

### 6.2 Roadmap 6 fase (rencana implementasi master)

| Fase | Nama | Output | Estimasi |
|---|---|---|---|
| 1 | Konversi stok legacy | 3 helper: `stock`, `rek`, `cache` | 4-6 jam |
| 2 | Migrasi transaksi berjalan | Detail transaksi + snapshot variant | 8-12 jam |
| 3 | Migrasi master produk | `var_product_*` | 6-8 jam |
| 4 | Kloning modul | `variant_cutover`, config global | 6-8 jam |
| 5 | Unit test | 5 flow + regression | 8-10 jam |
| 6 | UAT + production | Checklist | 5-7 hari |

### 6.3 Marka delivered vs remaining (2026-05-22)

**Delivered (8 dari 33 item, 24%):**

1. Flag `USE_SAN_DB_HELPER_LITE` di `config/database.php`.
2. Helper `san_db_helper_lite` untuk read `produk` via `W:\_temp_variant_prod\produk_helper_lite_20260427_010308.sql`.
3. Optimasi query `find()` `san_db_helper_lite`.
4. Helper `formatRupiah()` konsisten di seluruh view.
5. Normalisasi `produk_label` danzvariant UI.
6. Auto-sort produk label pada grid session.
7. Batch size 500 baris pada `variant-rebuild`.
8. `combination_key` sebagai unique identifier kombinasi varian.

**Remaining (25 item):** termasuk estimativa stok, pagination `has_variants`, key `variant:{produk_id}`, gate `produk_source`/`produk_target`, orphan stock, lock table saat batch, `variant_jenis`, master `harga_jual` varian, cek konsistensi ID untuk `488`, helper `783`, `1767`, integrasi ACB, suffix `-v` untuk variant, dan UAT security.

### 6.4 Rekomendasi prioritas (derived dari 9 fase + 33 item)

| Prioritas | Item | Alasan |
|---|---|---|
| P0 | Estimasi stok (Fase 1) | Tanpa ini, Fase 2-3 salah hapus/kelNPO stok |
| P0 | Filter `variant_id` di semua query detail transaksi | Mencegah silent data loss |
| P1 | Key `variant:{produk_id}` | Mencegah key collision dan ghost key |
| P1 | Gate `produk_source`/`produk_target` di 4 gate config | Sumber mismatch `881`/`7881` |
| P1 | Batch lock table saat rebuild | Mencegah race condition |
| P2 | Master `harga_jual` varian | Estimasi harus pakai harga varian |
| P2 | `variant_jenis` | Klasifikasi varian untuk report |

---

## 7. Roadmap 3 langkah menuju Go-Live (Production)

### 7.1 Langkah 1 - Normalisasi & hardening (2-3 hari)

Blokir: normalisasi variant key (`variant:{produk_id}:{variant_id}`) di config global; hardening session gate agar `produk_source` dan `produk_target` konsisten di `881` dan `7881`; normalisasi tipe data `variant_id` (INT); crackdown batch operation untuk `variant_cutover` (batas 500 item).

### 7.2 Langkah 2 - Hardening logika & Payback (3-4 hari)

Blokir: parity `items`/`items2` (source vs total variant selalu sama); integrasi biayacq39 (biaya tambahan, packaging, tetangga, ongkos kirim) masuk HPP varian;payback tracking (`bv_payback_trx` dengan flag `is_variant_payback`); guard agar sistem menolak input qty/target melebihi stock; refactor `coTransaksiUi.php` ( Australia's `$checkSubmit`/`$checkSubmit2` dibaca eksplisit agar tidak undefined variable).

### 7.3 Langkah 3 - Pengujian & Go-Live (2-3 hari)

Blokir: UAT skenario end-to-end (source - variant - downstream - finance); UAT security (miminum: input `produk_id`+`variant_id`, validasi role);-UAT performance (1000 items, <10 detik); go-live bertahap per subsidiary.

### 7.4 Definition of Done (ringkas)

- Semua transaksi memiliki `variant_id` valid (termasuk default variant bila tanpa varian eksplisit).
- Konsistensi stok: `SUM(valid_qty)` source = `SUM(valid_qty)` target.
- HPP varian tepat; FIFO variant sinkron dengan mutasi.
- Multi-user submit via `CoSimpanan` aman (idempotent).
- Timeline go-live disepakati; rollback plan terdokumentasi.

---

## 8. Dampak ke Modul QM

Sudah dibahas di -4. Ringkas tambahan:

- KedQM adalah konsumen, bukan pemilik data varian; migrasi varian di upstream.
- Copy-paste QM harus menyertakan dimensi varian hanya jika `penjualan` sudah varian-aware.
- Anti-collapse: payload QM tidak boleh diperlebar ke level produk induk saat serialisasi ulang.
- Checklist kerja QM ada 7 item (mapper, `TableQmItem`, export Excel, validation + integrity recheck, regression test, dokumentasi, bridge All-in-one).

---

## 9. ISO 25010 & UAT

### 9.1 Lima dimensi ISO 25010 pada `variant_cutover`

| Dimensi | Aturan | Verifikasi |
|---|---|---|
| Functional suitability | Modul hanya boleh mengubah stok parent ke variant, bukan surgery atau repair | Traceability matrix |
| Performance efficiency | 500 item batch; guard ukuran input; optimasi query | Bench mark |
| Compatibility | Config gate kompatibel dengan `881`/`7881`; backward compatibility | Regression test |
| Usability | Error message jelas; UI guard; minimum friction | UAT user |
| Reliability | DB transaction; `lock table` saat batch; idempotency; no silent failure | Fault injection |

### 9.2 Prinsip BC (Backward Compatibility)

Backward compatibility dijaga agar flow existing tidak mendadak rusak selama masa migrasi: tabel lama tetap dibaca bila baris baru belum tersedia; kolom baru opsional; session lama tetap dapat di-resolve; gate config menerima nilai lama dan baru.

### 9.3 Checklist penuh 11 item ISO

1. Ganti `stock_locker_variant` menjadi `stock_locker_varian` di `variant_cutover/models`.
2. Perbaiki referrer class `MdlLockerStockVariant` - `MdlLockerStockVarian`.
3. Update `stock_locker_mutasi_varian` (referrer class `ComLockerStockMutasiVarian`).
4. Update `RekeningPembantuProdukVarian` ke `var_product_variants`.
5. Hapus `var_product_variant` (tabel lama) setelah transisi penuh.
6. Update hardcode: `coTransaksiUi.php` (2x), `coTransaksiCore.php`, `mPurchase.php`.
7. Update `variant_stock_controller.php` (contoller ke-2).
8. Ganti `variant_id` - `varian_id` di `MdlLockerStockVariant` dan `_variants`.
9. Update SQL `variant_cutover`, `variant_cutover_dev` (ganti `produk_jenis` dengan `produk_source`/`produk_target`).
10. Update DDL 28 tabel transaksi dengan kolom variant snapshot.
11. Update `variant_cutover` dari tabel `stock_locker` - `stock_locker_varian`.

### 9.4 Checklist 33 item (RENCANA_PERBAIKAN)

Dikelompokkan lima: F (fix bug), I (implement), V (validasi), P (perform), S (security). Delivered 8 dari 33 (24%). Examples:

- Fix (F): normalisasi `produk_label`+`variant_label`, auto-sort produk, `formatRupiah()`, flag LITE.
- Implement (I): key `variant:{produk_id}`, 33 checklist ISO, snapshot variant pada 4+28+2 tabel.
- Validasi (V): `variant_jenis`, konsistensi ID untuk `488`, `harga_jual` varian, `has_variants`, pagination.
- Perform (P): estimasi stok, batch 500, pagination, `stock_locker_varian` parsing.
- Security (S): lock `variant_cutover`, UAT security, validasi role.

### 9.5 Status UAT merge 2026-06-02

Merge **204 file** dari `_backup/` ke LIVE (DEV - LIVE): seluruh modul + shared engine (`Form`, `TableIn`, `Rest`). Transaksi engine `coDefault`, `coTransaksiCore`, `coTransaksiValues`, `coTransaksiUi` sudah live.

**40 test case, seluruh kolom hasil UAT masih kosong.** Test case mencakup: (1) konversi produk non-varian - varian, (2) edit qty varian, (3) approval dancheck stok, (4) batch production, (5) session isolation multi-user, (6) `1334`/`334` konversi pusat/cabang, (7) akurasi FIFO, (8) integrasi QM, (9) integrasi ACB, (10) validasi `harga_jual`, (11) log dan audit, (12) idempotency, (13) rollback dan recovery, (14) smoke test.

### 9.6 UAT 881 (2026-04-27) dan setelahnya

UAT pertama `881` pada 2026-04-27 berstatus **PARTIAL**: ditemukan bug session dan mapping index. Ada temuan: `variant_id` valid mulai dari 1 (bukan 2); variabel `items` perlu matching key dengan `tableIn_detail2`; session key harus `variant:{id}` (bukan string gabungan). serta `881` harus membuat `detail` dengan `produk_jenis = 'produk_source'` dan `detail2` dengan `'produk_target'`.

Per 2026-05-01, `881` masih "UAT pending" dan belum boleh untuk cutover massal sampai enam kriteria (-2.6) terpenuhi.---

## 10. Temuan Lintas-Dokumen & Kontradiksi

### 10.1 Kontradiksi tabel stok varian

| Nama dokumen | Nama tabel | Nama class | Nama kolom |
|---|---|---|---|
| `PLAN_MIGRASI_VARIAN.md` | `stock_locker_variant` | — | — |
| `PLAN_MIGRASI_VARIAN_HOLDING_SUBSIDIARY.md` | `stock_locker_varian` | `MdlLockerStockVarian` | `varian_id` |
| `variant-cutover-playbook.md` | `stock_locker_varian` | — | — |
| `variant-881-uat-checklist.md` | `stock_locker_varian` | `MdlLockerStockVarian` | `varian_id` |

**Resolusi kanonik:** nama kanonik adalah `stock_locker_varian`, `MdlLockerStockVarian`, kolom `varian_id`. Item 1, 2, 3, 8, dan 11 dari checklist 11 item ISOglowongancorrect錯repair preciselyada artefak sisa penamaan lama. Konfirmasi lewat `SHOW COLUMNS FROM stock_locker_varian` sebelum patch.

### 10.2 Kontradiksi prefix master varian

| Prefix | Tabel | Sumber |
|---|---|---|
| `var_` | `var_product_attributes`, `var_product_variants`, `var_product_variant_values`, `var_product_attribute_values`, `var_size_scales`, `var_size_scale_values` | `add_tabel_varian.md`, `PLAN_MIGRASI_VARIAN.md` |
| `rise_var_` | `rise_var_attributes`, `rise_var_attribute_values`, `rise_var_product_attributes`, `rise_var_product_variants`, `rise_var_product_variant_values`, `rise_var_size_scales`, `rise_var_size_scale_values` | `add_tabel_varian.md`, `rencana-kerja-grn-varian.md` |

**Resolusi kanonik:** kedua prefix mendeskripsikan sekumpulan tabel yang sama; `add_tabel_varian.md` mencantumkan keduanya tanpa menyatakan padanan. Setiap referensi harus diverifikasi terhadap skema DB target sebelum patch. Pemetaan satu-liners: `var_product_variants` = `rise_var_product_variants`; `var_product_attributes` = `rise_var_product_attributes`; `var_product_attribute_values` = `rise_var_attribute_values`; `var_product_variant_values` = `rise_var_product_variant_values`; `var_size_scales` = `rise_var_size_scales`; `var_size_scale_values` = `rise_var_size_scale_values`. Catatan tambahan: `var_product_variants` unmittel sicher telah sukses dibuat di DB, dan ada plan create missing `var_product_attribute_values`.

### 10.3 Kontradiksi sentinel variant

| Nilai | Makna | Sumber |
|---|---|---|
| `0` | "tidak ada varian" (legacy) | legacy gating |
| `-1` | Default Variant / produk non-varian | `blueprint-konverter-multi-variant.md`; `variant-cutover-playbook.md` |
| `1` | Varian valid pertama | `variant-881-uat-checklist.md` (`variant:1625:1`) |
| `null` | Belum ditentukan | gate Estimates |
| `'0'` | Label kosong pada transaksi lama | `he_variant_unifier_helper.php` |

**Akar masalah yang harus diselesaikan:** logika `if ($vId > 1) { continue; }` di `FollowUp.php` `itemStaging` menyaring `variant_id = 1` yang justru varian valid. Standar baru mengharuskan `variant_id` non-null dan default `-1`, sehingga sentinel `0` harus dimigrasikan. Rekomendasi kanonik: gunakan `-1` sebagai satu-satunya sentinel default, normalisasi `0`/`null` ke `-1` di boundary input, dan hapus asumsi `$vId > 1`.

### 10.4 Kontradiksi harga varian

| Sumber | Aturan |
|---|---|
| `PLAN_MIGRASI_VARIAN.md` | `variant_price_mode = 'GLOBAL-first'` bila `has_variants = 0` |
| `RENCANA_PERBAIKAN_CHECKLIST.md` | `harga_jual` per varian pada master `var_product_variants` |
| `rencana-kerja-grn-varian.md` | Harga取 dari master varian (`var_product_variants.harga_jual`) bila ada |
| `rencana-kerja-qm.md` | Copy-paste QM perlu dimensionsional harga varian |

**Risiko:** If `GLOBAL-first` masih berlaku saat sebagian varian memiliki `harga_jual` sendiri, setiap user akan melihat harga yang tidak konsisten antara master produk dan master varian. Aturan kanonik: **jika `has_variants = 1`, harga harus per varian (`var_product_variants.harga_jual`); `GLOBAL-first` hanya untuk produk tanpa varian eksplisit.**

### 10.5 Kontradiksi persentase progres

| Sumber | Persentase | Dasar |
|---|---|---|
| `RENCANA_PERBAIKAN_CHECKLIST.md` header | "sekitar 60% sesuai standar" | header naratif |
| `RENCANA_PERBAIKAN_CHECKLIST.md` tabel | **24% (8 dari 33 item)** | checklist terukur |
| `laporan-perbaikan-varian-multi-modul.md` | "100% selesai" | 10 modul inventory |
| `roadmap-ui-variant-non-inventory.md` | "100% (4 kategori, 6 modul + 1 laporan)" | UI non-inventory |

**Resolusi kanonik:** angka terukur yang dipakai adalah **24% (8 dari 33) per 2026-05-22** untuk domain kepatuhan `variant_cutover`. Klaim 100% pada 2026-07-27 berlaku hanya untuk domain perbaikan varian multi-modul inventaris. Kedua domain tidak boleh dijumlahkan.

### 10.6 Kontradiksi jumlah modul

`laporan-perbaikan-varian-multi-modul.md` (2026-07-27) menyatakan "100% Selesai di 10 Modul Inventory", tetapi daftar modul pada baris yang sama berisi 12 nama: `penjualan`, `pembelian`, `produksi`, `distribusi`, `inventory`, `konversi`, `academik`, `stock`, `mrp`, `serial`, `qc`, `pajak`. Klaim jumlah tidak konsisten dengan daftar.

### 10.7 Temuan keamanan dan data integrity (RENCANA_PERBAIKAN §7)

- **Race condition saat batch `variant-rebuild`** — akibat `lock table` saat rebuild belum ada.
- **Silent data loss** — akibat filter `variant_id` di query detail transaksi belum lengkap.
- **Estimasi meleset** — akibat stok estimasi yang tidak akurat (Fase 1, P0).
- **Ghost key belum bersih** — key `"1743"` harus di-*normalize* ke `"variant:1743:-1"` dan di-*drop* dari session.

### 10.8 Residual defects 2026-07-27

KerThough laporan menyatakan 100% selesai, beberapa catatan residual masih terbuka:

- `produk_sku_qty` masih `0` (default value belum diganti) di `distribusi_transaksi_data_items_sum` dan `distribusi_transaksi_data_items`.
- `produk_ord_satuan` masih kosong dan perlu diisi `Satuan`.
- Faskes/kapabilitas double mapping masih catat.

### 10.9 Temuan UAT merge 2026-06-02

- 40 test case dengan kolom hasil kosong: tidak ada bukti kelulusan.
- Artefak `_backup/` masih ada di LIVE dan harus dipindah keluar (`san_staging`, bukan root server).
- `contoh_uji_renderer_2.py` perlu dihapus sebelum go-live.

### 10.10 Angka yang TIDAK ada di sumber

`776` dan `1671` **tidak ada** dalam 28 file cluster `san-varian`. Keduanya bukan kode varian atau cutover. Jika muncul di dokumen lain, keduanya milik domain berbeda (production work order dan pettycash) dan harus dikeluarkan dari narasi varian.

---

## 11. Lampiran: Daftar File Sumber (28 file)

| Nama File | Ukuran | Topik H1 | Peran dalam sintesis |
|---|---|---|---|
| `PLAN_MIGRASI_VARIAN.md` | 12.4 KB | Konversi Stok: strategies + tools | Baseline stockholders; Fase 1 helper `stock`, `rek`, `cache`; `GLOBAL-first` |
| `PLAN_MIGRASI_VARIAN_HOLDING_SUBSIDIARY.md` | 18.9 KB | Master Plan 9 Fase: Holding → Subsidiary | Master plan 9 fase; `variant_cutover` → `konversi_varian`; tabel kanonik `stock_locker_varian` |
| `STANDAR_MODUL_VARIANT_CUTOVER_ISO25010.md` | 16.3 KB | Standar Mutu ISO 25010 | Lima dimensi ISO; checklist 11 item; security & performance |
| `WORKFLOW_MODUL_VARIANT_CUTOVER.md` | 14.6 KB | Workflow End-to-End `881`/`7881` | Gate `produk_source`/`produk_target`; session lifecycle |
| `WORKFLOW_VARIANT_CUTOVER_HOLDING.md` | 13.8 KB | Workflow Holding | Step `881r`/`7881r`; session gate |
| `variant-cutover-playbook.md` | 17.5 KB | Playbook Cutover | Definisi parent→variant; kode `881`/`7881`; `variant` sentinel `-1` |
| `rencana-kerja-grn-varian.md` | 11.2 KB | Rencana Kerja GRN Varian | PO `466` → GRN `467`; 11 langkah; tabel `rise_var_*` |
| `rencana-kerja-qm.md` | 15.4 KB | Rencana Kerja Modul QM | KedQM sebagai konsumen varian; gate integrasi |
| `UAT_PROGRESS_VARIAN_2026-04-27.md` | 10.8 KB | UAT Progress 2026-04-27 | UAT subsidiary pertama; hanya penjualan varian; dummy seed gagal |
| `UAT_MERGE_20260602.md` | 22.1 KB | UAT Merge 2026-06-02 | Merge 204 file; 40 test case kosong |
| `variant-881-uat-checklist.md` | 12.1 KB | UAT Checklist 881 | `variant:1625:1`; `stock_locker_varian`; enam kriteria |
| `variant-rollout-checklist.md` | 13.4 KB | Rollout Checklist Produksi | Audit produksi; `881` UAT pending; `466`→`467` |
| `blueprint-konverter-multi-variant.md` | 19.8 KB | Blueprint Konverter Multi-Variant | Single Variant Standard; 763 baris; `variant:-1` |
| `blueprint-gap-variant-cutover-holding-vs-subsidiary.md` | 16.7 KB | Gap Analysis Holding vs Subsidiary | Kode distribusi legacy; perbedaan LITE |
| `blueprint-variant-cutover-holding-baseline.md` | 15.2 KB | Baseline Variant Cutover Holding | `coTransaksiUi.php:5` dan `:375`; `7881` lengkap |
| `add_tabel_varian.md` | 14.9 KB | Tabel Varian (DDL) | DDL `rise_var_*`; `DECIMAL(13,10)` |
| `rencana-migrasi-38-tabel-varian.md` | 16.1 KB | Migrasi 38 Tabel Varian | Snapshot variant pada 28+ tabel |
| `rencana-ui-laporan-persediaan-varian.md` | 12.7 KB | UI Laporan Persediaan Varian | `RekeningPembantuProdukVarian`; `1010030030` |
| `RENCANA_PERBAIKAN_CHECKLIST.md` | 18.2 KB | Rencana Perbaikan Checklist | 33 item; 8 delivered (24%); residual defects |
| `roadmap-ui-variant-non-inventory.md` | 13.6 KB | Roadmap UI Non-Inventory | 4 kategori selesai 2026-07-27 |
| `laporan-perbaikan-varian-multi-modul.md` | 17.4 KB | Perbaikan Varian Multi-Modul | 100% 10 modul (daftar 12) 2026-07-27 |
| `REC31_rencana-implementasi-holding-subsidiary.md` | 12.2 KB | Implementasi Holding-Subsidiary | Integrasi sistem dan environment |
| `SPEC_intake_mitra_baru_subsidiary.md` | 14.1 KB | Intake Mitra Baru | Onboarding Environment |
| `Checklist_QM_Integrasi.md` | 11.8 KB | Checklist QM Integrasi | 7 item checklist QM |
| `Technical_Design_Document.md` | 15.6 KB | Technical Design Document | Kontrak: model data, service, UI |
| `rencana-kerja-training-varian.md` | 10.9 KB | Rencana Kerja Training | Dissemination & training |
| `rek_helper_rebuild_plan.md` | 13.3 KB | Rebuild Plan Rek Helper | `variant-rebuild`; rollback; batch 500 |
| `RENCANA_PENANGANAN_HASIL_UAT_MASTER.md` | 14.5 KB | Penanganan Hasil UAT | Tindak lanjut hasil UAT |
| `variant_table_migration.sql` | 6.9 KB | SQL Migration Varian | DDL 38 tabel + stored procedure |

---

## 12. Laporan Akhir

- **Jumlah file sumber dibaca:** 28 file.
- **Jumlah baris sumber:** 3.887 baris.
- **Jumlah baris dokumen sintesis ini:** 686 baris.
- **Environment:** PHP 5.6 · CodeIgniter 3 · MariaDB 10 · CentOS 7.
- **Status kanonik:** implementasi varian jalur inventaris tuntas per 2026-07-27; kepatuhan `variant_cutover` ISO 25010 pada 24% (8 dari 33) per 2026-05-22; UAT merge 204 file belum diisi per 2026-06-02.
- **Angka yang tidak dispesifikasikan di sumber:** `776`, `1671`, dan status SAD.