# BLUEPRINT UAT FIX `konversi`: Selector + ShoppingCart Support Varian

## 1. Tujuan

Dokumen ini memecah masalah UAT terbaru di modul [`konversi`](../application/modules/konversi/) menjadi blueprint implementasi yang bisa dieksekusi bertahap, tanpa langsung patch di dokumen ini.

Fokus UAT:

1. Ada pembeda jelas produk varian vs non-varian di selector.
2. Klik produk non-varian langsung masuk shoppingcart.
3. Klik produk varian membuka modal pilih varian, lalu klik varian baru masuk shoppingcart.
4. Shoppingcart konsisten untuk source/target varian dan non-varian.
5. Varian vs master setara di session `items`.
6. Varian vs master setara di session `items2`.
7. Dropdown target di shoppingcart (`items2`) bisa pilih varian maupun non-varian.

## 2. Scope

### 2.1 In Scope

1. Jenis transaksi `1334` (pusat) dan `334` (cabang) pada [`coTransaksiUi.php`](../application/modules/konversi/config/coTransaksiUi.php).
2. Layer selector item, variant picker modal, processor select/remove, shoppingcart render, validator paired item.
3. Session contract: `items`, `items2`, `items2_sum`, `main.conversion_mode`.

### 2.2 Out of Scope

1. Perubahan mekanisme posting stok/followup accounting final (`coTransaksiCore.php`) pada fase UI ini.
2. Rewrite total template transaksi.
3. Perubahan lintas modul selain referensi pola implementasi.

### 2.3 Workflow Transaksi (Eksplisit)

Workflow transaksi modul `konversi` tetap **2 step** sesuai konfigurasi `coTransaksiUi.php`:

1. Step 1: `request` (pengajuan konversi)
2. Step 2: `approve` (persetujuan/eksekusi konversi)

Catatan:

1. Skenario M1-M4 pada blueprint adalah mode konversi (varian/non-varian), **bukan** jumlah step workflow transaksi.

## 3. Baseline Masalah (Evidence)

### 3.1 Mode varian belum aktif di `konversi`

1. Flag `variant_mode_enabled` masih `false` untuk `1334` dan `334`:
   - [`application/modules/konversi/config/coTransaksiUi.php:15`](../application/modules/konversi/config/coTransaksiUi.php:15)
   - [`application/modules/konversi/config/coTransaksiUi.php:1155`](../application/modules/konversi/config/coTransaksiUi.php:1155)
2. Dampak: block validasi/alokasi varian berbasis mode belum aktif penuh.

### 3.2 Selector belum menandai item varian

1. `selectorViewedFields` belum memuat `has_variants`:
   - [`application/modules/konversi/config/coTransaksiUi.php:78`](../application/modules/konversi/config/coTransaksiUi.php:78)
   - [`application/modules/konversi/config/coTransaksiUi.php:1215`](../application/modules/konversi/config/coTransaksiUi.php:1215)
2. Di `_selectorItem`, belum ada branch render badge varian seperti modul referensi:
   - [`application/modules/konversi/controllers/_selectorItem.php:323`](../application/modules/konversi/controllers/_selectorItem.php:323)
   - pembanding: [`application/modules/variant_cutover/controllers/_selectorItem.php:332`](../application/modules/variant_cutover/controllers/_selectorItem.php:332)

### 3.3 Belum ada `variantPicker` di modul `konversi`

1. View selector hanya membuka modal jika `socketURL` terisi, selain itu langsung submit ke processor:
   - [`application/modules/konversi/views/_selector.php:44`](../application/modules/konversi/views/_selector.php:44)
   - [`application/modules/konversi/views/_selector.php:81`](../application/modules/konversi/views/_selector.php:81)
2. Di `konversi`, controller `_selectorItem` tidak punya endpoint `variantPicker`.
3. Dampak: klik master produk varian masih masuk flow direct-select, belum flow modal pilih varian.

### 3.4 Kontrak `item_type` belum konsisten

1. Resolver mode di `Modul_Controller` membaca nilai `variant`:
   - [`application/modules/konversi/controllers/Modul_Controller.php:109`](../application/modules/konversi/controllers/Modul_Controller.php:109)
