# Arsitektur Sistem ERP dan Master Blueprint Terpadu

**Dokumen:** Sintesis Lintas-Dokumen dari 15 sumber markdown
**Cakupan:** Master Blueprint Enterprise System (Volume 1-5), cetak biru multitenancy, flowchart modul, hierarki workflow, standar inventaris, optimalisasi storage, sinkronisasi produk, standar varian, dan refaktor laporan DDD
**Tanggal penyusunan:** 1 Oktober 2026

---

## 1. Ringkasan Eksekutif

1. **Dua generasi arsitektur hidup berdampingan, bukan satu.** Master Blueprint Enterprise System (Volume 1-5) merancang sistem dari nol dengan stack Django, Django REST Framework, PostgreSQL, Redis, Celery, Next.js, dan TypeScript. Sementara dokumen operasional (flowchart modul, hierarki workflow, sinkronisasi produk, tracker varian, refaktor laporan) masih mendeskripsikan dan memperbaiki sistem lama CodeIgniter 3 HMVC dengan PHP 5.6 dan MySQL. Keduanya memakai kosakata domain yang sama, sehingga terlihat sebagai satu produk, padahal fondasinya berbeda.
2. **Sistem lama sudah berjalan dan tidak bisa diabaikan.** File konfigurasi `heTransaksi_ui.php`, `coTransaksiValues.php`, dan `coTransaksiCore.php` menjadi tulang punggung alur dokumen, jurnal, dan stok. Tabel `log` di lapangan mencatat penggunaan nyata, misalnya transaksi `677` Biaya Usaha 1.536 hit, `110` E-Faktur PPN Keluaran 725 hit, `771` Pettycash refill 36 hit, dan `776` assembling 112 hit. Setiap blueprint harus menghormati konvensi yang sudah ada.
3. **Model otorisasi target berbasis objek, sedangkan sistem lama berbasis grup.** Master Blueprint menetapkan rantai `User -> Role -> Authorization Object -> Action -> Context` dengan prinsip *default deny* dan *union of permission filtered by context*. Sistem lama hanya mengenal grup prefiks di level baris UI: `o_kasir`, `o_finance`, `c_holding`, `c_finance`, `c_finance_spv`, `c_gudang`, `p_produksi_spv`, `w_gudang`, `sys`, `root`.
4. **Isolasi data menjadi pilihan paling divisive.** Cetak Biru Multitenancy versi 2.0 (31 Mei 2026) memilih *single database, shared schema* dengan kolom `id_perusahaan`. Dokumen inventaris enterprise justru menuntut *multi-database* terisolasi fisik per anak perusahaan dengan message broker dan composite key. Diskusi storage menolak kedua ekstrem itu dan merekomendasikan *database cut-off* tanpa mengubah kode aplikasi.
5. **Stok memiliki tiga sumber kebenaran dengan ruang lingkup berbeda.** Stok pusat grup di tabel `master_produk.stok_pusat` milik PT. A, stok katalog yang disinkronkan dari Data Center ke cabang, dan stok operasional per cabang/gudang pada `stock_locker`, `stock_locker_variant`, serta `stock_locker_supplies_variant`.
6. **Standar varian produk adalah inisiatif paling matang.** Mekanisme *dual-write* stok dengan sentinel `variant_id = 1`, *cart key* `variant:{produk_id}:{variant_id}`, dan tiga pilar (produk jadi, supplies, BOM produksi) sudah punya blueprint terpisah yang dinyatakan *single source of truth*. Status eksekusi terdokumentasi sampai tingkat file dan nomor baris kode.
7. **Dokumen paling mutakhir adalah `master-blueprint-design-variant-erp.md` (27 Juli 2026).** Namun status fase pada dokumen ini tertinggal dibanding tracker rollout, yang mencatat DDL sudah dieksekusi di server `192.168.5.10` pada 12-13 Juli 2026. Kedua sumber harus dibaca bersama: dokumen desain untuk *apa*, tracker untuk *sudah sampai mana*.
8. **Ada tiga mekanisme berbeda untuk hal yang sama: mutasi stok.** Ia diwujudkan sebagai trigger PostgreSQL di Cetak Biru, sebagai state `active`/`hold` pada tabel locker di tracker varian, dan sebagai prosedur Atomicity dengan `SELECT ... FOR UPDATE` plus larangan `UPDATE` pada nilai finansial di dokumen inventaris. Ketiganya belum disatukan.

---

## 2. Arsitektur Sistem

### 2.1 Peta Arsitektur terhadap Tiga Horizon

| Horizon | Sumber Dokumen | Stack | Peran |
|---|---|---|---|
| H1 - Target enterprise | Master Blueprint Vol 1-5 | Django, DRF, PostgreSQL, Redis, Celery, Next.js, TypeScript | Arsitektur acuan; tidak ada catatan implementasi di sumber |
| H2 - Konsolidasi grup | `Cetak_Biru_Multitenancy_Updated.md` | PostgreSQL (DDL, trigger, PL/pgSQL) | Order desk terpusat untuk PT. A dan lima sister company |
| H3 - Legacy yang diwariskan | `FLOWCHART_MODUL.md`, `workflow_hierarchy.md`, `BLUEPRINT_SYNC_PRODUK.md`, `blueprint-progress-tracker.md`, `BLUEPRINT_REPORTING_REFACTOR_DDD.md` | CodeIgniter 3 HMVC, PHP 5.6, MySQL/InnoDB | Sistem yang berjalan, direfactor bertahap |

### 2.2 Arsitektur Target: Modular Monolith, Bukan Microservices

Volume 1 menolak *microservices* untuk tahap awal dengan empat alasan: transaksi lintas modul masih rapat, tim belum cukup besar, kompleksitas operasional microservices terlalu tinggi, dan kebutuhan utamanya konsistensi bisnis bukan distribusi sistem.

Rekomendasi arsitekturnya: **modular monolith di backend, satu API, satu database utama** dengan skema yang didisiplinkan per domain. Kriteria mulai memecah layanan hanya berlaku bila empat syarat terpenuhi sekaligus: beban transaksi per domain sudah tinggi, kebutuhan deploy terpisah sudah nyata, tim sudah matang per domain, dan *bottleneck* arsitektur monolith sudah terbukti bukan asumsi.

### 2.3 Layering Backend Django

| Layer | Tanggung jawab |
|---|---|
| Models | Struktur data |
| Selectors / Queries | Pembacaan data |
| Services | Business logic dan transaksi |
| Permissions / Authorization | Hak akses |
| Serializers / DTO logic | Validasi transport layer |
| Views / ViewSets | Orkestrasi tipis |
| Tasks | Proses asinkron pada non-critical path |

Volume 4 menegaskan DRF permission bukan satu-satunya tempat otorisasi, dan service layer adalah tempat workflow berada. Base abstract model yang direkomendasikan adalah `TimeStampedModel`, `ActivatableModel`, `CodeNameModel`, dan `DocumentStatusMixin`, dengan peringatan agar mixin status tidak terlalu pintar sampai menyembunyikan aturan bisnis.

### 2.4 Layering Frontend Next.js

Sembilan lapisan yang direkomendasikan Volume 5: routing, layouts, page containers, domain components, table/form components, hooks, API clients, permission-aware navigation, dan state layer seperlunya. Arsitektur App Router, page pattern (list, detail, form, workflow action modal), serta *route guard* dan *action guard* yang sumbernya endpoint permission.

### 2.5 Bounded Context yang Direkomendasikan

Tujuh konteks dengan prinsip satu entitas punya satu rumah:

| Bounded Context | Pemilik | Entitas kunci menurut Volume 4 |
|---|---|---|
| Governance | Rule sistem, authorization, approval rule, delegasi, audit rule | ApprovalRule, DelegationRule, AuditLog, SystemPolicy (opsional) |
| Master Data | Master lintas modul | Company, BusinessUnit, Branch, Department, Warehouse, Vendor, Customer, Item, ItemCategory, UOM, Currency, TaxCode, PaymentTerm, CostCenter |
| Purchasing | Request sampai pemesanan | PurchaseRequest, PurchaseRequestLine, PurchaseOrder, PurchaseOrderLine, PurchaseApprovalRecord |
| Sales | Order sampai invoice | SalesOrder, SalesOrderLine, SalesDelivery, SalesApprovalRecord, SalesPricingOverrideRequest |
| Inventory | Pergerakan dan saldo stok | GoodsReceipt, GoodsIssue, StockTransfer, StockAdjustment, StockReservation, StockMovement, StockBalance beserta seluruh entitas *Line |
| Expense / Costing | Biaya operasional dan alokasi | ExpenseRequest, ExpenseRequestLine, ExpenseAllocation, CostAllocationRule |
| Finance | Jurnal, AR/AP, kas, rekonsiliasi, tutup periode | JournalEntry, JournalEntryLine, AccountsPayable, AccountsReceivable, Payment, Receipt, FiscalPeriod, ReconciliationRecord |

Aturan kepemilikan data yang ditegaskan: Purchasing boleh mereferensikan Vendor tetapi tidak boleh memiliki data Vendor; Finance mereferensikan Purchase Order dan Vendor tetapi tidak boleh mengubah logika purchasing; Inventory menerima efek dari PO dan SO tetapi tetap pemilik stok; Governance tidak mengedit transaksi bisnis, hanya mengatur aturannya.

### 2.6 Gaya API

RESTful dengan *action endpoint* untuk transisi workflow, bukan CRUD murni:

- resource: `GET`/`POST /api/purchase-requests/`, `GET`/`PATCH /api/purchase-requests/{id}/`
- action: `POST /api/purchase-requests/{id}/submit/`, `/approve/`, `/reject/`, `/cancel/`
- konvensi: plural noun, kebab-case, minimal versioning `/api/v1/`
- filtering list: page/page_size atau limit/offset, sorting, keyword search, filter status, filter date range, context filter sesuai permission

Contoh endpoint session dan permission: `POST /api/v1/auth/login/`, `POST /api/v1/auth/logout/`, `POST /api/v1/auth/refresh/`, `GET /api/v1/me/`, `GET /api/v1/me/profile/`, `GET /api/v1/me/permissions/`, `GET /api/v1/me/modules/`, `GET /api/v1/me/navigation/`. Bentuk respons `GET /me/permissions` memuat daftar role (contoh `PURCHASE_REQUESTER`, `PURCHASING_STAFF`) dan peta modul boolean per bounded context, sehingga frontend bisa menentukan navigasi tanpa menebak.

Contoh action endpoint lain pada sumber: `/sales-orders/{id}/confirm/`, `/journal-entries/{id}/post/`, `/goods-receipts/{id}/post/`.

### 2.7 Katalog Error (15 Kode)

- Teknis: `BAD_REQUEST`, `UNAUTHORIZED`, `PERMISSION_DENIED`, `NOT_FOUND`, `CONFLICT`, `INTERNAL_SERVER_ERROR`
- Bisnis: `INVALID_STATUS_TRANSITION`, `APPROVAL_RULE_NOT_FOUND`, `PERIOD_CLOSED`, `INSUFFICIENT_STOCK`, `SOURCE_DOCUMENT_INVALID`, `DUPLICATE_DOCUMENT`, `DOCUMENT_ALREADY_POSTED`, `DOCUMENT_ALREADY_CANCELLED`, `ROLE_CONFLICT_DETECTED`

Tujuannya agar frontend dapat membedakan apakah cukup menampilkan toast, menyorot field, melakukan redirect, me-refresh data, atau meminta akses lain.

### 2.8 Layer Legacy: Config-Driven Transaction Engine

Sistem lama tidak mendefinisikan alur di dalam controller, melainkan di file konfigurasi per modul:

| File | Isi |
|---|---|
| `heTransaksi_ui.php` | Kode transaksi, label, urutan langkah, grup pengotorisasi, `place` (center/branch), flag `allowJoin`, `paymentConfig`, `paymentSrc` |
| `coTransaksiValues.php` | `tableIn` (field master dan detail), `components` (jurnal/rekening), `postProcessor` (LockerStock), pre-processor |
| `heComponents.php` | Definisi reversibel pre-processor dan post-processor |
| `coTransaksiCore.php` | Feature flag dan daftar entry service per modul, termasuk tempat konfigurasi `singleVariantStandard` |

