# 📘 BLUEPRINT ARSITEKTUR & OPERASIONAL SINKRONISASI PRODUK
**Sistem Aplikasi SAN (Cabang $\leftrightarrow$ Data Center)**  
*Dokumen Spesifikasi Teknis, Logika Eksekusi, dan Panduan Operasional*

---

## 1. Ringkasan Eksekutif & Latar Belakang

Sistem sinkronisasi produk berfungsi menjaga konsistensi katalog produk dan struktur harga antara **Data Center (Pusat)** dengan **Database Cabang Lokal**. 

### Masalah Awal yang Telah Diselesaikan:
1. **Kebocoran Produk Nonaktif (Trash):** Produk yang telah di-trash di DC (seperti ID 1731 *Fire Gloves*) masih berstatus aktif di cabang karena nilainya di-hardcode `status = 1` dan `trash = 0` pada model cabang, serta API DC memfilter produk trashed.
2. **Penumpukan Sampah Produk Baru:** Produk trashed baru di DC sebelumnya tetap di-insert ke cabang lokal.
3. **Pemborosan Blind Update (1.642 baris):** Setiap kali sinkronisasi manual ditekan, sistem melakukan `UPDATE` pada seluruh 1.642 produk lokal meskipun tidak ada perubahan data atau harga dari DC. Hal ini membuang resource CPU/I-O server dan memicu reload halaman yang tidak perlu.
4. **Notifikasi Kaku & Membingungkan:** Istilah teknis database (`inserted`, `updated`, `skipped: 114`) membingungkan operator kasir/toko.
5. **Kebutuhan Auto-Sync:** Diperlukan moda sinkronisasi terjadwal yang berjalan di latar belakang (*non-blocking*) tanpa mengunci antarmuka kasir dan tanpa merusak alur pemilihan multi-cabang saat login.

---

## 2. Diagram Arsitektur & Alur Data

```mermaid
flowchart TD
    subgraph DC [DATA CENTER - 192.168.11.100]
        API_DC[API seeItemAll_get<br/>Products.php]
        DB_DC[(Database Pusat)]
        DB_DC -->|Buka filter status & trash| API_DC
    end

    subgraph Cabang [CABANG LOKAL - 192.168.5.14]
        subgraph Triggers [Pemicu Sinkronisasi]
            ManualBtn[Moda 1: Tombol 'Syncron Now'<br/>Halaman Master Produk]
            AutoAJAX[Moda 2: Silent AJAX Dashboard<br/>auto_sync_check]
        end

        Lock[MySQL Advisory Lock<br/>SELECT GET_LOCK]
        Config[Konfigurasi Terpusat<br/>config.php: sync_produk_cooldown_minutes]
        DiffEngine[Smart Diff Check<br/>MdlProduk::syncApiData]
        JobLog[(Tabel sync_jobs)]
        DB_Lokal[(Database Cabang)]

        ManualBtn --> Lock
        AutoAJAX -->|Cek Cooldown 120 Menit| Config
        Config -->|Cooldown Lewat| Lock
        Lock -->|Tarik JSON DC| API_DC
        API_DC --> DiffEngine
        DiffEngine -->|Hanya baris yang berubah| DB_Lokal
        DiffEngine -->|Catat riwayat & waktu| JobLog
        DiffEngine -->|Moda 1: SweetAlert 3 Skenario| UserUI1[UI Master Produk]
        DiffEngine -->|Moda 2: Silent DOM Update| UserUI2[Card Dashboard Cabang]
    end
```

---

## 3. Komponen Berkas & Peranannya

| Komponen | Berkas | Peran Utama |
| :--- | :--- | :--- |
| **API Server DC** | `z:\san\application\controllers\eusvc\Products.php` | Menyajikan data produk & harga DC. Mengirimkan kolom `status` dan `trash` secara presisi via JSON. |
| **Model Engine Cabang** | `w:\san_sarana_30sep\application\models\Mdls\MdlProduk.php` | Eksekutor inti `syncApiData()`: Smart Diff Check, proteksi trash, update selektif tabel `produk` dan `harga_produk`. |
| **Controller Cabang** | `w:\san_sarana_30sep\application\modules\statik\controllers\Data.php` | Menangani endpoint `syncro_data` (manual) dan `auto_sync_check` (background/cooldown) serta advisory lock. |
| **Job Logger** | `w:\san_sarana_30sep\application\models\Mdls\MdlSyncJob.php` | Mengelola riwayat eksekusi pada tabel `sync_jobs` dan query status terakhir `getLastFinishedJob()`. |
| **Konfigurasi Global** | `w:\san_sarana_30sep\application\config\config.php` | Parameter terpusat `$config['sync_produk_cooldown_minutes'] = 120;`. |
| **Helper Global** | `w:\san_sarana_30sep\application\helpers\he_misc_helper.php` | Menyediakan fungsi `get_sync_cooldown_minutes()` dan `get_sync_cooldown_label()`. |
| **Dashboard Controller** | `w:\san_sarana_30sep\application\controllers\Welcome.php` | Memeriksa `$hasCabang` sebelum query data sync; mencegah eksekusi prematur saat baru login. |
| **Dashboard View** | `w:\san_sarana_30sep\application\views\welcome.php` | Menampilkan Card Status Sinkronisasi, Daftar Produk, dan alur pemilihan multi-cabang (`MiniDesk`). |

