# Variant Rollout Checklist

Dokumen acuan progres implementasi produk varian pada modul transaksi ERP.

## Status Saat Ini

- [x] Master varian setting dasar
- [x] Routing `variant-settings`
- [x] Kontrak data varian di transaksi
- [x] Policy harga `GLOBAL` / `VARIANT`
- [ ] Selector + shopping cart variant-aware
- [ ] Pembelian
- [ ] GRN pembelian (step 3 di modul `pembelian`)
- [ ] Penjualan
- [ ] Stock / locker / cache
- [ ] Modul turunan
- [ ] Report / hardening / UAT

## Fokus Eksekusi Saat Ini

- UAT `Fase 1` berjalan paralel.
- Hardening `Fase 2` tetap dilanjutkan pada jalur low-risk: identity varian di followup, reset locker, dan fondasi cache/calculation.
- UAT flow `881` tetap ditahan sampai UAT Fase 1 stabil.

## Update Analisa Produksi Rakitan (2026-05-01)

Scope analisa:
- Hanya `produk.jenis='item_rakitan'`.

Temuan data:
- Produk rakitan aktif: `83`.
- Produk rakitan dengan `has_variants=1`: `0`.
- Produk rakitan yang sudah punya row `var_product_variants`: `0`.
- Kandidat family kode rakitan (indikasi varian): `20 family / 44 produk`.

Temuan sample kritis:
- Sample family `ED175CK`:
  - `ED175CK-G` (`id=1019`) dan `ED175CK-R` (`id=736`) punya BOM bahan aktif yang berbeda.
  - `ED175CK-W` (`id=857`) saat ini tidak punya BOM bahan aktif (`produk_komposisi` aktif = `0`).
  - Komposisi biaya aktif (`produk_biaya`) ketiganya identik, tetapi ini tidak cukup untuk menyimpulkan BOM produksi sama.

Implikasi rollout:
- Aktivasi varian untuk rakitan tidak cukup hanya menyalakan master varian.
- Agar akurat untuk stok/HPP/audit produksi, dibutuhkan dukungan BOM per varian atau strategi hybrid (variant override + parent fallback).
- Sampai track BOM varian siap, rakitan tetap diperlakukan sebagai produk terpisah.

## Keputusan Rollout Saat Ini

- Fase 1 memakai `GLOBAL-first rollout`.
- Artinya harga beli dan jual untuk produk varian sementara tetap mengikuti harga produk utama.
- Mode harga `VARIANT` tetap disiapkan di master data, tetapi implementasinya ditunda ke fase berikutnya agar waktu pengerjaan dan risiko integrasi lebih kecil.

## Prinsip Harga

- `GLOBAL`
  - Harga beli dan jual mengikuti harga produk utama.
- `VARIANT`
  - Harga beli dan jual mengikuti harga pada varian.
- Rollout aktif saat ini:
  - Fase 1 hanya menargetkan `GLOBAL`.
  - `VARIANT` diparkir lebih dulu dan tidak menjadi blocker go-live fase 1.
- Jika mode `VARIANT` dipakai dan harga varian belum tersedia:
  - transaksi harus warning atau block
  - tidak fallback diam-diam ke harga global

## Kebijakan Transisi Legacy

- Aktivasi varian adalah `cutover operasional`, bukan sekadar ubah master data.
- Parent product yang sudah dipakai transaksi reguler harus diperlakukan sebagai item legacy sampai transisinya beres.

### Open Docs Legacy

- Best practice default:
  - `block aktivasi varian` jika masih ada dokumen operasional terbuka untuk parent product.
- Dokumen yang wajib dicek minimal:
  - `PRE-PO / PO / GRN` pembelian yang belum selesai
  - retur pembelian yang belum final
  - `SO / delivery / invoice / retur` penjualan yang belum selesai
  - movement stock yang belum final jika masih membaca `produk_id` parent