2. Namun source/target gate saat ini mengisi `item_type` sebagai `varian`:
   - [`application/modules/konversi/controllers/_processSelectProductConvertion.php:32`](../application/modules/konversi/controllers/_processSelectProductConvertion.php:32)
   - [`application/modules/konversi/controllers/_processSelectProductConvertion.php:54`](../application/modules/konversi/controllers/_processSelectProductConvertion.php:54)
3. Dampak: `conversion_mode` berpotensi jatuh ke `M4` meskipun baris item varian.

### 3.5 Shoppingcart target masih product-only dropdown

1. Dropdown paired target (`selItems`) dibangun dari `mdlName` `MdlProduk2` saja:
   - [`application/modules/konversi/config/coTransaksiUi.php:234`](../application/modules/konversi/config/coTransaksiUi.php:234)
   - [`application/modules/konversi/controllers/_shoppingCart.php:1902`](../application/modules/konversi/controllers/_shoppingCart.php:1902)
2. Dampak: target varian tidak setara karena pilihan utama masih parent-product.

### 3.6 Struktur session masih bercampur shadow-state

1. Terdapat shadow-state source/target (`items_source`, `items_target`) hasil rebuild.
2. Sementara flow utama cart membaca `items`, sehingga state bisa berbeda antar bucket.
3. Dampak: UI bisa menampilkan pesan/hasil yang tidak sinkron dengan data session utama.

### 3.7 Flow remove/edit masih identity produk parent

1. Remove memakai `$_GET['id']` dan release locker parent `MdlLockerStock` by produk:
   - [`application/modules/konversi/controllers/_processSelectProductConvertion.php:874`](../application/modules/konversi/controllers/_processSelectProductConvertion.php:874)
   - [`application/modules/konversi/controllers/_processSelectProductConvertion.php:891`](../application/modules/konversi/controllers/_processSelectProductConvertion.php:891)
2. Dampak: jika item varian dipisah per identity, remove/release belum aman.

### 3.8 Kontrak render source vs warning target belum tegas

1. Di view shopping cart, warning pairing target berada di branch yang sama dengan tabel anak target:
   - [`application/modules/konversi/views/shoppingCart.php:517`](../application/modules/konversi/views/shoppingCart.php:517)
2. Pada kondisi tertentu, warning target tampil dominan sehingga user menangkap seolah source tidak ada.
3. Session bukti UAT menunjukkan `items` dan `items_source` terisi, namun `items_target` kosong.
4. Dampak: user tidak bisa membedakan masalah “source kosong” vs “target belum dipilih”.

### 3.9 State messaging belum dipisah antara empty source dan empty target

1. Pesan:
   - `SILAHKAN PILIH ITEM HASIL KONVERSI...`
   - `you have not chosen any item yet`
   dapat muncul berurutan dan menimbulkan tafsir cart kosong total.
2. Dampak: diagnosis operasional meleset, padahal yang kosong hanya target pairing.

### 3.10 Temuan baru: perbedaan layer shoppingcart vs sub-postProcessor

1. Perpindahan stok saat pilih/hapus item di shoppingcart (`active <-> hold`) sudah bisa branch varian/non-varian di controller:
   - row varian: `MdlLockerStockVariant`
   - row non-varian: `MdlLockerStock`
2. Namun pada layer `sub-postProcessor` (FollowUp), eksekusi komponen masih mengikuti mapping step (`coTransaksiCore`) tanpa routing row-level.
3. Di step `request` (`1334r`/`334r`), komponen `LockerStockVariant` memang belum terdaftar; debug normal jika hanya terlihat `LockerStock`.
4. Dampak yang teramati:
   - row varian bisa ikut diproses `ComLockerStock` parent bila tidak difilter,
   - memicu error stok parent hold/active walau stok varian tersedia.
5. Guard awal sudah diterapkan:
   - `ComLockerStock` hanya memproses row non-varian (`variant_id=0`),
   - row varian (`variant_id>0` atau `cart_key` format `variant:{pid}:{vid}`) diabaikan di jalur parent locker.

## 4. Root Cause Summary