Konsekuensinya: Blueprint varian harus mendaftarkan entry baru di `coTransaksiCore.php`, bukan menyisipkan logika langsung ke dalam controller.

### 2.9 Tabel Transaksi Bersama

Semua modul transaksi memakai satu tabel master transaksi dengan struktur `tableIn` yang konsisten. Field master bersama (15 kolom): `jenis_master`, `jenis_top`, `jenis`, `jenis_label`, `div_id`, `div_nama`, `dtime`, `fulldate`, `oleh_id`, `oleh_nama`, `cabang_id`, `cabang_nama`, `transaksi_nilai`, `transaksi_jenis`, `keterangan`.

Field detail bersama (13 kolom): `dtime`, `produk_id`, `produk_kode`, `produk_label`, `produk_nama`, `variant_id`, `variant_sku`, `variant_label`, `variant_key`, `produk_ord_jml`, `produk_ord_hrg`, `satuan`.

Perhatikan bahwa `variant_id`, `variant_sku`, `variant_label`, dan `variant_key` sudah ada di level tabel detail bersama. Ini bukti bahwa standar varian mulai merambat ke kontrak data modul, bukan hanya berhenti di model PHP.

### 2.10 Layer Laporan: Read-Model Terdenormalisasi

Volume 5 dan blueprint DDD sama-sama repellent query laporan langsung ke tabel proyeksi terdenormalisasi (`__raw_rek_pembantu__*`), bukan ke aggregate root transaksional. Prinsipnya: aturan transaksi tetap hidup di bounded context masing-masing, sedangkan query agregasi untuk laporan dibaca lewat *read model* terpisah agar tidak melanggar batas kepemilikan data.

---

## 3. Peta Modul dan Dokumen Sumber

| Area atau Modul | Dokumen Sumber | Fokus | Peran dalam Sintesis |
|---|---|---|---|
| Fondasi arsitektur | `master_blueprint_enterprise_system_volume1.md` | Visi, layering, bounded context, model authorization | Kerangka berpikir target |
| Spesifikasi fungsional | `master_blueprint_enterprise_system_volume2.md` | Kosakata status, aturan workflow, fitur per modul | Kontrak perilaku dokumen |
| Matriks akses | `master_blueprint_enterprise_system_volume3.md` | Role family, authorization object, katalog action dan context, SoD | Kontrak otorisasi |
| ERD global | `master_blueprint_enterprise_system_volume4.md` | Delapan layer entitas, relasi lintas modul, contoh model Django | Kontrak data |
| Kontrak API dan frontend | `master_blueprint_enterprise_system_volume5.md` | Gaya endpoint, katalog error, struktur Next.js, guard | Kontrak integrasi |
| Multitenancy order desk | `Cetak_Biru_Multitenancy_Updated.md` | Enam entitas grup, DDL PostgreSQL, cost-plus, trigger, RBAC | Model bisnis grup terbaru |
| Alur dokumen dan database | `FLOWCHART_MODUL.md` | 23 kode transaksi aktif, cross-module flow, field dan jurnal per modul | Peta sistem legacy |
| Workflow UI | `workflow_hierarchy.md` | Kode transaksi, hit log, langkah, grup akses, output state | Bukti implementasi workflow |
| Standar inventaris | `inventory_matrix.md` | Zonasi gudang, matriks M:M, ACID, locking, dual key, multi-database | Standar data dan quality gate |
| Storage dan cut-off | `ringkasan_diskusi_database_erp.md` | Monolithic versus modular, risiko storage, strategi cut-off | Keputusan operasional storage |
| Struktur data | `hirarki murni dan matrix.md` | Hierarki murni (COA) versus matriks fleksibel (sparepart) | Prinsip pemodelan data |
| Sinkronisasi katalog | `BLUEPRINT_SYNC_PRODUK.md` | API DC ke cabang, smart diff, advisory lock, auto-sync | Mekanisme distribusi master |
| Rollout varian | `blueprint-progress-tracker.md` | Dual-write stok, sentinel, UAT, SQL rekonsiliasi, jurnal harian | Status implementasi varian |
| Standar laporan | `BLUEPRINT_REPORTING_REFACTOR_DDD.md` | DDD dan CQRS read-model, `MonthlyMatrixFormatter`, quality gate | Standar refaktor reporting |
| Master design varian | `master-blueprint-design-variant-erp.md` | Tiga pilar varian, inherit-or-override BOM, roadmap | Dokumen paling mutakhir |

---

## 4. Detail Per Bagian

### 4.1 Fondasi dan Prinsip Besar

Volume 1 menyatakan delapan prinsip besar yang menjadi rujukan kritis setiap keputusan desain:

1. Jangan mulai dari layar, mulai dari domain bisnis.
2. Jangan mulai dari menu, mulai dari authorization object.
3. Jangan tempel permission ke user, tempel ke role.
4. Jangan anggap frontend sebagai pengaman utama.
5. Jangan membangun modul seolah berdiri sendiri; pikirkan integrasi sejak awal.
6. Jangan mengejar "selesai cepat" dengan mengorbankan fondasi data dan kontrol.
7. Jangan membuat governance sebagai tempat "admin sakti".
8. Jangan mengunci desain untuk tiga modul saja; rancang agar bisa tumbuh.

Karakteristik sistem yang dituju: modular, aman, auditable, scalable, context-aware, mendukung approval, mendukung multi-role user, dan siap tumbuh dari tiga modul awal menjadi platform enterprise utuh. Target organisasinya perusahaan menengah-besar, multi unit, dengan omzet tahunan sekitar triliunan rupiah.

Cakupan bertahap: tahap awal Purchasing dan Master Data; tahap ekspansi menambah Inventory dan Finance; tahap lanjutan menambah Expense/Costing dan Selling. Yang tidak dibahas mendalam di Volume 1 adalah detail endpoint dan kode final, dan itu disengaja karena blueprint mendahulukan fondasi dan domain sebelum implementasi.

Prinsip yang berlaku lintas volume: **database transaksi operasional adalah sumber kebenaran**. Laporan dan cache boleh menyederhanakan, tetapi tidak boleh menggantikan kebenaran transaksi. Karena itu definisi status harus jelas, field context harus ada, referensi master harus disiplin, audit tidak boleh diabaikan, dan side effect transaksi harus tercatat.

### 4.2 Kosakata Status dan Aturan Workflow

Volume 2 menetapkan sembilan status kanonik dengan definisi tegas:

| Status | Definisi |
|---|---|
| Draft | Belum final, masih bisa diubah pihak berwenang |
| Submitted | Sudah diajukan ke proses berikutnya, tidak boleh diedit bebas seperti draft |
| Under Review | Sedang dalam proses peninjauan atau approval |
| Approved | Disetujui untuk dipakai sebagai dasar proses berikutnya |
| Rejected | Ditolak, biasanya kembali butuh revisi atau dihentikan |
| Cancelled | Dibatalkan secara resmi, tidak boleh dipakai untuk proses lanjutan |
| Closed | Dianggap selesai dan tidak lagi aktif untuk proses lanjutan normal |
| Posted | Sudah menjadi catatan resmi yang memengaruhi ledger atau kontrol final |
| Reversed | Efek transaksi resmi sudah dibalik melalui mekanisme yang sah |

Aturan umum yang berlaku: status hanya boleh berpindah melalui aksi yang valid; setiap perpindahan status harus tercatat; dokumen yang sudah diapprove tidak boleh diedit bebas; dokumen yang sudah dipakai modul lain memiliki kontrol pembatalan lebih ketat; side effect hanya boleh terjadi dari service layer backend; approval tidak boleh hanya mengandalkan visibilitas tombol di frontend.

Pola fungsi minimum per objek bisnis: create, update draft, submit, approve/reject, cancel, view detail, list/search, attach documents/notes, history/timeline, dan print/export bila relevan.

Pemisahan status yang paling penting secara operasional ditegaskan di Volume 3: `APPROVE` berbeda dari `UPDATE`, `POST` berbeda dari `APPROVE`, `CANCEL` berbeda dari `DELETE`, dan `REVERSE` bukan edit balik melainkan tindakan resmi pembalikan.

### 4.3 Model Authorization Enterprise

#### Rantai dan Prinsip

`User -> Role -> Authorization Object -> Action -> Context`, dengan tiga prinsip dari Volume 3:

- *Union of permission, filtered by context*: hak akses adalah gabungan seluruh role aktif, lalu disaring oleh rule context.
- *Menu follows permission*: menu adalah turunan permission, bukan sumbernya.
- *Default deny*: yang tidak diizinkan berarti tidak boleh.

Authorization object bukan menu. Object mewakili kapabilitas bisnis seperti purchase order, goods receipt, atau journal entry, sehingga perubahan nama menu tidak mengubah hak akses. Setiap entitas juga harus menyimpan *field context* sejak awal agar aturan row-level dapat dievaluasi secara deterministik di backend.

#### Role Family dan Definisi Role

Delapan role family: Governance, Master Data, Purchasing, Sales, Inventory, Expense/Costing, Finance, dan Cross-Cutting. Volume 3 mendefinisikan detail 21 role, antara lain Governance Admin, Security Admin, Auditor, Master Data Viewer, Vendor Master Admin, Item Master Admin, Purchase Requester, Purchasing Staff, Purchasing Supervisor, Purchasing Manager, Sales Admin, Sales Supervisor/Manager, Warehouse Staff, Inventory Controller, Expense Submitter, Cost Controller, AP Officer, AR Officer, GL Officer, Treasury Staff, dan Finance Manager.

#### Action Catalogue (29 Kode dalam 5 Kelompok)

| Kelompok | Action |
|---|---|
| Umum | CREATE, READ, UPDATE, DELETE, LIST, EXPORT |
| Workflow | SUBMIT, APPROVE, REJECT, CANCEL, CLOSE, REOPEN, CONFIRM |
| Operasional | ISSUE, RECEIVE, RESERVE, RELEASE, ALLOCATE, TRANSFER, ADJUST |
| Finansial | POST, REVERSE, PAY, RECEIVE_PAYMENT, RECONCILE |
| Governance | ASSIGN, DELEGATE, ACTIVATE, DEACTIVATE |

Pemakaiannya: `READ` untuk detail, `LIST` untuk daftar, `APPROVE` bukan `UPDATE`, `POST` bukan `APPROVE`. Frontend boleh memakai katalog ini untuk show/hide tombol, enable/disable aksi, dan guard halaman, tetapi backend tetap final authority.

#### Context Catalogue (13 Kode)

`company_code`, `business_unit_code`, `branch_code`, `department_code`, `warehouse_code`, `cost_center_code`, `sales_org_code`, `currency_code`, `fiscal_period`, `owner_user_id`, `vendor_group`, `customer_group`, `item_category_code`.

Polanya ada enam: EQUALS, IN, NOT_EQUALS, OWNS_RECORD, SAME_BUSINESS_UNIT, SAME_COMPANY. Untuk fase awal, EQUALS dan IN sudah cukup kuat. Prinsip restraint yang ditegaskan: gunakan hanya context yang punya nilai bisnis, jangan menambah context berlebihan sejak awal, dan pastikan field-nya benar-benar ada di data transaksi maupun master.

#### Segregation of Duties

Volume 3 menyodorkan 8 aturan SoD awal beserta bentuk implementasi minimal, yaitu memblokir kombinasi role pada objek yang sama, dan bentuk lanjutan berupa deteksi konflik saat assignment role. Area kebijakan yang dirinci: Draft Ownership Policy, Cross-Unit Policy, Viewer Policy, Export Policy, Approval Policy, dan Posting Policy. Volume 1 menambah pola *temporary assignment* dan delegasi supaya proses approval tetap berjalan tanpa memindahkan hak akses secara permanen.

#### RBAC pada Cetak Biru Multitenancy

Cetak Biru memakai pendekatan lebih sederhana: daftar role terdefinisi plus *query filter otomatis* berbasis `id_perusahaan`, sehingga setiap user hanya melihat dan mengubah baris perusahaannya. Secara konsep ini konsisten dengan *filtered by context*, tetapi tidak sehalus `RoleContextRule` yang menyimpan operator dan nilai context sebagai data.

### 4.4 Model Data: Delapan Layer Entitas

Volume 4 mengelompokkan seluruh entitas dalam delapan layer (A sampai H) seperti tabel pada Bagian 2.5, dengan prinsip relasi global:

- transaksi operasional mereferensikan master;
- transaksi turunan mereferensikan transaksi sumber;
- finance mereferensikan sumber operasional tanpa mengambil alih *ownership* domain;
- inventory memegang *ownership* movement stok;
- governance tidak mengedit transaksi bisnis, hanya mengatur rule yang memengaruhinya.

Relasi lintas modul yang wajib dijaga:

1. Purchasing ke Inventory: PurchaseOrder menjadi acuan GoodsReceipt, PurchaseOrderLine menjadi acuan GoodsReceiptLine, GRN memengaruhi StockMovement dan StockBalance.
2. Sales ke Inventory: SalesOrder dan SalesDelivery menjadi acuan GoodsIssue, GoodsIssue memengaruhi StockMovement dan StockBalance, SalesOrder dapat memicu StockReservation.
3. Purchasing dan Expense ke Finance: alur pembelian atau biaya dapat menghasilkan AccountsPayable, AP menjadi sumber Payment, dan AP dapat memicu JournalEntry sesuai desain.
4. Sales ke Finance: alur penjualan dapat menghasilkan AccountsReceivable, AR menjadi sumber Receipt, dan AR dapat memicu JournalEntry.
5. Inventory ke Finance: pada fase lanjutan movement tertentu dapat menghasilkan valuasi atau jurnal; pada fase awal integrasi ini dibuat lebih sederhana.
6. Governance ke semua modul: approval rule dan authorization berlaku lintas modul, audit log dapat merekam semua transisi penting.

Contoh model yang ditetapkan: `User` mewarisi `AbstractUser` dengan `USERNAME_FIELD = "email"`, `Role` memetakan ke `db_table = "roles"` dengan `ordering = ["name"]`, dan `AuthorizationObject` memiliki `MODULE_CHOICES` tujuh bounded context. `ApprovalRule`, `DelegationRule`, dan `AuditLog` diletakkan di app governance terpisah.

### 4.5 Alur Dokumen pada Sistem Legacy

#### Daftar Modul Aktif (23 Entri, berbasis `heTransaksi_ui.php`)

| Kode | Modul | Label | Place |
|---|---|---|---|
| 466 | `pembelian` | FG Purchasing | center |
| 463 | `pembelianjasa` | Service Purchasing | center |
| 583 | `distribusi` | FG Distribution | center |
| 585 | `distribusi` | FG Stock Reception | branch |
| 5822 | `penjualan` | Sales Support Mode | branch |
| 489 | `pembayaran` | FG A/P Payment | center |
| 462 | `pembayaran` | Service A/P Payment | center |
| 477 | `pembayaran` | Expense Payment (Biaya Usaha Cabang) | center |
| 1483 | `pembayaran` | PPh 21 A/P Payment | center |
| 1485 | `pembayaran` | Hutang Gaji A/P Payment | center |
| 1487 | `pembayaran` | BPJS A/P Payment | center |
| 749 | `penerimaan` | A/R Receipt | branch |
| 1757 | `kas` | Cash Balance Interchange (Pusat) | center |
| 677 | `biaya` | Biaya Usaha (Cabang) | branch |
| 2677 | `biaya` | Otorisasi Biaya Usaha (Cabang) | center |
| 1674 | `biaya` | Salary Expense (Branch) | center |
| 2674 | `biaya` | Salary Expense (Pusat) | center |
| 111 | `taxes` | Realisasi PPN Masukan | center |
| 110 | `taxes` | E-Faktur PPN Keluaran | center |
| 5683 | `taxes` | PPh 29 | center |
| 117 | `taxes` | PPh 25 | center |
| 118 | `taxes` | PPh Pasal 4(2) | center |
| 116 | `taxes` | Bukti Bayar PPh 23 | center |

Empat modul hanya memiliki config atau helper tanpa controller aktif: `addons`, `diskon`, `valas`, dan `umproject`.

#### Cross-Module Connections

| Dari | State | Ke | State | Keterangan |
|---|---|---|---|---|
| `pembelianjasa` (463) | SRN | `taxes` | 113 PPN Masukan | Realisasi PPN Masukan dari pembelian jasa |
| `pembelian` (466) | 467 GRN | `taxes` | 111 PPN Masukan | COMMENTED, PPN digabung dengan A/P |
| `distribusi` (583) | 583 Authorized | `distribusi` (585) | 585r Init | Barang dikirim pusat lalu diterima cabang |
| `penjualan` (5822) | 582 Invoice | `penerimaan` (749) | 749 | Piutang penjualan dibayar konsumen |
| `pembelian` (466) | 467 GRN | `pembayaran` (489) | 489 | Hutang supplier dibayar |
| `pembelianjasa` (463) | SRN | `pembayaran` (462) | 462 | Hutang vendor jasa dibayar |
| `biaya` (677/2677) | 677r/2677 | `pembayaran` (477) | 477 | Biaya usaha dibayar |
| `biaya` (1674/2674) | 1674/2674 | `pembayaran` (1485) | 1485 | Gaji dibayar |

Empat catatan konfigurasi yang mengubah cara flow dibaca:

- `allowJoin=true` pada step 4 (466 ke 467) dan step 5 (5822spd ke 582) mengizinkan penggabungan beberapa dokumen sumber ke satu dokumen target.
- `paymentConfig=true` pada 489, 462, 477, 749, 1483, 1485, dan 1487 menandai transaksi yang terintegrasi dengan modul kas atau bank.
- `paymentSrc` pada step 3 penjualan (5822so ke 5822pkd) memeriksa apakah uang muka sudah diterima sebelum packing.
- `place` menentukan lokasi, dengan `center` untuk Pusat atau DC dan `branch` untuk Cabang.

#### Contoh Konfigurasi Modul `pembelian`

File `application/modules/pembelian/config/coTransaksiValues.php`. Master field unik: `suppliers_id` sebagai foreign key ke tabel `suppliers`, `suppliers_nama`, `gudang_id` sebagai foreign key ke tabel `gudang`, dan `gudang_nama`. Detail field unik: `produk_ord_hrg` dari gate `harga` sebagai harga beli atau HPP awal, serta `hpp` yang dihitung melalui *valueBuilder* setelah PPV. Nilai statis: `detail.produk_jenis = "produk"` untuk menandai produk riil.

Komponen jurnal pada step 467 GRN:

| Account Code | Nama Akun | Debit/Kredit | Sumber Nilai |
|---|---|---|---|
| 010306 | Persediaan Produk Riil | Debit | `harga` |
| 01040100005 | PPN In (Belum Faktur) | Debit | `nilai_tambah_ppn_in` |
| 020101 | Hutang Dagang | Kredit | `nilai_tambah_piutang_pembelian` |
| 010203 | Piutang Pembelian | Kredit | `-nilai_dipakai_piutang_pembelian` |

Komponen jurnal pada step 467 PPV Transfer:

| Account Code | Nama Akun | Debit/Kredit | Sumber Nilai |
|---|---|---|---|
| 010304 | Persediaan Produk (Std) | Debit | `hpp_nppv` |
| 010306 | Persediaan Produk Riil | Kredit | `-harga` |
| 020407 | Hutang Lain PPV | Kredit | `ppv` |

Rekening pembantu `RekeningPembantuSupplier` melacak hutang per supplier pada akun `020101`, `010203`, dan `01040100005`. Pre-processor yang terdaftar: `ProdukSerialNumberExtractor` untuk mengekstrak nomor seri saat GRN, `LockerValue` untuk mengunci nilai `ppn_in` dan `piutang_pembelian`, `SyncDiskonPembelian`, serta `LockerStockFreeProduk`.

Pola loading model pada sistem legacy terdiri atas lima: config-driven melalui `coTransaksiUi.php`, controller-driven lewat `load->model`, dynamic component loading, pre-processor loading, dan cross-module loading.

### 4.6 Hierarki Workflow UI dan Bukti Penggunaan Nyata

Dokumen ini memetakan alur kerja manusia dari konfigurasi `coTransaksiUi.php` yang dikonfirmasi silang dengan tabel `log`. Format setiap entri: kode transaksi, nama, jumlah log aktif, lalu langkah demi langkah dengan grup pengotorisasi dan *output state*.

#### Pola Transaksi yang Terverifikasi

| Kode | Nama | Log | Alur Singkat |
|---|---|---|---|
| 677 | Biaya usaha (cabang) | 1.536 | request `o_kasir` (677r) lalu authorization `o_finance` (677) |
| 110 | E-faktur PPN keluaran | 725 | prepare `c_finance` (110r), entry `c_finance` (110e), otorisasi `c_finance_spv` (110) |
| 1675 | Biaya umum (pusat) | 261 | request `o_kasir` (1675r) lalu authorization `o_finance` (1675) |
| 2677 | Otorisasi biaya usaha | 355 | request `c_finance` (2677r) lalu authorization `c_finance` (2677) |
| 671 | Pettycash (branch) | 131 | pettycash `o_kasir` (671r) lalu authorization `o_kasir` (671) |
| 761 | Supplies request (branch) | 120 | supplies request `w_gudang` (761r) lalu authorization `w_gudang` (761) |
| 1757 | Cash balance interchange (pusat) | 99 | request `c_holding` (1757r) lalu authorization `c_holding` (1757) |
| 776 | Product assembling dan BOM | 112 | request `p_produksi` (776r), authorization work in process `p_produksi_spv` (776a), process assembling `p_produksi_spv` (776) |
| 771 | Pettycash refill (branch) | 36 | refill pettycash `c_finance` (771) |
| 587 | Destock ke gudang lain | 36 | request `o_gudang` (587r), authorization `o_gudang_spv` (587ra), destock reception (587) |
| 5681 | PPh 22 | 53 | PPh 22 taxes `c_finance` (5681r) lalu authorization `c_finance` (5681) |
| 681 | Taxes | 71 | request taxes `c_finance` (681r) lalu taxes authorization `c_finance` (681) |
| 1677 | Biaya usaha (pusat) | 61 | request `o_kasir` (1677r) lalu authorization `o_finance` (1677) |
| 1674 | Salary expense | 52 | request `c_holding` (1674r) lalu authorization `c_holding` (1674) |
| 2676 | Otorisasi biaya produksi | 34 | request `sys` (2676r) lalu authorization `c_finance` (2676) |
| 762 | Pembiayaan supplies | 42 | pembiayaan supplies `c_holding` (762r) lalu approval `c_holding` (762) |
| 8787 | Penyusutan aset berwujud | 23 | request `sys` (8787r) lalu authorization `o_finance` (8787) |
| 421 | Aset purchasing | 11 | request `c_purchasing` (421r), authorization `c_purchasing_spv` (421), goods receive note `c_gudang` (423), realisasi PPN masukan `c_finance` (111) |
| 999 | Adjustment journaling | 6 | adjustment journaling `root` (999) |
| 116 | Bukti bayar PPh 23 | 1 | entry e-faktur `sys` (116r) lalu approval e-faktur `c_finance` (116) |
| 117 | PPh 25 | 1 | request `c_finance` (117r) lalu authorization `c_finance_spv` (117) |
| 7762 | Pembiayaan supplies (receipt number) | 3 | pembiayaan supplies `c_holding` (7762r) lalu approval `c_holding` (7762) |

Contoh alur empat langkah pada transaksi `421` menunjukkan pola umum yang berulang: purchase request disetujui oleh purchasing, GRN diproses gudang, lalu realisasi PPN masukan dicatat keuangan. Tiga bounded context bekerja pada satu rantai dokumen.

Kode `776` juga muncul di dua konteks berbeda: sebagai *product assembling* tiga langkah di modul `PRODUKSI`, dan sebagai *BOM* dua langkah di modul `PRODUKSIPROSES` dengan grup `c_holding`. Kode yang sama dengan alur berbeda menandakan bahwa klasifikasi proses ada di level config, bukan di level kode transaksi.

#### Kode dengan Nol Log

Banyak entri konfigurasi belum pernah dipakai di lapangan, antara lain `9999` adjustment neraca, `9990` jurnal umum, `1672` dan `1771` pettycash pusat, `1587` dan `1687` pindah gudang pusat, `7761` sampai `7765` produksi PH-1 sampai PH-5, `6666` perubahan kepemilikan saham, `383` valas exchange, dan `118` PPh Pasal 4(2). Ini penting untuk prioritas UAT: konfigurasi ada, perilaku belum pernah teruji.