- Jika bisnis memaksa lanjut:
  - dokumen lama diselesaikan sebagai `legacy parent item`
  - transaksi baru setelah `cutover date` wajib memakai variant
  - sistem tidak boleh auto-menebak variant untuk dokumen lama

### Stock Legacy Sebelum Varian

- Best practice default:
  - `block aktivasi varian` jika parent product masih punya stok operasional yang belum dialokasikan.
- Alasannya:
  - stok lama parent tidak punya identitas variant
  - sistem tidak punya dasar audit untuk membagi stok lama ke variant tertentu secara otomatis
- Metode cutover yang disarankan:
  - freeze sementara transaksi untuk produk tersebut
  - hitung stok parent terakhir per gudang/lokasi
  - user menentukan alokasi stok awal ke masing-masing variant
  - gunakan modul `application/modules/konversi_varian` sebagai jalur cutover resmi
  - gunakan flow `7881` sebagai jalur resmi yang sudah benar-benar menulis stok varian
  - flow `881` branch masih perlu hardening/UAT, jangan dipakai sebagai jalur resmi dulu
  - posting `opening stock / stock cutover per variant`
  - nolkan stok parent lama sebagai hasil cutover, bukan edit diam-diam
  - setelah cutover, semua movement baru wajib memakai `variant_id`
- Prinsip setelah cutover:
  - parent stock hanya untuk `rollup / reporting`
  - source of truth stok operasional pindah ke variant
- Larangan:
  - jangan auto-split stok lama ke variant berdasarkan tebakan sistem

### Kontrol Sistem Yang Wajib Ada

- Validasi saat aktivasi varian:
  - cek open docs pembelian
  - cek open docs penjualan
  - cek stok parent per gudang
  - warning/block jika masih ada kondisi legacy yang aktif
- Dokumen operasional:
  - jangan gunakan SQL manual untuk membagi stok parent ke variant
  - gunakan modul `konversi_varian` agar mutasi stok, history, dan audit trail tetap tercatat
- Laporan transisi:
  - produk yang sudah menjadi varian tetapi masih punya open docs legacy
  - produk yang sudah menjadi varian tetapi parent stock belum nol
  - variant yang belum punya opening stock padahal parent stock lama sudah dipindah

## Progress Board

- [ ] Fase 1: Core Live
- [ ] Fase 2: Perluasan Operasional
- [ ] Fase 3: Reporting dan Hardening

## Track Khusus Harga Pembelian

Catatan:
- Penomoran fase di track ini memakai `H1-H5`.
- `H1-H5` berbeda dari fase rollout varian `Fase 1/2/3` di dokumen ini.
- Checklist detail: `docs/purchase-price-guardrail-checklist.md`.

Ringkasan status:
- [x] H1 - Delta harga + indikator visual di shopping cart (read-only)
- [x] H2 - Guardrail deviasi + alasan wajib sebelum submit/follow-up
- [ ] H3 - UX ringan: popup info harga per item
- [ ] H4 - Data quality tools: flag harga kosong/0/stale
- [ ] H5 - UAT + hardening fitur harga

## Track Khusus Produksi Rakitan

Catatan:
- Track ini fokus kesiapan varian untuk produk rakitan (`item_rakitan`), terpisah dari rollout transaksi varian umum.
- Hasil audit awal tersedia di `docs/sql/20260501_rakitan-variant-readiness-audit.sql`.

Ringkasan status:
- [x] P1 - Audit baseline data rakitan (has_variants, variant rows, family code kandidat)
- [x] P2 - Audit sample keluarga rakitan (`ED175CK`) untuk cek beda/sama komposisi
- [x] P3 - Keputusan sementara: rollback panel varian di edit `ProdukRakitan`
- [ ] P4 - Definisikan desain BOM varian (`variant_id` atau hybrid fallback)
- [ ] P5 - Siapkan migration + backfill BOM varian rakitan
- [ ] P6 - UAT produksi rakitan mode varian end-to-end