1. Konfigurasi UI `konversi` masih baseline non-varian untuk selector dan paired target.
2. Belum ada endpoint modal picker varian di modul `konversi`.
3. Session contract bercampur antara `items` dan shadow-state (`items_source/items_target`), sehingga identity row tidak tunggal.
4. Enum `item_type` tidak sinkron (`varian` vs `variant`) terhadap resolver mode.
5. Kontrak presentasi “source-first” belum dijaga secara eksplisit saat target belum dipilih.

## 5. Target Desain Perbaikan

## 5.1 UX Selector (wajib sesuai UAT)

1. Non-varian: klik item -> langsung add ke cart (tetap native).
2. Varian: klik master -> buka modal `variantPicker` -> user pilih varian + qty -> submit -> masuk cart.
3. Tampilkan badge jelas pada selector:
   - `PRODUK VARIAN` untuk item `has_variants=1`.
   - `PRODUK NON-VARIAN` untuk item biasa (opsional, bisa by style netral).

Referensi pola yang diadopsi:

1. Builder URL modal varian:
   - [`application/modules/requeststok/controllers/_selectorItem.php:30`](../application/modules/requeststok/controllers/_selectorItem.php:30)
2. Override `socketURL` ketika `has_variants=1`:
   - [`application/modules/requeststok/controllers/_selectorItem.php:478`](../application/modules/requeststok/controllers/_selectorItem.php:478)
3. Endpoint modal picker:
   - [`application/modules/requeststok/controllers/_selectorItem.php:540`](../application/modules/requeststok/controllers/_selectorItem.php:540)
4. View modal picker:
   - [`application/modules/requeststok/views/variant_picker.php:1`](../application/modules/requeststok/views/variant_picker.php:1)

## 5.2 Session Contract (setara varian vs non-varian)

Canonical identity:

1. Key row source non-varian di `items` tetap native (key numerik `id` master produk, tanpa perubahan).
2. Key row source varian di `items` memakai format `variant:{id_master}:{id_varian}`.
3. `id_master` = `produk_id` (parent product), `id_varian > 0` untuk varian.
4. `item_type` diturunkan dari pola key:
   - key `variant:*:*` => `variant`
   - key numerik native => `produk`

Aturan khusus source varian dari master yang sama:

1. Jika source berisi 2 varian dari master product yang sama, wajib tersimpan sebagai 2 row terpisah.
2. Kunci row wajib key session row (untuk varian: `variant:{id_master}:{id_varian}`), bukan `produk_id` numeric.
3. Contoh:
   - `variant:1743:501`
   - `variant:1743:502`
4. Larangan: menggabungkan dua varian berbeda ke satu row source hanya karena `produk_id` sama.

Contract item minimal (dipakai sama di `items`, `items2_sum`, gate source/target):

1. `session_key` (key index row di `items`)
2. `item_type` (standar nilai: `produk` atau `variant`)
3. `produk_id` (parent product)
4. `variant_id` (0/null untuk non-varian)
5. `parent_produk_id`
6. `nama`
7. `kode` / `produk_kode`
8. `satuan`
9. `jml`
10. `hpp` / `harga` (sesuai field source)

Aturan sinkron session:

1. `items` adalah single source of truth untuk source rows (tanpa `items_source/items_target`).
2. `items2` = detail distribusi target per row source (`items2[source_session_key][target_session_key]`).
3. `items2_sum` = agregat target per row source untuk validasi lama, tetapi wajib menyimpan `session_key`, `item_type`, `produk_id`, `variant_id`.
4. View shopping cart membaca `items` apa adanya dari session. Jika ada proses normalisasi/rebuild, hasilnya wajib ditulis kembali ke `$_SESSION[$cCode]['items']` sebelum render (prinsip `view = session`).

## 5.3 Konsolidasi `conversion_mode`

1. Samakan enum `item_type` ke `variant` (bukan `varian`) pada semua gate builder.
2. Jika perlu compatibility, sediakan normalizer:
   - input `varian` -> konversi internal ke `variant`.
3. Recompute mode setiap perubahan source/target.

## 5.4 Dropdown Target di ShoppingCart

1. `selItems` harus berasal dari union data:
   - produk non-varian (MdlProduk2)
   - varian aktif (MdlProdukVarian selector rows)