#### Indikasi Modul Bayangan

Nama modul berduplikasi dengan sufiks atau stempel tanggal: `PINDAHGUDANG_` (duplikat `PINDAHGUDANG`), `PRODUKSI_SEBELUM_GESER_BOM` (duplikat `PRODUKSI`), dan `TAXES_24APR2026` (duplikat `TAXES`). Selain `776` yang identik 112 hit, ada satu perbedaan material: pada `PINDAHGUDANG_` langkah destock reception diarahkan ke `w_gudang`, sedangkan pada `PINDAHGUDANG` ke `o_gudang`. Modul bayangan seperti ini adalah kandidat kuat untuk dibersihkan atau diarsipkan agar tidak menjadi dua sumber kebenaran.

### 4.7 Standar Inventaris, Matriks Produk, dan Quality Gate

Standar ini menyasar ERP multi-company, multi-branch, high-concurrency dengan target omset Rp 1 triliun dan ratusan ribu item sparepart, tunduk pada ISO 9001:2015 untuk mutu dan ketertelusuran serta ISO/IEC 27001 untuk keamanan informasi.

#### Zonasi Fisik Gudang

- **Zona Penerimaan:** pembongkaran muatan, QC Gate, dan pencocokan manifes.
- **Zona Penyimpanan Utama:** dibagi dengan Analisis ABC, yaitu fast-moving di koridor terdekat area pengiriman, medium-moving di baris tengah, dan slow-moving di area paling belakang atau mezzanine.
- **Zona Karantina:** area terisolasi dengan marka batas merah fisik untuk menampung retur, barang rusak, atau salah kirim agar tidak bercampur dengan stok komersial.
- **Zona Pengemasan dan Pengiriman:** pengecekan akhir, packing, dan serah terima ke kurir atau pelanggan.

#### Klasifikasi Berlapis dan Matriks Many-to-Many

Empat layer klasifikasi: kategori utama seperti Mesin, Kaki-kaki, dan Elektrikal; sub-kategori seperti Sistem Pendingin dan Sistem Bahan Bakar; jenis komponen seperti Radiator, Water Pump, dan Thermostat; lalu atribut spesifik seperti ukuran, material, berat, dan dimensi.

Pencarian konvensional dengan `SQL LIKE` dinyatakan tidak valid untuk ratusan ribu item. Yang dipakai adalah Relation Matrix yang memisahkan data barang fisik dari data kesesuaian kendaraan. Contoh pada sumber: part number `PAD-001` untuk Kampas Rem dipetakan ke tiga tipe kendaraan, yaitu Avanza (2012-2019), Xenia (2015-2021), dan Rush (2018-sekarang). Pencarian dilengkapi *fuzzy search* yang toleran terhadap salah ketik operator dan *faceted navigation* dengan filter multivariabel.

#### Aturan Transaksi dan Kontrol Konkurensi

- Pola header-detail: `transaction_header` untuk satu baris umum dan `transaction_detail` untuk banyak baris item, semuanya dibungkus blok transaksi terpadu.
- Jurnal keuangan otomatis dengan pemetaan *many-to-one* ke COA. Contoh pada sumber memakai akun `1101` Kas Toko dan `4101` Pendapatan.
- Pemeriksaan berlapis dari format input di frontend sampai ketersediaan stok riil di backend sebelum perintah `INSERT` dijalankan.
- Penguncian baris stok dengan pola `SELECT ... FOR UPDATE` untuk mencegah dua kasir menjual satu sisa barang terakhir pada detik yang sama, yang akan menghasilkan stok minus.
- `UPDATE` hanya boleh mengubah status alur dokumen, misalnya dari `PACKING` ke `SHIPPED`. `UPDATE` dan `DELETE` dilarang keras pada nilai finansial, kuantitas, atau riwayat transaksi yang sudah sah. Semua koreksi wajib memakai `INSERT` baru bermuatan nilai minus sebagai jurnal pembalik atau retur demi menjaga *audit trail*.

#### Dual Key dan Arsitektur Multi-Database

Dua pengenal per baris data. Pertama, *technical key* berupa `BIGINT` dengan klausa `GENERATED ALWAYS AS IDENTITY` yang mencatat urutan kronologis masuknya data, meningkatkan kecepatan indeksasi pencarian massal, dan tidak boleh diekspos ke publik demi alasan keamanan ISO 27001. Kedua, *business key* berupa nomor dokumen terbaca seperti `INV/2026/07/001` yang dipakai operator, manajemen, dan pelanggan.

Pada arsitektur *multi-database*, tiap anak perusahaan memiliki basis data yang terisolasi secara fisik demi performa lokal maksimal. Tanggung jawab integrator%: membangun pipa data dan antrean asinkron dengan message broker seperti Kafka atau RabbitMQ sehingga cabang tetap bisa bertransaksi secara luring dan data disinkronkan otomatis saat jaringan pulih; menyusun *composite key* gabungan `ID_Perusahaan + ID_Increment_Lokal` di server pusat agar data cabang tidak bertabrakan saat digabung untuk laporan manajemen; serta menyediakan mesin pivot untuk analisis perputaran stok, profitabilitas merek, dan laporan keuangan konsolidasi grup secara instan.

### 4.8 Struktur Data: Hierarki Murni versus Matriks Fleksibel

Dokumen ini menjawab pertanyaan kapan memakai pohon kaku dan kapan memakai jaringan.

| Parameter | Biologi dan COA | Sparepart dan Substitusi |
|---|---|---|
| Model struktur | Pohon berjenjang (*strict tree*) | Jaringan atau matriks (*network/flat*) |
| Sifat relasi | One-to-many, satu induk pasti | Many-to-many, bisa banyak induk |
| Desain penomoran | Smart ID berlevel `110100001` | ID sekuensial flat `SKU-8439` |
| Tujuan utama | Ketertiban, akurasi, kepatuhan | Kecepatan dan kemudahan pencarian |
| Teknologi query | Pemotongan karakter teks (`LEFT`) | Indeks terbalik (inverted index atau tag) |

Hierarki murni dicontohkan pada COA: level 1 akun utama `1` Aset, level 2 sub-akun `110` Aset Lancar, level 3 akun perkiraan `11010` Piutang Dagang, level 4 rekening pembantu `110100001` Piutang PT. Abadi Jaya. Aturannya: saldo atau mutasi milik PT. Abadi Jaya wajib menginduk 100% pada akun Piutang Dagang agar laporan keuangan tetap seimbang. Analogi yang dipakai adalah taksonomi Carl Linnaeus, di mana Harimau Sumatra hanya bisa masuk satu genus, yaitu `Panthera`, dan tidak mungkin sekaligus masuk genus `Canis`.

Bahaya pemaksaan hierarki pada sparepart dijelaskan dengan skenario baut: bila baut disimpan sebagai `Toyota -> Kijang 1997 -> Mesin -> Baut Karter (BT-001)`, maka ketika baut yang sama cocok untuk Isuzu Panther, sistem terpaksa membuat ID baru `Isuzu -> Panther 2000 -> Mesin -> Baut Karter (BT-002)`. Akibatnya ledakan duplikasi ID palsu, stok gudang kacau, dan pencarian substitusi barang menjadi lambat.

Solusinya flat SKU plus tagging: satu `SKU-8439` untuk baut fisik di rak, dihubungkan ke banyak variabel seperti Toyota Kijang 1997, Daihatsu Xenia 2015, Isuzu Panther 2000, dan tag fungsi Mesin.

Kesimpulan implementasi: dua mesin database dengan tugas berbeda. Mesin hierarki murni untuk COA keuangan dan rekening pembantu agar jurnal tertib, rapi, dan mudah digabung berlevel ke atas. Mesin matriks fleksibel untuk pencarian produk dan substitusinya agar kasir dengan informasi terbatas menemukan barang secara instan, dengan target di bawah 50 milidetik, hanya dengan mengetik kalimat samar seperti "baut mesin kijang 1997".

### 4.9 Diskusi Storage dan Strategi Database Cut-Off

Perbandingan dua pendekatan yang dianalisis:

| Database | Pendekatan | Ciri |
|---|---|---|
| `san_30mar` | Monolithic | Satu tabel raksasa global `transaksi_data_registry`, sekitar 60 kolom, mayoritas bertipe `longblob`, menampung seluruh data transaksi dari 50+ modul |
| `san_tsa_28agu` | Modular atau Domain-Driven | Tabel dipecah berdasarkan domain modul, contoh `pembelian_transaksi_data_registry`, sangat ramping sekitar 11 kolom, menyimpan registry spesifik per divisi |

Menurut ISO 25010 dan praktik rekayasa perangkat lunak, arsitektur modular dinilai jauh lebih unggul karena menyelesaikan masalah *lock contention* atau antrean baca tulis,whomidable pemeliharaan, dan memisahkan keamanan data antar divisi sebagai *bounded context*.

Namun migrasi penuh dari monolithic ke modular untuk 50+ modul diperkirakan memakan 6 sampai 12 bulan dan sangat berisiko. Karena itu solusi yang dipilih adalah **Database Cut-Off**, misalnya dengan membuat database `san_1jan26`, dengan empat langkah:

1. Hitung dan identifikasi: menarik data saldo akhir dan transaksi yang masih gantung, yaitu belum lunas atau belum dikirim, dari `san_30mar`.
2. Pembuatan database baru: membuat database kosong dan bersih.
3. Injeksi saldo awal: memasukkan transaksi gantung ke database baru sebagai saldo awal atau *starting balance*.
4. Peralihan atau *cut-over*: mengubah koneksi di `application/config/database.php` ke database baru, lalu mengarsipkan atau mengompres database lama.

Estimasi waktu 1 sampai 2 bulan, dengan catatan penting bahwa waktu terlama bukan dihabiskan untuk koding, melainkan untuk rapat koordinasi, pengecekan, dan validasi saldo awal bersama 50+ perwakilan divisi seperti Gudang, Keuangan, dan HR, agar data 100% akurat sebelum eksekusi (*dry run*).

#### Usulan Kolom `is_active` yang Ditolak

Usulannya menambah kolom penanda active atau inactive langsung ke `san_30mar.transaksi_data_registry` untuk menghindari pencarian manual saat proses cut-off. Tiga risiko teknis yang membuat usulan ini ditolak:

1. **Downtime ekstrem:** eksekusi `ALTER TABLE ADD COLUMN` pada tabel berukuran ratusan gigabyte yang penuh `longblob` dapat mengunci tabel selama berjam-jam sehingga ERP lumpuh total.
2. **Menghanguskan keuntungan tanpa koding:** jika kolom baru dibuat, seluruh logika pemrograman di 50+ modul wajib dibongkar agar aplikasi secara disiplin memperbarui nilai status setiap kali transaksi selesai.
3. **Backfill data lama:** tetap diperlukan skrip masal sekali untuk mengisi status pada jutaan transaksi historis yang sudah tersimpan.

Alternatif terbaik yang disarankan adalah membuat **Database View** di level MySQL. View ini menyaring otomatis transaksi yang masih gantung berdasarkan flag atau status bawaan yang sudah ada di masing-masing modul, tanpa menyentuh struktur tabel asli maupun kode aplikasi.

### 4.10 Sinkronisasi Produk antara Data Center dan Cabang

Dokumen ini menjelaskan sistem sinkronisasi katalog produk dan struktur harga antara Data Center di `192.168.11.100` dan database cabang lokal di `192.168.5.14`.

#### Masalah Awal yang Telah Diselesaikan

1. **Kebocoran produk nonaktif:** produk yang sudah di-*trash* di Data Center, seperti ID 1731 Fire Gloves, tetap aktif di cabang karena nilainya di-*hardcode* `status = 1` dan `trash = 0` pada model cabang, sementara API Data Center memfilter produk *trashed*.
2. **Penumpukan sampah produk baru:** produk *trashed* baru di Data Center sebelumnya tetap di-*insert* ke cabang lokal.
3. **Pemborosan *blind update* 1.642 baris:** setiap sinkronisasi manual menjalankan `UPDATE` pada seluruh 1.642 produk lokal meski tidak ada perubahan data atau harga, sehingga membuang resource CPU dan I/O server serta memicu reload halaman.
4. **Notifikasi kaku:** istilah teknis database seperti `inserted`, `updated`, dan `skipped: 114` membingungkan operator kasir.
5. **Kebutuhan auto-sync:** diperlukan moda sinkronisasi terjadwal di latar belakang yang tidak memblokir antarmuka kasir dan tidak merusak alur pemilihan multi-cabang saat login.