## Roadmap Bertahap

### Fase 1: Core Live

Target:
- Go live terbatas untuk jalur inti agar progres cepat terlihat.
- Fokus pada beli, terima, jual, dan harga `GLOBAL`.
- Mode harga `VARIANT` tidak masuk scope fase 1.
- Perkiraan: `6-9 hari kerja` dengan ritme `12 jam/hari` (sudah termasuk penggunaan AI).

Checklist:
- [x] Tetapkan kontrak data varian di transaksi: `produk_id` tetap ada, tambah `variant_id` dan snapshot varian
- [x] Tetapkan policy harga produk: `GLOBAL` atau `VARIANT`
- [x] Tambah field master produk untuk mode harga
- [x] Review schema tabel inti: transaksi, detail transaksi, locker, cache, serial
- [x] Siapkan migration SQL dan fallback untuk produk non-varian
- [x] Tetapkan policy cutover untuk open docs legacy dan stok parent legacy
- [x] Tambahkan validasi aktivasi varian: block/warning jika masih ada open docs
- [x] Tambahkan validasi aktivasi varian: block/warning jika parent stock belum nol / belum dialokasikan
- [x] Finalisasi rule harga beli/jual untuk mode `GLOBAL`
- [x] Upgrade selector item agar bisa memilih variant, bukan hanya produk parent
- [x] Upgrade shopping cart/session agar key item mendukung `variant_id`
- [x] Upgrade write path transaksi inti
- [x] Upgrade logic harga pada processor selector untuk mode `GLOBAL`
- [x] Upgrade modul `pembelian` untuk jalur PO
- [x] Upgrade jalur `GRN` pada modul `pembelian` step 3
- [x] Hardening warning perubahan data produk pada `pembelian/FollowUp` agar variant-aware
- [x] Upgrade modul `penjualan`
- [x] Upgrade stock / locker minimum yang wajib agar transaksi inti benar
- [ ] Uji master produk non-varian tetap normal
- [ ] Uji master produk varian tetap bisa create/edit/save
- [ ] Uji beli produk non-varian
- [ ] Uji beli produk varian mode `GLOBAL`
- [ ] Uji `GRN` dari PO varian
- [ ] Uji jual produk non-varian
- [ ] Uji jual produk varian mode `GLOBAL`
- [ ] Uji stok berkurang sesuai variant
- [ ] Uji aktivasi varian ditolak jika masih ada open docs legacy
- [ ] Uji aktivasi varian ditolak jika parent stock legacy belum dibereskan

Definition of Done Fase 1:
- [ ] Produk varian bisa dibeli
- [ ] Produk varian bisa diterima
- [ ] Produk varian bisa dijual
- [x] Harga fase 1 mengikuti `GLOBAL`
- [ ] Non-varian tetap berjalan tanpa regresi besar
- [x] Ada kontrol transisi untuk open docs legacy
- [x] Ada kontrol transisi untuk stok parent legacy