2. Option menyimpan identity penuh (minimal `value=session_key` + metadata hidden).
3. Saat user pilih target:
   - jika key numerik native -> isi target non-varian
   - jika `variant:{id_master}:{id_varian}` -> isi target varian
4. Dengan ini requirement no.7 terpenuhi: satu dropdown `items2` untuk dua tipe target.

## 5.5 Validasi UI Integrity

1. Pairing wajib: tiap source row harus punya target row valid.
2. Qty rule:
   - total target per source = qty source.
   - untuk target varian per parent: total child varian = qty yang dialokasikan.
3. Mapping rule:
   - `variant_id` harus milik `produk_id` parent (cek DB).
4. Block approve jika gagal, error message sebut item + tipe + `session_key`.

## 5.6 Kontrak Render Source-First (wajib)

1. Jika `items` berisi minimal 1 row, tabel source wajib tetap tampil.
2. Warning target (`paired item warning`) tidak boleh menggantikan/menutupi tabel source.
3. Empty state wajib dipisah:
   - Empty source: tampilkan pesan "belum pilih item source".
   - Source ada, target kosong: tampilkan warning pairing target per row/section target.
4. Styling warning target hanya di section target (bukan global cart container).
5. `pairedItemNoTarget` tidak boleh memicu fallback "cart kosong total".

## 5.7 Lifecycle Session per Jenis Source

1. Source non-varian:
   - insert/update row source dengan key native existing (numerik `id` master)
   - `item_type=produk`, `variant_id=0`
2. Source varian:
   - insert/update row source dengan key `variant:{id_master}:{id_varian}`
   - `item_type=variant`, `variant_id>0`
3. Source dua varian dari master yang sama:
   - wajib dua row source terpisah berdasarkan key varian
   - update qty satu row tidak boleh mengubah row varian lain
4. Pairing target:
   - pilih target dari dropdown, simpan ke `items2_sum` dengan key target yang dipilih
5. Remove source:
   - hapus row source by `session_key`
   - cascade remove pairing target row terkait source tersebut

## 5.8 Kontrak Routing PostProcessor per Row (baru)

1. Routing postProcessor wajib berbasis row (`variant_id`) dan tidak boleh per-transaksi global.
2. Rule source (`items`):
   - `variant_id=0` => komponen `LockerStock` / `LockerStockMutasi`
   - `variant_id>0` => komponen `LockerStockVariant` / `LockerStockMutasiVariant`
3. Rule target (`items2_sum`):
   - `variant_id=0` => `FifoAverage/FifoProdukJadi + LockerStock + LockerStockMutasi`
   - `variant_id>0` => `FifoProdukJadiVarian + LockerStockVariant + LockerStockMutasiVariant`
4. Guard minimum:
   - `ComLockerStock` skip row varian,
   - `ComLockerStockVariant` skip row non-varian (`variant_id<1`),
   - untuk payload legacy, `cart_key=variant:{pid}:{vid}` dianggap varian walau `variant_id` belum terisi.
5. Catatan step:
   - `request` tidak otomatis menambah stok target; eksekusi stok target utama tetap terjadi saat `approve`.

## 6. Opsi Solusi

### Opsi A (Recommended): Unified Identity Adapter di modul `konversi`

1. Tambah normalizer `session_key` (non-varian native + varian `variant:{pid}:{vid}`) di processor/cart.
2. Pertahankan gate lama untuk compatibility (`items2_sum`) tapi isi berdasarkan identity baru.
3. Tambah `variantPicker` native di `konversi` (adopsi pola requeststok).

Kelebihan:

1. Tetap di modul `konversi` (tanpa delegasi modul lain).
2. Menjawab 7 gap UAT sekaligus.
3. Memudahkan mode M1-M4 ke depan.

Risiko:

1. Menyentuh area legacy yang dipakai validator/followup.

### Opsi B: Patch Minimal UI tanpa ubah identity contract

1. Hanya tambah badge + modal picker + inject ke struktur lama.
2. Biarkan key utama tetap numeric product id.

Kelebihan:

1. Patch cepat.

Risiko:

1. Source varian multi-item parent sama akan sulit konsisten.
2. Potensi bug edit/remove tetap tinggi.
3. Requirement setara session `items/items2` cenderung tidak tuntas.