#### Komponen dan Berkas

| Komponen | Berkas | Peran |
|---|---|---|
| API Server DC | `z:\san\application\controllers\eusvc\Products.php` | Menyajikan data produk dan harga DC, method `seeItemAll_get` |
| Model engine cabang | `w:\san_sarana_30sep\application\models\Mdls\MdlProduk.php` | Eksekutor inti `syncApiData()`, smart diff, proteksi trash, update selektif `produk` dan `harga_produk` |
| Controller cabang | `w:\san_sarana_30sep\application\modules\statik\controllers\Data.php` | Endpoint `syncro_data` dan `auto_sync_check`, advisory lock |
| Job logger | `w:\san_sarana_30sep\application\models\Mdls\MdlSyncJob.php` | Riwayat eksekusi pada tabel `sync_jobs`, `getLastFinishedJob()` |
| Konfigurasi global | `w:\san_sarana_30sep\application\config\config.php` | `$config['sync_produk_cooldown_minutes'] = 120;` |
| Helper global | `w:\san_sarana_30sep\application\helpers\he_misc_helper.php` | `get_sync_cooldown_minutes()` dan `get_sync_cooldown_label()` |
| Dashboard | `w:\san_sarana_30sep\application\controllers\Welcome.php` dan `views\welcome.php` | Memeriksa `$hasCabang`, mencegah auto-sync prematur, menampilkan card status sinkronisasi dan alur `MiniDesk` |

#### Mekanisme Inti

- **Pembukaan status dan trash:** filter lama `status=1` dan `trash=0` dinonaktifkan sehingga produk nonaktif tetap dikirimkan. Kolom `status` dan `trash` dimasukkan ke daftar `$manualFields` agar tidak terpotong `getFields()`. Contoh hasil: ID 1731 mengirim payload `"status":"0","trash":"1"`.
- **Proteksi masuknya produk trash baru:** pada baris 2496 sampai 2503 `MdlProduk.php`, jika produk belum ada di cabang dan Data Center mengirim `trash == 1`, data langsung dilewati dengan `skipped++` dan tidak di-*insert* ke `produk` maupun `harga_produk`.
- **Smart diff check:** pada baris 2504 sampai 2577, sebelum mengeksekusi `UPDATE`, sistem membandingkan kolom lokal dengan kiriman Data Center. Tabel `produk` dibandingkan seluruh field kecuali `id` dan `last_update`. Tabel `harga_produk` dibandingkan nilai harga dengan toleransi `abs($lokal - $dc) > 0.0001`, status, dan trash. Jika sama, `UPDATE` tidak dijalankan dan angka `updated` tetap 0.
- **Proteksi konkurensi:** menggunakan `SELECT GET_LOCK('mdlproduk_{toko_id}_{cabang_id}', 0)`. Jika sinkronisasi sedang berlangsung, request baru tidak menumpuk beban query, tetapi langsung mendapat respons status `running`.

#### Dua Moda Sinkronisasi

**Moda 1, manual** melalui tombol Sync Now di halaman `/statik/Data/viewdt/Produk`. Notifikasi berbasis tiga skenario dengan *progressive disclosure*. Skenario A nol perubahan menampilkan "Data Sudah Mutakhir!" dengan rincian teknis disembunyikan di balik dropdown "Lihat Rincian Teknis" dan tombol Lanjutkan Kerja tanpa reload. Skenario B ada perubahan menampilkan badge jumlah produk baru dan jumlah diperbarui dengan tombol Muat Ulang Halaman. Skenario C gagal menampilkan "Sinkronisasi Terkendala" dengan penjelasan kendala jaringan.

**Moda 2, auto-sync latar belakang** lewat silent AJAX pada dashboard `/`. Alurnya: saat user baru login dan `cabang_id` belum dipilih, dashboard tidak menjalankan auto-sync dan memuat `Welcome/MiniDesk` agar user memilih cabang lebih dulu. Setelah dipilih, endpoint `GET /statik/Data/auto_sync_check/Produk` memeriksa selisih waktu terhadap `sync_jobs`. Jika kurang dari 120 menit, status `cooldown` dikembalikan dalam hitungan milidetik dan card menampilkan badge hijau "Data Sudah Mutakhir". Jika sudah 120 menit, sistem mengambil lock, menjalankan `syncApiData()`, mencatat ke `sync_jobs`, dan memperbarui card secara halus. Operator juga dapat memaksa sinkronisasi dengan parameter `?force=1` untuk melewati cooldown, dengan progress bar mini tanpa reload halaman.

Nilai cooldown terpusat di satu berkas. Mengubahnya otomatis menyesuaikan backend sekaligus label frontend, misalnya `120` menjadi "Siklus: Otomatis disinkronkan tiap 2 jam", `60` menjadi tiap 1 jam, dan `30` menjadi tiap 30 menit.

#### Skema Penyimpanan Riwayat

Tabel `sync_jobs` InnoDB dengan kolom `id`, `mdl_name`, `toko_id`, `cabang_id`, `status` bernilai `done`, `failed`, atau `running`, `total_items`, `processed_items`, `inserted_items`, `updated_items`, `skipped_items`, `failed_items`, `message`, `created_by`, `created_at`, `updated_at`, dan `finished_at` sebagai waktu acuan cooldown. Index `idx_sync_jobs_active` dibuat atas kombinasi `mdl_name`, `toko_id`, `cabang_id`, dan `status`.

Verifikasi CLI dilakukan lewat `curl` ke endpoint pada host demo untuk menguji sinkronisasi manual, auto-sync cooldown, dan force sync.

### 4.11 Multitenancy Centralized Order Desk

Cetak Biru ini merupakan dokumen versi 2.0 Final Production Ready dengan tanggal efektif 31 Mei 2026, sifat dokumen Rahasia Terbatas. Riwayat dokumen mencatat versi 1.0 pada 30 Mei 2026 dan versi 2.0 pada 31 Mei 2026 dengan tambahan jurnal akuntansi, pembelian pihak ketiga, dan dokumen berjenjang. Kolom persetujuan disediakan untuk Direktur Utama PT. A, Head of Information Technology, Head of Finance and Tax Compliance, serta Head of Legal Affairs.

#### Ekosistem

Enam entitas hukum berbentuk Perseroan Terbatas. PT. A berperan sebagai Central Operational Hub atau Pusat, sedangkan PT. Satu sampai PT. Lima berperan sebagai sister company atau *whitelabel vehicle*. Nama sister company dipinjam secara bergantian di hadapan konsumen untuk variasi administratif, segmentasi pasar, atau regulasi formal setempat, dan masing-masing memiliki NPWP, rekening bank, serta pembukuan sendiri.

PT. A memegang gudang dan stok barang fisik, integrasi API dan sistem IT, tim operasional, finance, dan customer service, serta rekening bank utama dan settlement. Distribusi seluruh pesanan dari pasar diakomodasi Aplikasi Netral atau aggregator yang berfungsi solely sebagai alat bantu penyalur data.

Tiga tujuan strategis: satu titik transaksi sehingga transaksi diproses, stok dipotong, dan dicatat satu kali di PT. A; whitelabel sempurna sehingga harga, komunikasi, virtual account bank, dan invoice fisik memakai identitas sister company; serta kepatuhan pajak tertinggi melalui metode *intercompany back-to-back trading* berbasis HPP plus *markup* operasional yang wajar.

#### Prinsip Single Database

Sistem memakai *single database, shared schema* dengan isolasi data lewat kolom `id_perusahaan`. Empat alasannya: stok terpusat sehingga potong stok satu kali, atomic, tanpa risiko *double-booked*; trigger otomatis untuk mutasi stok administratif real-time di sister company; laporan konsolidasi dengan satu query tanpa join enam database; serta operasional sederhana karena backup satu database dan satu maintenance dengan biaya rendah.

#### Entitas dan Integrasi API

Relasi konseptual: `master_perusahaan` satu ke banyak `orders`, `konfigurasi_markup_afiliasi`, `intercompany_ledger`, `kartu_stok_sister_company`, `jurnal_akuntansi_sister`, dan `pembelian_sister_pihak_ketiga`; `master_produk` satu ke banyak `order_details` dan `pembelian_sister_pihak_ketiga`; serta `orders` satu ke banyak `order_details`, satu ke satu `intercompany_ledger`, dan satu ke banyak `kartu_stok_sister_company` serta `jurnal_akuntansi_sister`.

Gateway tunggal adalah `POST https://api.pta.com/v1/orders`. Aplikasi netral mengirim satu POST; sistem menentukan PT penjual secara round-robin, stok, atau preferensi. Struktur payload memuat blok `api_authentication` dengan `api_key`, blok `order_header` berisi `order_id` seperti `ORD-PT3-2026-00001`, `id_perusahaan_penebeng`, `sumber_order` bernilai `AGGREGATOR` atau `SALES_SISTER`, `customer_name`, `customer_address`, dan `payment_gateway_provider` seperti XENDIT atau MIDTRANS, serta array `order_items` berisi `product_id`, `qty`, dan `price_per_unit`. Di sisi server terjadi `INSERT` pada `orders` dan `order_details`, pembuatan virtual account bank, serta trigger stok dan kartu stok.

#### Omnichannel Masking

Lima nomor WhatsApp Business API terdaftar atas nama masing-masing sister company dan dikelola PT. A melalui satu dashboard multi-agent. Logika visual staf: chat masuk ke nomor PT. Tiga menampilkan pop-up warna kuning dengan templat sapaan otomatis, dan staf dilarang menyebut PT. A di saluran tersebut. Untuk email, PT. A memegang hak akses DNS seluruh domain sister seperti `ptsatu.com` sampai `ptlima.com`, dan setiap email otomatis dikirim dengan `From: noreply@pttiga.com` sesuai PT penebeng, bukan `@pta.com`.

#### Infrastruktur Perbankan dan Aliran Kas

Aliran kas per transaksi: konsumen membayar melalui virtual account atas nama sister, dana masuk ke rekening PT. A, lalu terjadi *auto-pooling*. Pembukaan rekening operasional sister dijalankan dengan mekanisme *host-to-host* virtual account.

#### Keuangan, Cost-Plus, dan Simulasi PPN 11%

Matriks alokasi finansial: HPP riil persediaan sekitar 70 persen, *markup* operasional hub sekitar 25 persen, dan margin laba sister sekitar 5 persen.

Simulasi lengkap memakai harga konsumen atau DPP Rp100.000, HPP riil PT. A Rp70.000, *markup* operasional Rp25.000, dan harga jual PT. A ke sister Rp95.000.

Langkah 1, penjualan sister ke konsumen: konsumen membayar Rp111.000 lewat virtual account atas nama PT. Tiga, dana masuk ke PT. A. Jurnal sister membebankan Kas dan Bank Rp111.000, mengkredit Penjualan Rp100.000 dan PPN Keluaran Rp11.000. Jurnal PPN sister mencatat `jurnal_ppn_sister` dengan `id_sister` 3, `jenis_ppn` KELUARAN, DPP 100000, PPN 11000, lawan transaksi KONSUMEN, masa pajak 2026-05.

Langkah 2, pembelian sister dari PT. A secara back-to-back: PT. A terbitkan intercompany invoice dengan DPP Rp95.000, PPN Rp10.450, total Rp105.450. Jurnal sister membebankan Pembelian atau HPP Rp95.000 dan PPN Masukan Rp10.450, serta mengkredit Hutang ke PT. A Rp105.450.

Langkah 3, settlement akhir bulan: total dana konsumen Rp111.000, tagihan PT. A ke sister Rp105.450, sisa dana untuk sister Rp5.550, PPN kurang bayar Rp550, dan laba bersih Rp5.000. Jurnal settlement membebankan Hutang ke PT. A dan Kas dan Bank, serta mengkredit Kas dan Bank, Laba Ditahan, dan PPN Kurang atau Lebih Bayar.

#### Otomasi Stok dengan Trigger