Catatan implementasi Fase 1:
- Kontrak varian transaksi sudah disiapkan di engine `MdlTransaksi`.
- DDL manual tersedia di `docs/sql/20260406_variant-transaction-contract.sql`.
- Endpoint idempotent untuk menambahkan kolom: `MigrateMaster/add_variant_transaction_contract`.
- Sampai kolom tersebut dijalankan di database aktif, data varian transaksi belum akan tersimpan.
- Policy harga produk `GLOBAL` / `VARIANT` sudah disiapkan pada master produk dan UI `variant_settings`.
- DDL manual mode harga tersedia di `docs/sql/20260406_product-variant-price-mode.sql`.
- Endpoint idempotent untuk kolom mode harga: `MigrateMaster/add_product_variant_price_mode`.
- DDL manual tabel stok varian tersedia di `docs/sql/20260406_stock_locker_varian.sql`.
- Endpoint idempotent untuk tabel stok varian: `MigrateMaster/add_variant_stock_locker_table`.
- DDL manual tabel dukungan stok varian lanjutan tersedia di `docs/sql/20260408_variant-stock-support.sql`.
- Endpoint idempotent untuk tabel dukungan stok varian lanjutan: `MigrateMaster/add_variant_stock_support_tables`.
- Pembuatan tabel `stock_locker_varian` hanya menutup blocker teknis pembacaan stok varian; saldo stok varian tetap harus diisi lewat stock cutover / `konversi_varian`.
- Untuk mengejar live lebih cepat, implementasi aktif fase 1 dibatasi ke mode `GLOBAL`.
- Mode `VARIANT` dianggap backlog fase berikutnya, bukan bagian definition of done fase 1.
- Guard aktivasi varian sudah dipasang di `ProductVariantSettings` agar produk tidak bisa langsung diaktifkan menjadi varian jika masih ada `open docs legacy` atau `parent stock legacy`.
- Guard master data sekarang dua arah di `ProductVariantSettings`: aktivasi `non-varian -> varian` dan rollback `varian -> non-varian` sama-sama diblok jika kondisi operasional belum aman.
- Sumber guard sementara memakai transaksi detail `transaksi/transaksi_data` dan locker parent `stock_locker`.
- Guard rollback `varian -> non-varian` mengecek tiga hal: master varian masih ada, open docs dengan `variant_id`, dan stok operasional di `stock_locker_varian`.
- Untuk stok legacy, jalur cutover yang direkomendasikan tetap `konversi_varian`, bukan SQL manual.
- Flow `881` cabang sudah di-hardening mengikuti pola `7881` pada level config/core/com, tetapi tetap berstatus `UAT pending` sampai jalur cabang diuji end-to-end.
- Hardening stok varian lanjutan sekarang mencakup `LockerStockVarian`, `LockerStockMutasiVarian`, dan `FifoProdukJadiVarian`.
- Fondasi selector/cart sudah mulai variant-aware: selector sudah bisa mengirim `produk_id + variant_id + cart_key`, dan cart processor inti sudah dipisah antara identity item vs parent product.
- Modul `penjualan` sudah distandarkan lebih dulu dengan pola `variant picker`: pilih parent product, tampil semua SKU/varian, tampil `stok avail`, lalu user isi `qty final` per varian sebelum item masuk ke cart.
- Modul `pembelian` sekarang mengikuti pola `variant picker` yang sama: pilih parent product, tampil semua SKU/varian, tampil `stok avail`, lalu user isi `qty final` per varian untuk masuk ke cart pembelian.
- Jika tabel operasional `stock_locker_varian` belum ada, picker `penjualan` sekarang tidak lagi `500`; UI akan menampilkan warning bahwa setup stok varian dan cutover belum siap, dan submit ke cart diblok dengan pesan yang jelas.
- Modul `pembelian` sudah mulai dinaikkan langsung: `_selectorItem`, `_processSelectProduct`, dan `_shoppingCart` sekarang membawa metadata varian dan menjaga `cart_key` saat edit/remove.
- Write-path `detailValues` di modul `pembelian` dan `penjualan` sudah dinormalisasi (jalur `Create`, `Create_total`, `FollowUp`) agar `produk_id` tetap parent product dan snapshot varian ikut terbawa jika item berasal dari variant.
- Warning perubahan data produk pada `pembelian/FollowUp/updateItems` sudah di-hardening: compare dilakukan per `cart_key` (termasuk key `variant:*`), tidak lagi menimpa antar baris varian parent yang sama, dan metadata approval menyimpan `produk_id` + `item_key` untuk jejak audit.
- Resolver identity selector (`_processSelectProduct`) sekarang fallback ke `variant_id` untuk mengisi `produk_id` parent jika request hanya membawa `cart_key variant:*`.
- Write-path `MdlTransaksi` sekarang menormalisasi payload detail varian sebelum insert (`writeDetailEntries`, `writeDetailSubEntries`, `writeDetailSubEntries_items`, `writeDetailValues`, `writeDetailFields`, `writeDetailSerialEntries`) agar `produk_id` tidak jatuh ke `0` dan snapshot varian tetap konsisten.
- Rule harga transaksi fase 1 sudah dikunci ke `GLOBAL` pada processor `pembelian` dan `penjualan`, sehingga override harga varian belum aktif walaupun master mode terset `VARIANT`.
- Post komponen stok inti fase 1 sudah disambungkan ke jalur varian: `pembelian` step `467` (GRN) serta `penjualan` flow inti dan retur aktif sekarang ikut menulis `LockerStockVarian`, `LockerStockMutasiVarian`, dan `FifoProdukJadiVarian` dengan filter no-op untuk item non-varian.
- Sisa pekerjaan fase 1 saat ini dominan `UAT/manual verification`. Untuk mempercepat eksekusi tim, gunakan panduan `docs/phase1-uat-quick-check.md` dan query smoke-check `docs/sql/20260409_phase1_uat_smoke.sql`.
- Disiapkan endpoint repair snapshot detail untuk kasus historis `produk_id=0` di transaksi varian: `MigrateMaster/repair_variant_zero_produk_id` (default `dry_run=1`, source data dari `transaksi_data_registry.items`, tanpa auto-tebak). Gunakan bertahap dengan filter `jenis`/`from_id` dulu, contoh: `?dry_run=1&jenis=466,466r,467&from_id=456500`.