## 7. Rencana Perubahan File (belum dieksekusi)

1. [`application/modules/konversi/config/coTransaksiUi.php`](../application/modules/konversi/config/coTransaksiUi.php)
   - aktifkan `variant_mode_enabled` (`1334`, `334`)
   - tambah `has_variants` di `selectorViewedFields`
   - siapkan config dropdown target union (produk + varian)
2. [`application/modules/konversi/controllers/_selectorItem.php`](../application/modules/konversi/controllers/_selectorItem.php)
   - tambah helper variant picker (`build_variant_picker_url`, `selector_supports_variant_picker`, `variantPicker`)
   - override `socketURL` untuk item `has_variants=1`
   - badge visual varian/non-varian
3. [`application/modules/konversi/views/_selector.php`](../application/modules/konversi/views/_selector.php)
   - dukung `dialog_size` untuk modal varian
   - pastikan click non-varian tetap direct-add
4. [`application/modules/konversi/views/variant_picker.php`](../application/modules/konversi/views/variant_picker.php)
   - file baru, adopsi pola requeststok sesuai kebutuhan konversi
5. [`application/modules/konversi/controllers/_processSelectProductConvertion.php`](../application/modules/konversi/controllers/_processSelectProductConvertion.php)
   - normalisasi `item_type`
   - tambah parser/generator key varian `variant:{pid}:{vid}` pada select/remove
   - non-varian tetap pakai key native existing
6. [`application/modules/konversi/controllers/_shoppingCart.php`](../application/modules/konversi/controllers/_shoppingCart.php)
   - hilangkan dependency `items_source/items_target`
   - pastikan renderer memakai `items` apa adanya dari session
   - jika normalisasi row dijalankan, wajib commit balik ke `$_SESSION[$cCode]['items']` sebelum render
   - generator `selItems` union produk+varian
   - writer `items2`/`items2_sum` agar simetris varian/non-varian
7. [`application/modules/konversi/views/shoppingCart.php`](../application/modules/konversi/views/shoppingCart.php)
   - render dropdown target berbasis identity
   - value dropdown target pakai key session (`id` native untuk non-varian, `variant:{pid}:{vid}` untuk varian)
   - perbaiki parity tampilan dan edit flow untuk row varian/non-varian
   - enforce source-first render: source table always-on ketika `items` ada
   - pisahkan pesan empty source vs empty target
8. [`application/modules/konversi/controllers/Transaksi.php`](../application/modules/konversi/controllers/Transaksi.php)
   - validasi paired item berbasis `session_key` + `item_type`
   - validasi tabrakan row saat parent sama tapi `variant_id` berbeda
9. [`application/models/Coms/ComLockerStock.php`](../application/models/Coms/ComLockerStock.php)
   - guard row varian agar tidak menulis ke `stock_locker` parent
   - deteksi varian dari `variant_id/varian_id` dan fallback `cart_key`
10. [`application/modules/konversi/controllers/FollowUp.php`](../application/modules/konversi/controllers/FollowUp.php)
   - router/filter row-level sebelum `pair()` agar komponen varian/non-varian menerima row yang tepat
   - hard-filter shadow row source invalid (`produk_id=0` / `nama=0`) saat `followupPrePreview` + `followupPreview`

## 8. Parameter Pembuktian (Acceptance Parameters)

Parameter ini dipakai untuk membuktikan bahwa kondisi sudah benar.

### P-00 Source Visibility Guard

1. Dengan session yang memiliki `items` (source) dan target pairing kosong (`items2_sum` kosong), source table harus tetap tampil.
2. Warning target boleh tampil, tapi tidak boleh menggantikan source table.
3. Tidak boleh muncul kesan "cart kosong total" jika `items` ada.

### P-01 Selector Marker

1. Search item campuran.
2. Hasil harus menampilkan pembeda varian/non-varian.

### P-02 Click Behavior Non-Varian

1. Klik produk non-varian.
2. Item langsung masuk `items` dan tampil di cart tanpa modal.

### P-03 Click Behavior Varian

1. Klik produk `has_variants=1`.
2. Modal varian terbuka.
3. Pilih varian + qty > 0.
4. Item masuk cart sebagai key `variant:{id_master}:{id_varian}`.