Fungsi trigger utama bernama `proses_mutasi_stok_per_transaksi()` yang hanya berjalan saat status order berubah dari `PENDING` ke `PAID`. Untuk setiap baris `order_details`: mutasi fisik mengurangi `master_produk.stok_pusat`; mutasi administratif masuk dengan `INSERT` ke `kartu_stok_sister_company` jenis `MASUK` dengan dokumen referensi `PO/{id_perusahaan}/{id_order}`; lalu mutasi administratif keluar dengan jenis `KELUAR` dan dokumen referensi `SO/{id_perusahaan}/{id_order}`. Selanjutnya trigger menghitung `v_markup` dari `konfigurasi_markup_afiliasi` dengan nilai default 25000, menghitung `v_hpp_beli_sister` dari penjumlahan HPP satuan dikali qty ditambah markup dikali total qty, menghitung `v_ppn_masukan` sebagai `v_hpp_beli_sister * 0.11`, menghitung `v_profit` sebagai `total_dpp` dikurangi `v_hpp_beli_sister`, lalu meng-`INSERT` ke `intercompany_ledger`.

#### Operasional, Hukum, dan Peluncuran

SOP penanganan komplain dan returcovering tiga tahap: triage laporan, eksekusi retur fisik, dan pengembalian dana. Kerangka hukum memuat empat klausula: pemberian mandat hak kuasa operasional dan penyamaran identitas, konsolidasi arus kas serta otorisasi penyanderaan saldo atau *cash pooling*, skema jual-beli kembali *back-to-back* dengan ketentuan margin wajar, serta pembelian mandiri dari pihak ketiga.

Rencana peluncuran memakai garis waktu 30 hari kerja, matriks UAT, dan lembar checklist audit serta rekonsiliasi bulanan. Pembelian sister company dari pihak ketiga dibahas terpisah sebagai skenario baru beserta alur, jurnal, implikasi stok, serta rekomendasi plafon dan limitasi.

### 4.12 Standar Varian ERP: Produk, Supplies, dan BOM Produksi

#### Identitas Dokumen dan Tiga Pilar

Dokumen master design varian bertanggal 27 Juli 2026, berstatus *Single Source of Truth - Master Architectural Design*, dengan target repository PT. Indosan atau *Multi-Module Variant Standardization*. Arsitektur dibagi menjadi tiga pilar: Core Finished Goods untuk produk utama bervarian, Operational Supplies untuk bahan penolong bervarian, dan Multi-Variant BOM atau formula manufaktur bervarian.

#### Pilar 1: Core Finished Goods

Parent master table `produk` dengan kolom penting `id`, `kode`, `nama`, `has_variants` bernilai 1 atau 0, dan `variant_price_mode` bernilai `GLOBAL` atau `CUSTOM`. Child SKU table `var_product_variants` dengan kolom `id`, `produk_id` sebagai foreign key, `sku`, `variant_name`, `attribute_value_ids` bertipe JSON, dan `price_adjustment`. Master atribut berada di `var_attributes` seperti Warna dan Ukuran, serta `var_attribute_values` seperti Merah dan XL.

Transformasi *cart key* meninggalkan ID numerik polos seperti `1625` yang menyebabkan item overwrite, dan memakai format unik `variant:{produk_id}:{variant_id}` dengan contoh `variant:1625:14` untuk varian dan `variant:1625:1` untuk default atau sentinel.

*Sentinel pattern*: jika produk non-varian, `variant_id` diisi otomatis dengan 1 pada `stock_locker_variant` untuk mencegah null pointer dan error `WHERE variant_id = 0`. *Dual-write*: setiap penulisan inventori memperbarui `stock_locker` sebagai parent aggregate dan `stock_locker_variant` sebagai rincian varian anak secara bersamaan. *Line item*: tabel `transaksi_data` membawa `produk_id` induk, `variant_id`, dan `variant_nama`, dengan larangan keras mengisi `produk_id = 0` sebagai perbaikan *ghost row*.

#### Pilar 2: Operational Supplies

Parent master table `produk_supplies` dengan kolom `id`, `kode`, `nama`, `satuan`, dan `has_variants`. Child SKU table `var_supplies_variants` dengan `id`, `supplies_id`, `sku`, `nama`, `combination_key`, dan `aktif`. Master atribut berada di `var_supplies_attributes` dan `var_supplies_variant_values`. Tabel locker stok adalah `stock_locker_supplies_variant` dengan kolom `supplies_id`, `variant_id`, `jenis`, `state`, `jumlah`, `cabang_id`, dan `gudang_id`. *Cart key* supplies memakai format `supplies:{supplies_id}:{variant_id}`, ditangani `ComLockerStockSuppliesDualWrite` dan `MdlSuppliesVarian`.

Modul target penyetaraan supplies adalah `biaya` untuk pembelian supplies dan biaya penolong, `distribusisupplies` untuk transfer antar gudang, `pembelian` untuk purchase order supplies dari vendor, `produksi` dan `produksiproses` untuk konsumsi supplies sebagai bahan penolong produksi, serta `opname` untuk stock opname supplies.

#### Pilar 3: Multi-Variant BOM

Pola yang dipilih adalah *inherit-or-override*: formula induk global dengan `variant_id = 0` menjadi default untuk semua varian, dan varian tertentu dapat mengaktifkan override untuk memakai formula khusus. Tujuannya mencegah pengguna mengulang input formula ketika 90 persen varian komposisinya sama.

Tabel master `produksi_bom` memiliki kolom `id`, `produk_id` untuk produk induk finished goods, `variant_id` dengan 0 berarti formula global dan lebih besar dari 0 berarti formula khusus varian, `nama_formula`, `deskripsi`, `is_active`, `dtime`, `oleh_id`, serta index `idx_produk_variant`. Tabel rincian bahan `produksi_bom_detail` memiliki `id`, `bom_id` sebagai foreign key, `item_type` dengan enum `produk` atau `supplies`, `item_id`, `item_variant_id` dengan default 1 untuk bahan non-varian, `qty` bertipe `DECIMAL(24,10)`, `satuan`, `keterangan`, serta index `idx_bom`.

Algoritma *auto-resolution* saat membuat SPK produksi dua langkah. Pertama, sistem mengecek formula khusus varian dengan kondisi `produk_id` dan `variant_id` yang diminta serta `is_active = 1`. Kedua, bila hasilnya nol baris, sistem jatuh ke formula induk global dengan `variant_id = 0`. Rincian bahan baku dan bahan penolong langsung ter-*populate* ke SPK produksi secara otomatis.

#### Roadmap Fase

| Fase | Nama Task | Target Modul | Status |
|---|---|---|---|
| 1 | Core Finished Goods varian | 10 modul inventory core | Selesai 100 persen |
| 2 | Penyetaraan varian supplies | `biaya`, `distribusisupplies`, `pembelian`, `opname` | Roadmap (Task 1) |
| 3 | Produksi BOM bervarian | `produksi`, `produksiproses` | Roadmap (Task 2) |

### 4.13 Status Implementasi Varian pada Tracker Rollout

Tracker memakai workspace `w:\new_san_variant` dengan backup `W:\new_original_indosan_for_variant`, dimulai 14 Juni 2026 dengan target 6 sampai 8 minggu. Legenda status: selesai dan terverifikasi di codebase, sebagian selesai, belum dikerjakan, tidak diperlukan, dan perlu verifikasi UAT manual.

#### Arsitektur Dual-Write

Setiap transaksi atau penulisan stok produk kini menulis ke dua tabel. `stock_locker` pada level parent hanya menyimpan stok level produk dengan `variant_id = 0` untuk non-variant. `stock_locker_variant` pada level variant menyimpan data stok semua item, yaitu non-variant lewat sentinel `1` dan varian lewat `variant_id` asli.

Tiga kelas mendasarinya. `ComLockerStockDualWrite` sebagai wrapper utama yang mendelegasikan panggilan `pair()` ke `ComLockerStockVariant::pair()` dan `ComLockerStock::pair()`. `ComLockerStockVariant` menerapkan aturan: `variant_id < 0` dilewati, `variant_id == 0` diubah menjadi 1 sebagai sentinel lalu diproses, dan `variant_id >= 1` diproses langsung. `ComLockerStock` menghapus skip `variant_id > 0`, sehingga semua item termasuk yang memiliki varian ditulis sebagai parent level dengan kolom `variant_id` diabaikan.

State transition `active` versus `hold` berlaku di kedua tabel: add to cart mengurangi `active` dan menambah `hold`; remove from cart melepas `hold` dan mengembalikan `active`; finalize atau checkout mengurangi `hold` dan mengonsumsi `active`; timeout, logout, atau reset membersihkan `hold` dan memulihkan `active`.

#### Fondasi yang Sudah Selesai

Sepuluh item fondasi: helper empat fungsi pada `he_variant_unifier_helper.php` yaitu `resolveVariantId`, `normalizeSessionKeys`, `getUnifiedStock`, dan `reconcileStockDualWrite`; auto-normalize MdlMother tanpa method baru; enam model varian terdiri dari tiga Coms dan tiga Mdls; SQL view `stock_unified` dengan `UNION ALL` dan *NOT IN guard*; seed sentinel `var_product_variants` sebanyak 1.299 baris; seed stock `stock_locker_variant` sebanyak 5.894 baris; perbaikan P1 dengan *debug call* `arrPrint*` dikomentari; perbaikan P2 *SQL injection* dengan cast `(int)` eksplisit; penghapusan file orphan `variant_settings - Copy.php`; dan wrapper `ComLockerStockDualWrite.php` yang mendelegasikan ke locker.

#### UAT Wajib (11 Skenario)

Setiap modul inventori wajib lulus: non-varian ke non-varian dengan session key `variant:{pid}:1` dan dual-write ke `stock_locker` serta `stock_locker_variant(1)`; varian ke varian dengan session key `variant:{pid}:{vid}` dan variant_id asli; non-varian ke varian pada konversi dengan picker muncul dan stok *split* benar per varian; varian ke non-varian dengan fallback ke sentinel `variant_id = 1`; multi-item campuran non-varian dan varian dalam satu transaksi; add item ke cart; remove item dari cart; edit quantity item; reset cart; siklus draft, submit, approve; serta logout atau timeout dengan *release* hold otomatis.

#### Query Rekonsiliasi

Empat query wajib. Pertama, konsistensi dual-write dengan `HAVING parent != variant` yang harus menghasilkan nol baris, dengan join pada `produk_id`, `cabang_id`, `gudang_id`, dan `state` serta filter `jenis = 'produk'`. Kedua, pengecekan jumlah sentinel dengan `combination_key LIKE '_default_%' AND aktif = 1` yang harus sama dengan jumlah produk aktif di master. Ketiga, pengecekan orphan yang mencari produk `jenis = 'item'`, `status = 1`, `trash = 0` yang tidak ada di `var_product_variants`. Keempat, pengecekan *hold leak* pada `stock_locker_variant` dengan `state = 'hold' AND oleh_id = 0 AND transaksi_id = 0 AND jumlah > 0`.

#### Kronologi Pelaksanaan