---

## 4. Mekanisme Inti (Core Logic)

### 4.1. Pembukaan Status & Trash dari Data Center
* **Lokasi:** `z:\san\application\controllers\eusvc\Products.php` (`seeItemAll_get`)
* Filter lama `status=1` dan `trash=0` dinonaktifkan sehingga produk yang dinonaktifkan di DC tetap dikirimkan ke cabang.
* Kolom `"status"` dan `"trash"` dimasukkan ke dalam daftar `$manualFields` agar tidak terpotong oleh `getFields()` model DC.
* **Contoh Hasil:** Produk ID 1731 mengirimkan payload `"status":"0","trash":"1"`.

### 4.2. Pencegahan Masuknya Produk Trash Baru (`skipped`)
* **Lokasi:** `w:\san_sarana_30sep\application\models\Mdls\MdlProduk.php` (L2496–L2503)
* Jika produk belum ada di cabang (`!$existing`) dan DC mengirimkan `trash == 1`, data **langsung dilewati (`skipped++`)** dan tidak di-insert ke tabel `produk` maupun `harga_produk`.

### 4.3. Smart Diff Check (Anti-Blind Update)
* **Lokasi:** `w:\san_sarana_30sep\application\models\Mdls\MdlProduk.php` (L2504–L2577)
* Sebelum mengeksekusi SQL `UPDATE`, sistem membandingkan nilai kolom lokal vs kiriman DC:
  1. **Tabel `produk`:** Membandingkan seluruh field (kecuali `id` dan `last_update`).
  2. **Tabel `harga_produk`:** Membandingkan nilai harga (`abs($lokal - $dc) > 0.0001`), status, dan trash.
* **Hasil:** Jika seluruh data sama persis, query `UPDATE` **tidak dijalankan** dan `updated` tetap `0`. Angka `1.642 updated` yang sia-sia hilang total.

### 4.4. Proteksi Concurrency (MySQL Advisory Lock)
* Menggunakan `SELECT GET_LOCK('mdlproduk_{toko_id}_{cabang_id}', 0)`.
* Jika sinkronisasi sedang berlangsung (baik oleh kasir lain atau oleh auto-sync background), request baru tidak akan menumpuk beban query, melainkan langsung mendapat respons status `running`.

---

## 5. Dua Moda Sinkronisasi

### MODA 1: Sinkronisasi Manual (Tombol "Syncron Now")
* **Akses:** Halaman Master Data Produk (`/statik/Data/viewdt/Produk`).
* **Karakteristik:** Operator menunggu di depan layar secara sadar.
* **Notifikasi Berbasis 3 Skenario (Progressive Disclosure):**
  1. **Skenario A (0 Perubahan):**
     * Judul: `Data Sudah Mutakhir!` *(Icon Info Biru)*
     * Pesan: *"Semua data Produk sudah sesuai dengan Data Center. Tidak ada perubahan data baru saat ini."*
     * Detail Teknis: Disembunyikan di balik dropdown `<details>` *"Lihat Rincian Teknis"*.
     * Tombol: `[ Lanjutkan Kerja ]` $\rightarrow$ Hanya menutup pop-up, **tanpa reload**.
  2. **Skenario B (Ada Perubahan / inserted > 0 || updated > 0):**
     * Judul: `Sinkronisasi Berhasil!` *(Icon Success Hijau)*
     * Pesan: Menampilkan badge jumlah produk baru dan jumlah produk diperbarui harga/statusnya.
     * Tombol: `[ Muat Ulang Halaman ]` $\rightarrow$ Begitu diklik, otomatis reload layar untuk memuat data baru.
  3. **Skenario C (Gagal / Error):**
     * Judul: `Sinkronisasi Terkendala` *(Icon Error Merah)*
     * Pesan: Penjelasan kendala jaringan/sistem dan anjuran coba beberapa saat lagi.

---

### MODA 2: Auto-Sync Latar Belakang (Opportunistic Non-Blocking di Dashboard)
* **Akses:** Halaman Dashboard Utama (`/`).
* **Karakteristik:** Berjalan senyap (*silent background AJAX*), tanpa pop-up yang mengganggu kasir, tanpa membekukan antarmuka.