### P-04 Session Equality

1. Untuk non-varian dan varian, cek row session memiliki field canonical yang sama.
2. `items` dan `items2_sum` wajib memuat `item_type`, `produk_id`, `variant_id`, `session_key`.

### P-05 `items2` Equality

1. Pair target per source bisa berisi key non-varian native atau `variant:{id_master}:{id_varian}`.
2. Edit/remove row tidak menimpa row identity lain.

### P-06 Dropdown Target Equality

1. Dalam satu source row, dropdown menampilkan opsi produk non-varian + varian.
2. Pilihan tersimpan dengan identity benar dan bisa direstore setelah reload cart.

### P-07 Validator Integrity

1. Total qty source = total qty target.
2. Mapping variant-parent valid.
3. Error message menyebut `session_key`/nama item saat gagal.

### P-08 No Regression M4

1. Skenario non-varian -> non-varian existing (`M4`) tetap lolos seperti sebelumnya.

### P-09 State Messaging Split

1. Saat `items` kosong: tampil pesan empty source saja.
2. Saat `items` ada namun `items2_sum` kosong: tampil warning target pairing saja.
3. Pesan tidak saling tumpang tindih.

### P-10 Same Master Multi-Variant Source

1. Tambahkan dua source varian dari parent produk yang sama.
2. Session harus menyimpan dua row source terpisah dengan key `variant:{pid}:{vid}` berbeda.
3. Edit qty salah satu varian tidak boleh mengubah qty varian satunya.

### P-11 Session Lifecycle Integrity

1. Uji alur create source -> pair target -> edit qty -> remove source.
2. Setiap transisi harus menjaga konsistensi `items` dan `items2_sum` (tanpa shadow-state source/target).
3. Setelah remove source, row target pasangan source tersebut ikut terhapus.

### P-12 PostProcessor Routing Integrity (baru)

1. Step `request` varian tidak boleh gagal karena cek stok parent locker (`stock_locker`) ketika stok varian valid.
2. Row varian tidak boleh menghasilkan write ke `stock_locker` parent melalui `ComLockerStock`.
3. Row non-varian tidak boleh menghasilkan write ke `stock_locker_varian`.
4. Pada transaksi campuran (varian + non-varian), setiap row harus diproses ke komponen locker yang sesuai.

### P-13 No Shadow Row in FollowUp Preview (baru)

1. Pada `followupPrePreview`/`followupPreview`, row source invalid (`id/key=0` atau `produk_id=0`) tidak boleh tampil.
2. Nilai `qty`/`uom` dari row invalid tidak boleh ikut kebawa ke table source.
3. Proses approve tidak boleh membaca row invalid tersebut sebagai item source sah.

## 9. UAT Matrix Ringkas

1. M1: varian -> varian
2. M2: non-varian -> varian
3. M3: varian -> non-varian
4. M4: non-varian -> non-varian
5. negative: qty 0
6. negative: stok kurang
7. negative: variant tidak sesuai parent
8. negative: source tanpa target
9. concurrency: 2 user edit source sama

## 10. Risiko & Mitigasi

1. Risiko: perubahan key session memutus flow legacy.
   - Mitigasi: non-varian tetap key native, perubahan hanya menambah pola key varian `variant:{pid}:{vid}`, rollout bertahap di `1334` dulu.
2. Risiko: remove/release salah locker untuk varian.
   - Mitigasi: parser identity + branch release ke locker varian jika `variant_id>0`.
3. Risiko: dropdown union membuat payload ambigu.
   - Mitigasi: value wajib key session row (`id` native atau `variant:{pid}:{vid}`), bukan label/nama polos.

## 11. Urutan Eksekusi Setelah Blueprint Disetujui

1. Fase A: Selector marker + variantPicker modal
2. Fase B: Session contract canonical + normalizer enum `item_type`
3. Fase C: Shoppingcart dropdown union + writer `items2/items2_sum`
4. Fase D: Validator hardening + UAT matrix + evidence

Tambahan guard eksekusi:

1. Perbaikan source-first render dijalankan sebelum optimasi UX target lain.

Dokumen ini adalah blueprint desain/perbaikan. Eksekusi patch dilakukan setelah approval.