### Fase 2: Perluasan Operasional

Target:
- Perluas dukungan variant ke movement stock dan modul turunan.
- Perkiraan: `6-10 hari kerja` dengan ritme `12 jam/hari` (sudah termasuk penggunaan AI).

Checklist:
- [ ] Upgrade hold stock / active stock / booking stock agar by variant (progress: `pembelian/_processSelectProduct` jalur `select/remove/selectNoQty/multiSelect/multiSelect_/multiSelectBiNew` + wrapper identity di `MdlLockerStock` dan `MdlLockerStockBooking`)
- [ ] Upgrade stock cache dan stock calculation agar by variant (progress: `MdlLockerStockCache` sudah punya wrapper `lookUpStockSumActiveByIdentity`, non-varian tetap fallback ke jalur lama)
- [ ] Pastikan parent product hanya rollup, bukan source of truth stok
- [ ] Siapkan mekanisme opening stock / stock cutover per variant
- [ ] Tetapkan `konversi_varian` sebagai jalur resmi cutover stok legacy
- [ ] Pakai `7881` sebagai jalur resmi stock cutover produk varian
- [x] Hardening flow `881` branch mengikuti pola `7881`
- [x] Siapkan komponen dan tabel dukungan stok varian lanjutan untuk `881/7881`
- [ ] UAT flow `881` branch sebelum dinyatakan boleh dipakai (ditunda sampai Fase 1 selesai)
- [ ] Upgrade retur / cancel / reverse flow bila mengambil item transaksi
- [ ] Aktifkan mode harga `VARIANT` bila kebutuhan bisnis sudah jelas
- [ ] Tambahkan validasi harga kosong jika mode `VARIANT` dipakai
- [ ] Uji beli produk varian mode `VARIANT`
- [ ] Uji jual produk varian mode `VARIANT`
- [ ] Upgrade modul `requeststok` (progress: selector + cart identity varian untuk `select/remove/selectNoQty/multiSelect` sudah aktif, reserve/release locker varian sudah ke `stock_locker_varian`, tinggal UAT flow end-to-end)
- [ ] Upgrade modul `pindahgudang` (progress: selector + variant picker + identity cart varian di `select/remove/selectNoQty/multiSelect` sudah aktif, tinggal UAT flow end-to-end)
- [ ] Review modul `distribusifg` (prioritas untuk transfer stok varian dari pusat ke cabang agar rollout lebih cepat)
- [ ] Upgrade modul `opname` (progress: selector + variant picker + identity cart varian di `select/remove/selectNoQty/multiSelect` sudah aktif, tinggal UAT flow end-to-end)
- [ ] Review modul `pembelianimport` (progress: selector + variant picker + identity cart varian di `select/remove/selectNoQty/multiSelect` sudah aktif, tinggal UAT flow end-to-end)
- [ ] Review modul `pembelianprojek`
- [ ] Review modul `penerimaanprojek`
- [ ] Review modul `penjualanproject`
- [ ] Review modul `distribusiproduksi`
- [ ] Review modul `distribusisupplies`
- [ ] Review modul `produksi`
- [ ] Review modul `produksiproses`
- [ ] Uji hold/release stock sesuai variant
- [ ] Uji movement stock sesuai variant
- [ ] Uji dokumen turunan yang mengambil item transaksi