- **14 Juni 2026:** lapisan fondasi dibangun, termasuk helper, view `stock_unified`, dan 6 model varian. Perbaikan P1 sampai P3 dikerjakan. Seed sentinel 1.299 dan default stock 5.894 berhasil dieksekusi. Rollout Agent 1 modul `konversi` selesai untuk config dan post-processor.
- **26 Juni 2026:** perbaikan selector produk dan variant picker di `konversi` pada bagian GROUP BY dan bypass filter stok untuk varian. Penulisan ulang skrip backfill migration `Backfilllockervariant`. Draf cetak biru selector variant picker.
- **30 Juni 2026:** penyelarasan controller `konversi_varian` mengubah static call `ComLockerStockDualWrite` menjadi dynamic model calls. Penyesuaian `normalizeSessionKeys` untuk membuang quantity nol atau negatif.
- **10 Juli 2026:** rollout `pembelianimport` selesai dengan variant identity resolution, helper locker, dan dual-write di `_processSelectProduct` serta `_processSelectProductException`. Penambahan reset filter `setFilters(array())` pada lookup produk `pembelian` dan `pembelianimport` untuk mencegah crash "tidak ada itemnya".
- **12 Juli 2026:** konsolidasi seluruh dokumen varian menjadi satu berkas master tracker dan penghapusan berkas redundan di folder `docs/`. Penyelarasan format feature flag `singleVariantStandard` menjadi bentuk array standar di `coTransaksiCore.php`. Audit akhir Agent 2 memverifikasi 7 poin implementasi varian pada `pembelian`, `pembelianimport`, dan `biaya` sebagai 100 persen lengkap dan siap UAT. Rollout Agent 4 sampai 6 selesai 100 persen dan lolos syntax checks. Penyelesaian setup database baru dengan DDL, view unified, data sentinel, dan saldo awal stok ke database `new_san_variant` di server `192.168.5.10`. Perbaikan *infinite resize loop* pada iframe relasi data varian dengan teknik *collapse height*, yaitu set 100px sementara sebelum membaca `scrollHeight` asli, dan pergantian listener MutationObserver ke user event-based listener `click`, `change`, `keyup`. Pembuatan metode `set_has_variants` untuk memperbarui flag `has_variants = 1` saat konfigurasi varian disimpan. Implementasi fitur varian supplies terpisah dengan DDL `var_supplies_variants`, `var_supplies_variant_values`, `stock_locker_supplies_variant`, dan view `stock_supplies_unified`, model `MdlSuppliesVarian.php` serta `MdlLockerStockSuppliesVariant.php`, wrapper `ComLockerStockSuppliesDualWrite.php`, dan integrasi modul `distribusisupplies`.
- **13 Juli 2026:** BOM dan komponen varian mandiri pada produk rakitan. Penambahan kolom `variant_id` ke tabel `produk_komposisi` dengan default 1 beserta index `idx_produk_variant`. Pembaruan model `MdlProdukKomposisi` agar lookup menyaring berdasarkan `variant_id`. Modifikasi `ProductEditor.php` dan `_productEditor.php` untuk memisahkan session `$_SESSION['PROED'][$prodID][$variantID]` dan mengizinkan penyusunan resep BOM per varian. Penambahan UI dropdown pemilih varian, serta fitur "Salin dari Master" dengan metode `cloneFromMaster`.
- **25 Juli 2026:** finalisasi dan QA modul pada Task 10 sampai Task 15. Simulasi ekstraksi logika bisnis *fat controller* seperti `Create.php` dan `FollowUp.php` menuju service library `LibPenjualanCreateSave.php` untuk mencapai *thin controller*. Pemastikanan seluruh kueri memakai *query binding* CodeIgniter 3 dan kode kompatibel penuh PHP 5.6 tanpa sintaks modern seperti `??` atau *arrow functions*. Penyusunan `workflow_penjualan.md` berbasis mermaid-js, dokumen UAT Plan `uat_penjualan.md`, pemutakhiran `dev-jurnal.md` per modul, dan pensinkronan pelaporan dokumentasi global.

#### Status per Modul

Enam agent dengan total 15 baris status, seluruhnya bertanda selesai secara keseluruhan. Agent 1 mencakup `konversi` dan `konversi_varian`; Agent 2 mencakup `pembelian`, `pembelianimport`, dan `biaya`; Agent 3 mencakup `penjualan` dan `penjualanproject`; Agent 4 mencakup `pindahgudang` dan `opname`; Agent 5 mencakup `distribusifg`, `distribusiproduksi`, dan `distribusisupplies`; Agent 6 mencakup `produksi`, `produksiproses`, dan `adjustment`. Kolom status yang dipantau meliputi dual-write `_shoppingCart`, dual-write `_processSelect*`, follow-up, feature flag, FIFO varian, konfigurasi locker varian, konfigurasi mutasi locker varian, selector variant picker, dan view `variant_picker.php`. Modul `biaya` dan `penjualan` masih ditandai sebagian selesai dengan butir berstatus N/A karena sifat supplies-only.

### 4.14 Standar Refaktor Laporan DDD dan CQRS

Target platform adalah CodeIgniter 3.1.8 HMVC yang kompatibel dengan PHP 5.6. Tujuannya standardisasi pembuatan dan refaktorisasi laporan matriks berkala agar sederhana, terstruktur, bebas duplikasi kode, dan tetap menaati aturan Domain-Driven Design.

#### Tiga Pilar Arsitektur

Arsitektur memisahkan tiga lapisan. **Domain Modul Bisnis** sebagai bounded context berisi domain controller yang mengatur alur bisnis, hak akses cabang, dan seleksi parameter, serta domain read-model berupa `MdlRaw*` dan `Com*` yang mengelola query agregasi, join tabel, dan kalkulasi data. **Cross-cutting Presentation Service** berisi `MonthlyMatrixFormatter` yang menata data mentah menjadi matriks 12 bulan Januari sampai Desember, menghitung footer total bawah, YTD, MTD, dan margin, serta menyiapkan metadata kolom DataTables dan UI switcher. **View Layer** berisi `laporan_periode.php` atau generic view engine yang menampilkan tabel DataTables, tombol navigasi subjek, dan tombol ekspor Excel, CSV, serta Print.

#### Kontrak Output Read-Model

Setiap method agregasi WAJIB mengembalikan array asosiatif dengan struktur kolom standar: `subjek_id`, `subjek_nama`, `thn` sebagai tahun transaksi, `bln` sebagai bulan dua digit, `sum_qty_kredit`, `sum_qty_debet`, `sum_kredit`, `sum_debet`, dan `sum_hpp` opsional bila relevan. Kontrak ini yang membuat satu formatter dapat melayani banyak modul tanpa duplikasi.

#### Service Library `MonthlyMatrixFormatter`

Lokasi `application/libraries/laporan/MonthlyMatrixFormatter.php`. Method `formatMatrix($rawRecords, $config)` menerima konfigurasi `subjek_key`, `subjek_name_key`, `year`, `has_margin`, dan `has_hpp`. Alur kerjanya: inisialisasi total 12 bulan dengan key `tahun-bulan` dua digit, pengelompokan data mentah ke matriks per subjek dan per periode, penghitungan nilai neto sebagai `sum_kredit - sum_debet` dan netto quantity sebagai `sum_qty_kredit - sum_qty_debet`, perhitungan margin `(netto - hpp) / netto * 100` bila `has_hpp` aktif, akumulasi total bawah, total penjualan, dan total HPP. Kunci kembaliannya adalah `src_harians`, `src_qty`, `src_margins`, `total_bawah`, `total_penjualan`, `total_hpp`, dan `year`.

#### Contoh Penerapan pada Modul Penjualan

Endpoint `Penjualan::ceksobulanan()` melakukan empat langkah berurutan. **Domain step** memilih query berdasarkan subjek yang diminta lewat switch enam cabang: `customer` memakai `callSummaryCustomerSoBulanan()` dengan `subjek_key` `pihak_id` dan model `MdlCustomer`; `produk` memakai `callSummaryProdukSoBulanan()` dengan `produk_id` dan `MdlProduk`; `kategori` memakai `callSummaryKategoriProdukSoBulanan()` dengan `kategori_id` dan `callSpecs()` kategori; `tipe` memakai `callSummaryTipePenjualanSoBulanan()` dengan `tipe_id` dan `MdlTipeTransaksi`; `cabang` memakai `callSummaryCabangSoBulanan()` dengan `cabang_id` dan `MdlCabang`; serta default `seller` memakai `callSummarySellerSoBulanan()` dengan `seller_id` dan `MdlSalesman`. **Domain step** mengambil master header data lewat `callData()` bila tersedia. **Service step** memformat matriks 12 bulan secara otomatis. **Presentation step** merender view `laporan_periode` dengan data subjek, master data, matrix data, mode, dan link switcher.

#### Roadmap Refaktor Tiga Fase

Fase 1 membangun service fondasi yang non-breaking di `application/libraries/laporan/MonthlyMatrixFormatter.php` dan diuji terisolasi dengan skrip PHP 5.6. Fase 2 melakukan pilot pada satu endpoint, yaitu `Penjualan::ceksobulanan()`, dengan verifikasi side-by-sideoutput baris per baris terhadap tampilan existing. Fase 3 melakukan standardisasi ke laporan Penjualan Faktur lewat `cekpenjualanbulanan`, modul Pembelian, dan modul Mutasi Stok.

#### Quality Gates

1. **Kompatibilitas ketat PHP 5.6:** dilarang memakai short array `[]`, null coalescing `??`, arrow functions, dan spread operators.
2. **Branch security guard:** setiap query raw di domain model WAJIB mengecek isolasi cabang dengan pola `if (my_cabang_id() != CB_ID_PUSAT && my_cabang_id() != "-1") { $this->db->where("cabang_id", my_cabang_id()); }`.
3. **Zero debug leakage:** seluruh perintah debug seperti `showLastQuery`, `cekHere`, dan `arrPrint` wajib dibersihkan sebelum rilis.
4. **Dual workspace consistency:** setiap perubahan di workspace aktif `w:\san_29agus` wajib diverifikasi dan disinkronkan ke workspace target `z:\san`.

---

## 5. Temuan Lintas-Dokumen dan Kontradiksi

### 5.1 Kontradiksi dan Tegangan Desain

| No | Topik | Tampilan A | Tampilan B | Penilaian |
|---|---|---|---|---|
| 1 | Stack teknologi | Volume 1-5: Django, DRF, PostgreSQL, Next.js, TypeScript | Flowchart, tracker varian, sinkronisasi, laporan: CodeIgniter 3 HMVC, PHP 5.6, MySQL | Dua generasi sistem. Dokumen implementasi adalah yang menyebut CI3 dan MySQL, jadi itu yang dekat dengan kenyataan |
| 2 | Isolasi data | Cetak Biru: *single database, shared schema* dengan kolom `id_perusahaan` | Dokumen inventaris: *multi-database* terisolasi fisik per anak perusahaan dengan Kafka atau RabbitMQ | Ketiganya tidak dapat dipakai bersamaan. Untuk grup dengan 6 entitas, *single database* paling murah operasionalnya |
| 3 | Bentuk skema transaksi | Diskusi storage: satu tabel `transaksi_data_registry` 60 kolom `longblob` | Volume 4: domain terpisah dengan tabel per objek bisnis seperti `PurchaseOrder` dan `StockBalance` | Arah evolusi sudah jelas: dari *fat monolith table* ke domain table. partly karena storage, partly karena *bounded context* |
| 4 | Naming endpoint | Volume 5: RESTful `POST /api/v1/purchase-requests/{id}/approve/` | Legacy: segment CI3 seperti `statik/Data/auto_sync_check/Produk` dan `laporan/Penjualan/ceksobulanan` | Butuh lapisan adapter atau gateway agar frontend baru tidak menyeret konvensi URL lama |
| 5 | Model otorisasi | Volume 1-3: `User -> Role -> Object -> Action -> Context` dengan *default deny* | Legacy: grup prefiks di baris UI seperti `o_kasir`, `c_holding`, `p_produksi_spv` | Grup legacy adalah bentuk *context rule* yang belum jadi data. Migrasi berarti mengubah setiap baris UI menjadi referensi aturan |
| 6 | Mekanisme mutasi stok | Cetak Biru: trigger PostgreSQL `proses_mutasi_stok_per_transaksi()` pada status `PAID` | Tracker varian: state `active` dan `hold` di `stock_locker` dan `stock_locker_variant` | Dua mekanisme berbeda dengan titik pemicu berbeda. Perlu ditetapkan mana yang otoritatif untuk stok varian |
| 7 | Kontrol konkurensi stok | Dokumen inventaris: `SELECT ... FOR UPDATE`, `UPDATE` hanya untuk status workflow, `DELETE` dilarang pada nilai finansial | Sync produk: MySQL advisory lock `GET_LOCK` untuk Serialize sinkronisasi | Keduanya komplementer, bukan saingan: yang satu melindungi baris stok, yang satu melindungi proses sinkron |
| 8 | Letak skema varian BOM | Tracker 13 Juli: kolom `variant_id` di `produk_komposisi` dengan `cloneFromMaster` | Master design 27 Juli: tabel baru `produksi_bom` dan `produksi_bom_detail` dengan pola *inherit-or-override* | Dua skema berbeda untuk kebutuhan sama. Yang lebih baru pervasive belum dinyatakan selesai di roadmap |
| 9 | Status Fase Varian | Tracker 12-13 Juli mencatat DDL supplies dan BOM sudah dieksekusi di `192.168.5.10` | Master design 27 Juli menyatakan Fase 2 dan Fase 3 masih *roadmap* | Status di dokumen *single source of truth* tertinggal dari kenyataan. Tracker adalah bukti eksekusi, master design adalah rujukan desain |
| 10 | Peran sumber stok | Cetak Biru: stok terpusat di PT. A, sister hanya punya kartu stok administratif | Sync produk dan varian tracker: stok operasional per cabang dan gudang | Kedua-duanya sah pada level berbeda, tetapi harus dinyatakan eksplisit agar tidak muncul dua angka stok |
| 11 | Target acronym | Cetak Biru dan DDD memakai istilah *domain*, *bounded context*, dan *CQRS* | Blueprint varian menyebut *variant* sebagai fitur tanpa konteks domain | Risiko kebingunan istilah: "variant" pada modul sudah berarti SKU anak, bukan *contextual variant* seperti `variant_price_mode` |
| 12 | Prinsip penomoran | Hirarki murni: nomor COA berlevel seperti `110100001` | Matriks fleksibel: nomor flat `SKU-8439` | Keduanya benar pada konteksnya. Larangan berlaku untuk ID yang punya parent, sementara kebebasan berlaku untuk SKU |