#### Alur Kerja Auto-Sync:
1. **Preservasi Alur Login Multi-Cabang:**
   * Saat user baru login (`cabang_id` belum dipilih), Dashboard **tidak menjalankan auto-sync**.
   * Layar memuat `Welcome/MiniDesk` agar user memilih lokasi cabang dahulu.
   * Setelah cabang dipilih dan session terisi, barulah Card Dashboard dan auto-sync aktif.
2. **Pemeriksaan Cooldown (120 Menit / Terpusat):**
   * Browser memanggil endpoint: `GET /statik/Data/auto_sync_check/Produk`.
   * Sistem membaca selisih waktu dari tabel `sync_jobs`.
   * **Jika < 120 Menit:** Mengembalikan status `cooldown` dalam hitungan milidetik. Card langsung menampilkan badge hijau: *"🟢 Data Sudah Mutakhir"*.
   * **Jika $\ge$ 120 Menit:** Mengambil lock, mengeksekusi `syncApiData()`, mencatat ke `sync_jobs`, dan memperbarui Card secara halus.
3. **Tombol Sinkronkan Cepat di Dashboard:**
   * Operator dapat menekan tombol `[ Sinkronkan ]` di Card Dashboard kapan saja.
   * Menembak endpoint dengan parameter `?force=1` (membypass cooldown), menampilkan progress bar mini di card, dan selesai tanpa perlu me-reload halaman.

---

## 6. Pengaturan & Konfigurasi Terpusat

Seluruh interval waktu diatur di satu berkas:
👉 **`w:\san_sarana_30sep\application\config\config.php`**

```php
// Konfigurasi siklus auto-sync produk dari Data Center (satuan menit)
// Contoh: 120 = 2 jam, 60 = 1 jam, 30 = 30 menit, 180 = 3 jam
$config['sync_produk_cooldown_minutes'] = 120;
```

### Efek Otomatis Saat Nilai Diubah:
* **Backend:** Controller otomatis menggunakan batas waktu baru untuk throttle request.
* **Frontend:** Teks Card Dashboard otomatis menyesuaikan labelnya:
  * Jika diset `120` $\rightarrow$ *"Siklus: Otomatis disinkronkan tiap 2 jam."*
  * Jika diset `60` $\rightarrow$ *"Siklus: Otomatis disinkronkan tiap 1 jam."*
  * Jika diset `30` $\rightarrow$ *"Siklus: Otomatis disinkronkan tiap 30 menit."*

---

## 7. Skema Penyimpanan Riwayat (`sync_jobs`)

Riwayat setiap kali sinkronisasi dicatat pada tabel MySQL `sync_jobs`:

```sql
CREATE TABLE IF NOT EXISTS `sync_jobs` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `mdl_name` VARCHAR(128) NOT NULL,            -- 'MdlProduk'
    `toko_id` BIGINT NOT NULL DEFAULT 0,
    `cabang_id` BIGINT NOT NULL DEFAULT 0,        -- ID Cabang aktif
    `status` VARCHAR(32) NOT NULL DEFAULT 'done', -- 'done' / 'failed' / 'running'
    `total_items` INT NOT NULL DEFAULT 0,
    `processed_items` INT NOT NULL DEFAULT 0,
    `inserted_items` INT NOT NULL DEFAULT 0,
    `updated_items` INT NOT NULL DEFAULT 0,
    `skipped_items` INT NOT NULL DEFAULT 0,
    `failed_items` INT NOT NULL DEFAULT 0,
    `message` TEXT NULL,
    `created_by` BIGINT NOT NULL DEFAULT 0,       -- ID User yang mengeksekusi
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    `finished_at` DATETIME NULL,                  -- Waktu acuan cooldown
    PRIMARY KEY (`id`),
    KEY `idx_sync_jobs_active` (`mdl_name`,`toko_id`,`cabang_id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

## 8. Panduan Pengujian & Verifikasi CLI

Untuk memverifikasi endpoint sinkronisasi secara mandiri melalui terminal server cabang:

```bash
# 1. Uji Sinkronisasi Manual (Menghasilkan script SweetAlert)
curl -k -s "https://demo.mayagrahakencana.com/san_sarana_30sep/statik/Data/syncro_data/Produk/Produk"

# 2. Uji Auto-Sync Cooldown (Menghasilkan JSON ringkas & instan)
curl -k -s "https://demo.mayagrahakencana.com/san_sarana_30sep/statik/Data/auto_sync_check/Produk"

# 3. Uji Force Sync (Membypass cooldown 2 jam via CLI)
curl -k -s "https://demo.mayagrahakencana.com/san_sarana_30sep/statik/Data/auto_sync_check/Produk?force=1"
```

---
*Dokumen ini disusun sebagai acuan resmi arsitektur dan pemeliharaan fitur sinkronisasi produk aplikasi SAN.*