Catatan progres fase 2:
- `application/modules/pembelian/config/coTransaksiUi.php` sekarang memiliki `priceDeltaValidator` untuk transaksi `466` (step `1/2`, mode varian) agar deviasi harga besar wajib disertai alasan (`note`).
- `application/modules/pembelian/controllers/Transaksi.php` dan `application/modules/pembelian/controllers/FollowUp.php` sekarang menambahkan guard deviasi harga terhadap `harga_last_purchase` sebelum preview/save/followup dieksekusi.
- `application/modules/pembelian/controllers/_processSelectProduct.php` sudah dinaikkan untuk jalur locker varian di action `select` dan `remove` (reserve/release stock `active <-> hold` by `variant_id`).
- Jalur bulk selector pembelian (`multiSelect`, `multiSelect_`, `multiSelectBiNew`) dan `selectNoQty` sudah ikut memproses identity item varian (`produk_id + variant_id + cart_key`) agar hold/release tidak jatuh ke parent-only.
- `application/models/Mdls/MdlLockerStock.php` dan `application/models/Mdls/MdlLockerStockBooking.php` sekarang punya wrapper identity (`cekLokerByIdentity`, `fetchStatesByIdentity`, `fetchStates2ByIdentity`) agar modul turunan bisa migrasi ke by-variant tanpa memecah API lama.
- `application/modules/pembelian/controllers/Create.php` (`swapFrom`) sudah memakai key item `produk|variant` untuk payload `itemSwapper`, sehingga varian parent yang sama tidak saling menimpa saat auto-select multi-item.
- `application/modules/pembelian/controllers/FollowUp.php` (jalur check stock + push ke pre-purchase) sudah memakai key identity `produk|variant` dan membaca locker lewat `cekLokerByIdentity` bila tersedia, dengan fallback legacy tetap aktif.
- `application/modules/pembelian/controllers/_shoppingCart.php` dan `application/modules/penjualan/controllers/_shoppingCart.php` (`reset`) sekarang resolve identity item dari payload cart (`produk_id + variant_id`) lalu release locker memakai `cekLokerByIdentity` bila tersedia, dengan fallback legacy tetap aktif.
- `application/modules/penjualan/controllers/FollowUp.php` sekarang mengikuti pola identity yang sama pada jalur check stock + push pre-purchase (`produk|variant`) agar varian parent yang sama tidak saling menimpa.
- `application/modules/penjualan/controllers/FollowUp.php` (`doFollowup` dan `doCancelPacking`) sekarang membaca key item session via resolver identity, sehingga validasi item closed/followed-up tidak salah target saat ada varian.
- `application/modules/penjualan/controllers/_processSelectProduct.php` (`multiSelect`) sekarang sudah membaca payload bulk dengan resolver identity (`produk_id + variant_id + cart_key`) dan memakai reserve locker varian saat item varian dipilih.
- `application/modules/penjualan/controllers/_processSelectProduct.php` (`selectNoQty`) sekarang resolve request identity varian, lookup harga/discount berdasarkan parent (`produk_id`), dan reserve/release locker mengikuti `variant_id` jika item varian.
- `application/modules/requeststok/controllers/_selectorItem.php` sekarang mendukung source selector varian (`MdlProdukVarian`), collapse parent varian untuk picker, dan menyediakan endpoint `variantPicker`.
- `application/modules/requeststok/views/_selector.php` sekarang bisa membaca `queryParams`, `check_key`, dan `dialog_size` dari item selector sehingga payload varian tidak hilang saat klik selector.
- `application/modules/requeststok/controllers/_processSelectProduct.php` jalur `select/remove` sekarang resolve identity item (`produk_id + variant_id + cart_key`) agar cart key varian (`variant:*`) tidak bentrok dengan item non-varian.
- `application/modules/requeststok/controllers/_processSelectProduct.php` jalur `multiSelect` dan `selectNoQty` sekarang punya hardening varian: payload varian dialihkan ke `select()` variant-aware, sedangkan payload non-varian tetap lewat flow legacy.
- `application/modules/requeststok/controllers/_processSelectProduct.php` reserve/release locker untuk item varian (`select/remove`) sekarang menulis ke `stock_locker_varian` lewat `MdlLockerStockVarian` agar tidak lagi jatuh ke locker parent-only.
- `application/modules/requeststok/views/variant_picker.php` ditambahkan untuk flow pilih qty final per varian sebelum item masuk cart.
- `application/modules/pindahgudang/controllers/_selectorItem.php`, `application/modules/pindahgudang/views/_selector.php`, dan `application/modules/pindahgudang/views/variant_picker.php` sekarang sudah variant-aware (collapse parent untuk picker, payload identity varian, modal qty per varian).
- `application/modules/pindahgudang/controllers/_processSelectProduct.php` jalur `select/remove/selectNoQty/multiSelect` sekarang memakai identity item (`produk_id + variant_id + cart_key`) dan reserve/release locker varian via `MdlLockerStockVarian`.
- `application/modules/opname/controllers/_selectorItem.php`, `application/modules/opname/views/_selector.php`, dan `application/modules/opname/views/variant_picker.php` sekarang sudah variant-aware untuk jalur pemilihan item varian.
- `application/modules/opname/controllers/_processSelectProduct.php` jalur `select/remove/selectNoQty/multiSelect` sekarang memakai identity item (`produk_id + variant_id + cart_key`) dan reserve/release locker varian via `MdlLockerStockVarian`.
- `application/modules/opname/controllers/_shoppingCart.php` sekarang encode id target edit/remove agar cart key varian (`variant:*`) tidak putus saat klik edit/hapus.
- `application/modules/pembelianimport/controllers/_selectorItem.php`, `application/modules/pembelianimport/views/_selector.php`, dan `application/modules/pembelianimport/views/variant_picker.php` sekarang sudah variant-aware (collapse parent untuk picker, payload identity varian, modal qty per varian).
- `application/modules/pembelianimport/controllers/_processSelectProduct.php` jalur `select/remove/selectNoQty/multiSelect` sekarang memakai identity item (`produk_id + variant_id + cart_key`), reserve/release locker varian via `MdlLockerStockVarian`, dan menjaga `cart_key` saat reload cart.
- `application/modules/pembelianimport/controllers/_shoppingCart.php` sekarang encode id target edit/remove agar cart key varian (`variant:*`) tidak putus saat klik edit/hapus.
- Paket UAT fase 2 disiapkan di `docs/phase2-uat-quick-check.md` dan query smoke-check `docs/sql/20260409_phase2_uat_smoke.sql`.
- Paket UAT tambahan untuk verifikasi accounting varian disiapkan di `docs/phase2-uat-rekening-varian-check.md` dan query `docs/sql/20260412_rekening_varian_uat_smoke.sql`.