### 5.2 Urutan Mutakhir per Topik

| Topik | Sumber paling mutakhir | Dasar Penentuan |
|---|---|---|
| Desain varian ERP | `master-blueprint-design-variant-erp.md` | Tanggal tulis 27 Juli 2026, paling baru dari semua sumber |
| Status implementasi varian | `blueprint-progress-tracker.md` | Jurnal harian sampai 25 Juli 2026 dengan bukti eksekusi DDL |
| Model bisnis grup | `Cetak_Biru_Multitenancy_Updated.md` | Versi 2.0 efektif 31 Mei 2026, paling baru di dokumen grup |
| Peta sistem legacy | `FLOWCHART_MODUL.md` | Dibuat 18 Mei 2026 dengan sumber `heTransaksi_ui.php` |
| Standar laporan | `BLUEPRINT_REPORTING_REFACTOR_DDD.md` | Tidak bertanggal, tetapi memakai CI 3.1.8 dan workspace `w:\san_29agus` yang lebih baru dari `san_sarana_30sep` |
| Arsitektur target | `master_blueprint_enterprise_system_volume1.md` | Versi 1.0 tanpa tanggal, karena itu paling awal secara dokumentasi |
| Standar data inventaris | `inventory_matrix.md` | Tidak bertanggal, konsistensi internal paling utuh |
| Keputusan storage | `ringkasan_diskusi_database_erp.md` | Tanggal "[otomatis tersimpan]", tidak dispesifikasikan di sumber |

### 5.3 Temuan Kualitas Data

- **Stempel versi ganda.** Modul `TAXES_24APR2026`, `PRODUKSI_SEBELUM_GESER_BOM`, dan `PINDAHGUDANG_` menunjukkan rekam jejak perubahan yang tidak pernah dibersihkan dari konfigurasi.
- **Konfigurasi tanpa usage.** puluhan kode transaksi seperti `9990`, `118`, `383`, `7761` sampai `7765` memiliki alur lengkap tetapi nol log, sehingga belum pernah divalidasi di lapangan.
- **Angka yang perlu dibaca sebagai anekdot.** `1.642 updated` dan `skipped: 114` pada blueprint sinkronisasi adalah observasi satu waktu, bukan baseline.
- **Ketidakjelasan nama database.** `san_30mar`, `san_tsa_28agu`, `san_1jan26`, `san_29agus`, `san_sarana_30sep`, dan `new_san_variant` adalah nama yang berbeda; dokumen tidak menyatakan relasi garis waktu antar nama-nama ini.
- **Bahasa campur pada field teknis.** Beberapa label sumber menggunakan istilah Inggris pada konteks operasional Indonesia, misalnya `request`, `authorization`, `complete`, dan `release`, sementara sebagian lain memakai Bahasa Indonesia penuh. Konsistensi kosakata bukan hal yang ditegaskan di dokumen mana pun.

### 5.4 Celah yang Belum Terisi

- Tidak ada dokumen yang memetakan pemindahan dari konvensi legacy (`coTransaksiUi.php`, `coTransaksiValues.php`, grup prefiks) ke stack target Django/Next.js.
- Tidak ada keputusan tertulis tentang migrasi tabel varian dari `produk_komposisi.variant_id` menuju `produksi_bom`.
- Tidak ada tatakelola untuk `id_perusahaan` di Cetak Biru dikombinasikan dengan `company_code` di Volume 3 sebagai `context` resmi.
- Tidak ada spesifikasi single source of truth untuk harga produk antara Data Center dan cabang selain tabel `harga_produk` dan `konfigurasi_markup_afiliasi`.
- Target performa `di bawah 50 milidetik` untuk pencarian sparepart tidak memiliki ukuran indeks atau ambience pengukuran.

---

## Lampiran A: Daftar Lengkap Sumber (15 File)

| Nama File | Ukuran | Topik H1 | Peran dalam Sintesis |
|---|---|---|---|
| `master_blueprint_enterprise_system_volume1.md` | 35.9 KB | MASTER BLUEPRINT ENTERPRISE SYSTEM - Django + Next.js for Multi-Module Enterprise Platform, Volume 1 - Foundation, Domain Architecture, and Module Blueprint | Sumber kerangka arsitektur target, prinsip besar, bounded context, dan model authorization; memberi konteks vision dalam Ringkasan Eksekutif butir 1 dan 3 |
| `master_blueprint_enterprise_system_volume2.md` | 30.4 KB | MASTER BLUEPRINT ENTERPRISE SYSTEM - Volume 2 - Functional Specification and Workflow | Sumber definisi status kanonik, aturan validasi workflow, dan pola fungsi per objek; dipakai di Bagian 4.2 |
| `master_blueprint_enterprise_system_volume3.md` | 28.7 KB | MASTER BLUEPRINT ENTERPRISE SYSTEM - Volume 3 - Access Control Matrix and Authorization Pack | Sumber katalog role family, 29 action, 13 context, 21 definisi role, dan aturan SoD; dipakai di Bagian 4.3 |
| `master_blueprint_enterprise_system_volume4.md` | 38.9 KB | MASTER BLUEPRINT ENTERPRISE SYSTEM - Volume 4 - Global ERD and Django Implementation Pack | Sumber delapan layer entitas, prinsip relasi lintas modul, dan contoh model Django; dipakai di Bagian 4.4 |
| `master_blueprint_enterprise_system_volume5.md` | 28.5 KB | MASTER BLUEPRINT ENTERPRISE SYSTEM - Volume 5 - API Contract and Next.js Application Structure | Sumber gaya endpoint, versioning, katalog error, dan struktur Next.js; dipakai di Bagian 2.6, 2.7, dan 2.10 |
| `Cetak_Biru_Multitenancy_Updated.md` | 40.1 KB | CETAK BIRU KOMPREHENSIF: ARSITEKTUR SISTEM & OPERASIONAL BISNIS - Sistem Multi-Tenancy Centralized Order Desk (Whitelabel Operational Hub) | Sumber model bisnis grup 6 entitas, DDL PostgreSQL, cost-plus, simulasi PPN 11%, trigger stok, dan RBAC; dipakai di Bagian 4.11 |
| `FLOWCHART_MODUL.md` | 89.3 KB | Flowchart Jaringan Keterhubungan Modul ERP SAN5 | Sumber peta 23 kode transaksi aktif, cross-module document flow, field dan jurnal per modul, serta pola loading model; dipakai di Bagian 4.5 |
| `workflow_hierarchy.md` | 64 KB | Peta Hirarki Workflow Antarmuka Aplikasi (Log vs Config) | Sumber pemetaan langkah UI, grup pengotorisasi, output state, dan hit log sebagai bukti penggunaan nyata; dipakai di Bagian 4.6 |
| `inventory_matrix.md` | 8 KB | Dokumen Arsitektur ERP: Manajemen Inventaris Khusus Skala Enterprise | Sumber standar zonasi gudang, matriks M:M produk, ACID header-detail, locking, dual key, dan arsitektur multi-database; dipakai di Bagian 4.7 |
| `ringkasan_diskusi_database_erp.md` | 3.9 KB | Diskusi Arsitektur Database & Solusi Storage ERP | Sumber perbandingan `san_30mar` dan `san_tsa_28agu`, risiko storage, dan strategi database cut-off beserta view alternatif; dipakai di Bagian 4.9 |
| `hirarki murni dan matrix.md` | 4.6 KB | DOKUMEN ARSITEKTUR DATA: PERBANDINGAN STRUKTUR DATA - Hierarki Murni (Biologi & COA) vs Matriks Fleksibel (Produk Sparepart) | Sumber prinsip pemodelan data hierarki versus matriks dan aturan penomoran COA; dipakai di Bagian 4.8 |
| `BLUEPRINT_SYNC_PRODUK.md` | 11 KB | BLUEPRINT ARSITEKTUR & OPERASIONAL SINKRONISASI PRODUK | Sumber mekanisme sinkronisasi katalog DC ke cabang, smart diff, advisory lock, dua moda sync, dan tabel `sync_jobs`; dipakai di Bagian 4.10 |
| `blueprint-progress-tracker.md` | 19.7 KB | Master Variant Rollout Tracker & Architecture Blueprint (Unified) | Sumber status implementasi varian, kelas dual-write, sentinel, skenario UAT, query rekonsiliasi, dan kronologi 14 Juni sampai 25 Juli 2026; dipakai di Bagian 4.13 |
| `BLUEPRINT_REPORTING_REFACTOR_DDD.md` | 13 KB | BLUEPRINT ARSITEKTUR REFAKTOR LAPORAN (DDD + CQRS READ-MODEL) | Sumber standar layanan laporan dengan `MonthlyMatrixFormatter`, kontrak output read-model, roadmap tiga fase, dan empat quality gate; dipakai di Bagian 4.14 |
| `master-blueprint-design-variant-erp.md` | 7.9 KB | Master Blueprint Design Terpadu: Arsitektur Varian ERP (Produk, Supplies, & BOM Produksi) | Sumber tiga pilar varian, sentinel dan cart key, DDL `produksi_bom`, algoritma inherit-or-override, dan roadmap fase; dipakai di Bagian 4.12 |

---

## Lampiran B: Kronologi Tanggal yang Tersebut dalam Sumber

| Tanggal | Peristiwa | Sumber |
|---|---|---|
| Tidak dispesifikasikan | Volume 1-5 ditulis sebagai blueprint implementasi versi 1.0 tanpa tanggal | Volume 1-5 |
| Tidak dispesifikasikan | Diskusi arsitektur database, tanggal ditulis sebagai "[otomatis tersimpan]" | `ringkasan_diskusi_database_erp.md` |
| 18 Mei 2026 | Flowchart jaringan keterhubungan modul dibuat dari `heTransaksi_ui.php` tahap 1 sampai 3 | `FLOWCHART_MODUL.md` |
| 30 Mei 2026 | Cetak Biru Multitenancy versi 1.0 Final Production Ready | `Cetak_Biru_Multitenancy_Updated.md` |
| 31 Mei 2026 | Cetak Biru Multitenancy versi 2.0 Final Production Ready, tanggal efektif | `Cetak_Biru_Multitenancy_Updated.md` |
| 14 Juni 2026 | Fondasi varian dibangun, seed sentinel 1.299 dan stock 5.894 dieksekusi, rollout Agent 1 | `blueprint-progress-tracker.md` |
| 26 Juni 2026 | Perbaikan selector produk dan variant picker `konversi`, backfill migration ditulis ulang | `blueprint-progress-tracker.md` |
| 30 Juni 2026 | Penyelarasan controller `konversi_varian` dan helper `normalizeSessionKeys` | `blueprint-progress-tracker.md` |
| 10 Juli 2026 | Rollout `pembelianimport` selesai, reset filter lookup produk | `blueprint-progress-tracker.md` |
| 12 Juli 2026 | Konsolidasi dokumen varian, DDL supplies variant dieksekusi di `192.168.5.10` | `blueprint-progress-tracker.md` |
| 13 Juli 2026 | BOM dan komponen varian mandiri pada produk rakitan | `blueprint-progress-tracker.md` |
| 25 Juli 2026 | Finalisasi dan QA modul Task 10 sampai Task 15, thin controller dan query binding | `blueprint-progress-tracker.md` |
| 27 Juli 2026 | Master Blueprint Design Terpadu arsitektur varian, single source of truth | `master-blueprint-design-variant-erp.md` |