Definition of Done Fase 2:
- [ ] Stock operasional sudah by variant
- [ ] Modul movement utama tidak salah baca parent product
- [ ] Mode harga `VARIANT` siap dipakai jika memang dibutuhkan
- [ ] Jalur turunan utama tidak rusak untuk non-varian maupun varian

### Fase 3: Reporting dan Hardening

Target:
- Rapikan agregasi, laporan, stabilisasi, dan readiness produksi penuh.
- Perkiraan: `4-8 hari kerja` dengan ritme `12 jam/hari` (sudah termasuk penggunaan AI).

Checklist:
- [ ] Upgrade report produk agar bisa rollup per parent product
- [ ] Upgrade report produk agar bisa detail per variant
- [ ] Review HPP, biaya, dan summary penjualan/pembelian yang masih produk-level
- [ ] Review serial / garansi bila item berserial harus by variant
- [ ] Regression test end-to-end
- [ ] UAT dengan sample data real
- [ ] Fix bug dan final cleanup config
- [ ] Review backward compatibility dokumen lama

Definition of Done Fase 3:
- [ ] Report minimal tidak rusak
- [ ] Dokumen lama tetap bisa dibuka
- [ ] Produk varian stabil di jalur transaksi utama
- [ ] UAT dinyatakan lulus

## Daftar Modul/Folder Terdampak

### Prioritas Utama

- `application/modules/pembelian`
- `application/modules/penjualan`

### Turunan Stock / Movement

- `application/modules/requeststok`
- `application/modules/pindahgudang`
- `application/modules/opname`
- `application/modules/konversi_varian`

### Turunan Distribusi / Produksi

- `application/modules/distribusifg`
- `application/modules/distribusiproduksi`
- `application/modules/distribusisupplies`
- `application/modules/produksi`
- `application/modules/produksiproses`

### Project / Import / Khusus

- `application/modules/pembelianimport`
- `application/modules/pembelianprojek`
- `application/modules/penerimaanprojek`
- `application/modules/penjualanproject`

### Laporan

- `application/modules/laporan`
- `application/modules/laporankeuangan`

### Shared Engine

- `application/controllers/Selectors`
- `application/models/Mdls`
- `application/config`

Catatan:
- Jalur penerimaan barang / `GRN` untuk pembelian berada di modul `application/modules/pembelian` sebagai step 3 transaksi purchase.
- Di config UI, flow pembelian produk bergerak dari `466` ke `467` dengan label `GOODS RECEIVED NOTE`.
- Folder `application/modules/penerimaan` bukan owner utama flow `GRN` pembelian ini.
- Untuk cutover stok parent lama ke stok variant, jalur yang direkomendasikan adalah modul `application/modules/konversi_varian`.
- Jalur resmi yang saat ini benar-benar menulis stok variant adalah flow `7881`.
- Flow `881` sudah di-hardening agar target-nya ikut menulis stok variant, tetapi tetap perlu UAT cabang sebelum dinyatakan siap produksi.

## Definition of Done Minimum

- [ ] Non-varian tetap berjalan tanpa regresi
- [ ] Produk varian bisa dibeli
- [ ] Produk varian bisa diterima
- [ ] Produk varian bisa dijual
- [ ] Harga rollout aktif mengikuti `GLOBAL`
- [ ] Stok bergerak sesuai variant
- [ ] Report minimal tidak rusak
- [ ] Dokumen lama tetap bisa dibuka
- [ ] Dokumen legacy tidak bercampur diam-diam dengan variant baru
- [ ] Parent stock legacy tidak tersisa tanpa keputusan cutover

## Cara Pakai

- Pakai dokumen ini sebagai acuan checkpoint.
- Jika posisi kerja terputus, lanjut dari checklist terakhir yang belum selesai.
- Saat ada item selesai, centang langsung di dokumen ini.
- Jika ingin progres cepat terlihat, prioritaskan menyelesaikan `Fase 1` lebih dulu.
