## 2026-07-10 - Perjelas KPI Tugas Hari Ini di /absensi
- KPI **Perlu Dikerjakan**, **Sedang Diproses**, dan **Selesai Hari Ini** menggantikan label membingungkan (Tugas / Dalam Proses / Tiket Aktif).
- Angka tidak lagi menghitung jadwal/tiket yang sudah selesai sebagai pekerjaan aktif; Visit GPS dipisahkan di hint teks.
- Daftar Pekerjaan menampilkan item yang masih perlu ditangani; jika semua selesai muncul pesan hijau jelas.

## 2026-07-10 - Fix upload foto multiple di semua form pekerjaan /absensi
- **Selesai & Lapor Jadwal** kini mendukung multi-select foto (sama seperti Selesaikan Tiket): tombol galeri/kamera, Tambah Foto Lagi, maks 30 foto.
- **Update Progress Tiket** juga mendukung banyak foto sekaligus; backend menerima field `photos[]` (maks 30).
- Komponen picker foto dipakai bersama di Selesaikan, Selesai & Lapor, dan Progress.

## 2026-07-10 - Deploy instance standalone BUMDes (bumdes231.ion.net.id)
- Instance terpisah di server `bumdes` (31.56.199.108): `/netadmin`, `/absensi`, `/portal`, `/superadmin`.
- Plan `bumdes` membatasi modul operasional BUMDes; dashboard menyesuaikan modul aktif.
- DNS `bumdes231.ion.net.id` → bumdes; root domain redirect ke `/netadmin`.

## 2026-07-10 - Perbaikan kontras theme light di /netadmin
- Memperjelas warna teks, label, border, dan tombol tonal saat theme light agar sidebar, tabel, badge fiber, dan panel form lebih mudah dibaca.

## 2026-07-10 - Fix upload foto multiple di Selesaikan Tiket /absensi
- Memperbaiki picker foto dokumentasi yang hanya bisa pilih 1 foto di HP: `accept="image/*"` + tombol galeri/kamera terpisah.
- Galeri mendukung multi-select; kamera dan tombol **Tambah Foto Lagi** untuk upload bertahap (maks 30 foto total, langsung tersimpan ke tiket).

## 2026-07-10 - Fix alur Mulai Proses maintenance + material Gudang Teknisi
- Setelah Mulai Proses, status tiket/jadwal kini sinkron ke Diproses sehingga tombol berubah ke Selesaikan (bukan tetap Mulai Proses).
- Baris Jadwal dengan tiket terkait menampilkan status tiket, bukan hanya jam jadwal.
- Material Gudang Teknisi yang dipilih saat Mulai Proses disimpan sementara dan dibawa otomatis ke form Selesaikan Tiket; stok dipotong saat selesai.

## 2026-07-10 - Fix error "baris request belum memilih barang" di /absensi maintenance
- Memperbaiki validasi material inline: baris Request Gudang Asal kosong tidak lagi memblokir penyelesaian tiket bila teknisi hanya memakai stok Gudang Teknisi.
- Baris request tidak lagi otomatis ditambahkan saat form dibuka; hanya muncul setelah tekan Tambah Barang Request.

## 2026-07-10 - Fix tiket dobel di /absensi Daftar Pekerjaan
- Memperbaiki tiket yang muncul dua kali di `/absensi` (sebagai Jadwal + Tiket) padahal di `/netadmin` hanya satu.
- Tiket yang sudah punya jadwal teknisi hari ini hanya ditampilkan sebagai baris Jadwal; entri Tiket terpisah dihilangkan dari API dan dedupe di frontend.

## 2026-07-10 - Material Gudang Teknisi untuk tiket maintenance di /absensi
- Memperbaiki gap: tiket maintenance/gangguan di `/absensi` kini bisa memilih dan memotong stok langsung dari Gudang Teknisi saat Selesaikan Tiket (seperti alur material PSB).
- Endpoint baru `GET /api/attendance/mobile/inventory/technician-catalog` menampilkan stok Gudang Teknisi teknisi.
- `POST /api/attendance/mobile/tickets/{id}/resolve` menerima field opsional `materials_used` (item_id, quantity, notes) untuk pemotongan stok otomatis.
- Form Selesaikan Tiket non-PSB memisahkan **Pakai Material dari Gudang Teknisi** dan **Request Tambahan dari Gudang Asal**.

## 2026-07-08 - Fix upload foto lampiran tiket Diproses di NetAdmin
- Memperbaiki error `Failed to fetch` saat upload foto dokumentasi pada tiket berstatus Diproses: batas upload PHP-FPM dinaikkan (12 MB/file), foto di-resize otomatis maksimal ~1,9 MB sebelum dikirim, dan pesan error jaringan/upload dibuat lebih jelas.
- Upload lampiran tiket kini bisa dilakukan oleh role dengan `tickets.update` (bukan hanya `tickets.create`), dan foto langsung ter-upload setelah dipilih di tab Lampiran.

## 2026-07-07 - Ganti ONU dari tiket + feedback asset PSB
- Penyelesaian tiket maintenance/gangguan bisa mencatat penggantian ONU: SN baru dari gudang, kondisi ONU lama (rusak/perbaikan/hilang/layak), histori asset movement otomatis.
- Endpoint `POST /api/tickets/{id}/resolve` menerima field opsional `onu_replacement`.
- PSB selesai mengembalikan `asset_sync` (ok/failed) dan pesan UI menampilkan status registrasi asset ONU.

## 2026-07-04 - Badge sidebar Request Uang & Request Barang di NetAdmin
- Sidebar `/netadmin` pada menu `Request Uang` dan `Request Barang` kini menampilkan badge merah seperti `Tiket` dan `Alarm`.
- Admin melihat jumlah request yang perlu tindakan (approval/pembayaran atau proses gudang); staff melihat request pending milik sendiri.
- Endpoint baru: `GET /api/money-requests/pending-count` dan `GET /api/inventory/requests/pending-count`.

## 2026-07-04 - DeepSeek global untuk teks AI + Anthropic untuk OCR/tools (Opsi B)
- Provider aktif (mis. DeepSeek) kini dipakai untuk semua fitur teks: AI Assistant chat, AI Center (reply komplain, ONT matcher, NAS/OLT copilot, alarm correlator, heatmap planner, template notifikasi).
- OCR bukti transfer, OCR KTP/identitas, dan aksi teknis AI (search ONU, reboot, ack alarm) tetap memakai **Anthropic Claude** dengan API key terpisah di tab Claude — tidak perlu mengganti provider aktif ke Anthropic.
- AI Center tidak lagi memaksa `ai_provider = anthropic` saat simpan pengaturan AI Center.
- Heatmap AI Planner menambahkan cabang DeepSeek.

## 2026-07-04 - Provider DeepSeek di Pengaturan AI
- Menu `Pengaturan AI` menambahkan tab provider **DeepSeek** dengan penyimpanan API key terpisah (`ai_deepseek_api_key`) dan model `deepseek-v4-flash` / `deepseek-v4-pro`.
- Backend memakai endpoint OpenAI-compatible `https://api.deepseek.com/chat/completions` untuk chat AI Assistant, test koneksi, dan generate template notifikasi.
- Validasi API: `ai_provider` kini menerima nilai `deepseek` pada `PUT /api/settings`, `POST /api/settings/test-ai`, dan `GET /api/ai/balance`.

## 2026-07-04 - Fix Gudang Tujuan kosong pada Request Barang teknisi
- Memperbaiki bug: Request Barang `Stok Keluar` yang dibuat teknisi dari NetAdmin (form Request Barang / material tiket) tidak terdeteksi sebagai request teknisi, sehingga barang keluar dari gudang asal tanpa masuk ke Gudang Teknisi (Gudang Tujuan kosong). Ini mengganggu teknisi PSB karena stok tidak tercatat di gudangnya.
- Akar masalah: relasi `requester` dimuat tanpa kolom `role`, sehingga deteksi otomatis `isTechnicianInventoryRequest` selalu gagal untuk request dari luar `/absensi`. Kini `role` ikut dimuat, sehingga request teknisi otomatis dirutekan sebagai transfer ke Gudang Teknisi pemohon saat diproses (Gudang Tujuan terisi otomatis).
- Dampak: request `out` oleh teknisi yang masih Pending/Disetujui akan otomatis masuk ke Gudang Teknisi saat diproses. Request lama yang sudah `completed` tidak berubah otomatis (perlu koreksi stok manual bila barang masih di teknisi).

## 2026-07-04 - Mapping Gudang Asal per Home Base (Request Barang Absensi)
- Menu `Home Base` menambahkan pemetaan resmi `Home Base → Gudang Asal`. Teknisi hanya bisa Request Barang dari `/absensi` pada gudang/POP yang dicentang untuk Home Base-nya.
- Memperbaiki kasus teknisi Tabanan (Home Base BONGAN) yang hanya melihat satu gudang: admin kini bisa menambahkan gudang lain (mis. Gudang Utama) secara eksplisit tanpa bergantung pada kecocokan nama wilayah.
- Jika sebuah Home Base belum dipetakan, sistem tetap memakai pencocokan otomatis nama wilayah (perilaku lama), sehingga tenant lain tidak terpengaruh.
- Endpoint baru `POST /api/home-bases/{id}/inventory-locations` dan field `inventory_location_ids[]` pada create/update Home Base. Tabel pivot baru `home_base_inventory_location`.
- Label Gudang Asal di Request Barang kini menampilkan semua Home Base yang berhak atas gudang tersebut (mis. `BONGAN, DPS-OMA, KANTOR-JADI`), sehingga gudang bersama seperti Gudang Utama tidak lagi salah tercap satu wilayah saja.

## 2026-06-30 - Footer Notifikasi ion.net.id
- Semua pesan WhatsApp, Telegram, dan email yang dikirim lewat service notifikasi kini otomatis menambahkan footer `ion.net.id - Internet Super Ngebut` dan `Portal Pelanggan : https://apps.ion.net.id`.
- Footer dibuat idempotent agar tidak dobel jika template sudah memuat salah satu baris footer.
- KB Notifikasi diperbarui agar operator tidak perlu menulis footer manual di setiap template pesan.

## 2026-06-30 - Template Invoice Klinik Tech/ION.NET
- Logo invoice standar diganti memakai asset khusus `invoice-kliniktech.png` dari gambar yang diberikan user, sehingga print/PDF invoice memakai logo tersebut secara konsisten.
- Field `billing_settings.invoice.logo_url` tetap bisa dipakai sebagai override eksplisit; jika kosong sistem memakai asset invoice khusus tersebut, bukan logo admin/portal.
- Layout template invoice dirapikan: spacing A4, alignment status invoice, tabel ringkasan, opsi pembayaran, dan padding print/PDF dibuat lebih konsisten.
- Print/export invoice standar kini memakai template Klinik Tech/ION.NET yang reusable untuk status `Sudah Bayar`, `Belum Bayar`, dan jatuh tempo.
- Template menampilkan header logo, gradient orange, dua kolom penagihan, tabel ringkasan layanan, transaksi pembayaran atau opsi transfer/tunai, dan footer note computer generated.
- Halaman `/invoice-template-preview` ditambahkan untuk QA visual dua contoh invoice paid/unpaid sebelum template dipakai ke pelanggan.

## 2026-06-30 - Paypoint POP Mobile
- Portal mobile `/paypoint` ditambahkan untuk kasir POP menerima pembayaran cash pelanggan, mencari invoice/pelanggan, memilih akun Kas POP, mencatat pembayaran, dan mencetak struk thermal dari browser.
- Backend menambahkan endpoint `paypoint` dengan permission `paypoint.view` dan `paypoint.pay`; pembayaran memakai `InvoicePaymentService` existing agar invoice lunas, `PaymentRecord`, Data Keuangan, perpanjangan masa aktif, dan buka isolir tetap konsisten.

## 2026-06-30 - Perbaikan Sync Email Akun Saya
- Sync Inbox `Akun Saya > Email Setting` kini menangani mismatch protocol/port IMAP/POP3 dengan pesan yang lebih jelas, misalnya ketika port 993 IMAP tersimpan sebagai POP3.
- Setting email akun `aris@kliniktech.com` dikoreksi ke IMAP SSL port 993 setelah koneksi read-only ke mailbox berhasil.

## 2026-06-30 - Notifikasi Invoice Otomatis
- Invoice recurring yang digenerate cron kini otomatis mengirim/queue notifikasi WhatsApp ke pelanggan melalui WA-KU, dengan duplicate guard berdasarkan invoice agar tidak terkirim ulang.
- Pengaturan Billing menambahkan toggle `Notifikasi WA Saat Invoice Otomatis Dibuat`, dan command `php artisan billing:notify-invoices` tersedia untuk backfill invoice yang sudah terlanjur dibuat.
- Command backfill mendukung pengiriman satu per satu memakai `--limit` dan `--delay`, tanpa mengubah toggle/config notifikasi global tenant.

## 2026-06-30 - Modul Aset Kantor
- KPI `Aset Kantor` dibuat clickable untuk filter cepat, daftar aset dibuat lebih jelas sebagai card clickable, dan panel detail dirapikan menjadi ringkasan aset yang mudah dipindai.
- NetAdmin menambahkan menu `Aset Kantor` untuk register aset milik kantor, mutasi lokasi/PIC, maintenance, opname, disposal, dan KPI nilai aset.
- Backend menambahkan endpoint `office-assets` dengan permission `office_assets.*`, feature plan `office_assets`, serta pesan error validasi yang menyebut field dan tindakan perbaikan.
- KB menambahkan halaman `aset-kantor.html`, mockup `screenshot-office-assets.svg`, entry landing KB, dan dokumentasi di `Semua Modul`.

## 2026-06-30 - Label Menu Gudang
- Label side menu NetAdmin untuk modul stok/material diganti dari `Inventaris` menjadi `Gudang`, termasuk label permission/module terkait agar operator memakai istilah yang sama.
- KB `Semua Modul` dan panduan `Gudang & Inventory` diperbarui agar langkah penggunaan membuka menu `Gudang`.

## 2026-06-29 - Perluasan Incident Tiket
- `/portal` kini menampilkan `Panduan Pembayaran Mudah` di checkout invoice: pelanggan melihat langkah bernomor untuk cek tagihan, pilih channel, scan QRIS/salin VA/kode pembayaran, menyelesaikan pembayaran, dan menekan `Cek Status Pembayaran`.
- Modal `Incident` kini memiliki tombol `Generate Kata-kata` untuk membuat draft deskripsi incident, template tiket anak/jadwal teknisi, dan pesan WA group operasional yang tetap bisa diedit operator sebelum dikirim.
- Tiket anak incident kini dapat memakai template deskripsi khusus dengan placeholder incident, pelanggan, ONU, area terdampak, dan estimasi pulih.
- Opsi tiket pelanggan terdampak pada Incident kini otomatis membuat jadwal pending di `Jadwal Teknisi` untuk tiap tiket anak, sehingga koordinator bisa langsung assign/dispatch teknisi.
- Incident kini memiliki opsi `Langsung buat tiket pelanggan terdampak` untuk membuat tiket anak otomatis dari ONU pelanggan pada OLT/Home Base terdampak, dengan batas jumlah dan penghindaran duplikat tiket aktif.
- Incident kini memiliki opsi `Kirim WA ke group operasional`; group tujuan diatur dari WhatsApp Bot dengan event baru `Incident Massal`.
- Modal `Incident` di Tiket & Helpdesk kini mendukung OLT terdampak, Home Base/Area, severity, estimasi pulih, area terdampak, dan penyebab awal.
- Incident dapat menautkan otomatis tiket `Open`/`Diproses` berdasarkan OLT dan/atau Home Base, dengan fallback aman jika scope kosong agar tidak menarik semua tiket aktif.
- Broadcast WA incident dibuat opt-in melalui checkbox `Kirim WA ke pelanggan tiket tertaut`, memakai template `Tiket Terkait Incident`, dan menampilkan jumlah tiket tertaut, WA terkirim, serta WA gagal.
- KB Tiket dan Semua Modul diperbarui dengan SOP incident massal, indikator sukses, dan fallback aman.

## 2026-06-28 - Keamanan Login Multi Device
- `waku.ion.net.id` menambahkan paket lengkap lanjutan: Developer Portal, webhook signature verifier, Postman collection, SDK sample, AI Bot Builder, Security & Compliance Center, IP anomaly monitor, security policy, dan Growth Center untuk upgrade funnel/promo.
- Backup storage kini mengecualikan `storage/app/private/ticket_progress_photos` dari archive agar backup tidak gagal saat folder foto progress private tidak bisa dibaca oleh proses `tar`.
- WA-KU SaaS Ops Center ditambahkan untuk kebutuhan enterprise: campaign manager dengan dry-run/send, incident center, queue control untuk process webhook dan retry failed today, history campaign, queue snapshot, serta export CSV report operasional.
- WA-KU by KDI kini tidak berhenti pada response provider `PENDING`: router mencoba akun WA-KU berikutnya, dan jika semua akun masih pending atau terkena limit maka pesan masuk antrian auto-route untuk dicoba ulang sekitar 1 menit.
- WA-KU SaaS Support Center ditambahkan di `waku.ion.net.id`: onboarding checklist, live diagnostics, rekomendasi per masalah, FAQ cepat, dan pembuatan ticket support yang menyertakan diagnostics otomatis melalui audit log tenant.
- WA-KU SaaS Admin Backoffice kini lebih lengkap: superadmin/master tenant bisa memfilter tenant, melihat summary risiko, suspended tenant, API error, webhook gagal, app key, request log, webhook delivery, dan pesan terakhir per tenant dari tab Admin.
- Tampilan sender WA-KU di `Inbox / Outbox` dirapikan menjadi chip `Sender WA-KU` berisi nomor sender dan badge akun, misalnya `62818999619` dengan akun `waku2`, agar pengecekan round-robin lebih mudah.
- Modal `Buat Tiket` dari daftar `ONU / ONT` kini menampilkan teknisi beserta label Home Base/POP dan tidak lagi memblokir teknisi hanya karena sudah memegang tiket atau visit/work log aktif. Teknisi POP Negara tetap bisa dipilih selama tidak ada bentrok jadwal pada waktu eksekusi.
- Outbox WhatsApp kini menyimpan dan menampilkan sender akun WA-KU pada pesan outbound. Tabel `Inbox / Outbox` menampilkan label akun seperti `waku1`/`waku2` dan nomor sender, sehingga hasil round-robin bisa dicek langsung dari Outbox.
- Tampilan `waku.ion.net.id` dirapikan menjadi UI SaaS yang lebih profesional: tema terang, control center dengan sidebar, card/table/form konsisten, dan landing yang lebih bersih.
- Notifikasi WhatsApp kini tidak lagi diabaikan hanya karena toggle provider WhatsApp umum nonaktif jika tenant memiliki akun WA-KU aktif dan valid. Saat toggle umum off, router outbound dibatasi ke akun WA-KU agar provider lama seperti Wablas/Fonnte tetap tidak ikut hidup tanpa sengaja.
- WA-KU SaaS Portal menambahkan Realtime Ops Center: tab realtime dengan auto-refresh, health aggregate instance, QR/connect live, queue pending, provider timeline, failed today, dan webhook due dalam satu layar.
- `waku.ion.net.id` dikembangkan menjadi WA-KU SaaS Portal v2 lengkap: dashboard bertab Overview, Instances, Queue, Messages, Apps, Webhooks, Billing, Playground, Audit, Admin, dan Settings.
- Client-key API WA-KU kini memiliki request log, response latency, bulk send, timeline pesan, webhook delivery list/test, key rotation, allowed IP UI, quota harian/bulanan, audit event, dan superadmin tenant monitor.
- Retry otomatis webhook WA-KU SaaS kini aktif di scheduler setiap menit melalui `waku:webhooks:process --limit=100 --json`, dilengkapi metrik due/failed di dashboard dan tombol `Process Due` untuk menjalankan retry segera.
- WA-KU SaaS menambahkan webhook dispatcher eksternal: event `message.accepted`, `message.queued`, `message.status`, dan `message.incoming` dikirim ke `webhook_url` client app dengan HMAC signature, delivery log, retry manual dashboard, dan command `php artisan waku:webhooks:process`.
- `waku.ion.net.id` dikembangkan menjadi WA-KU SaaS Portal: landing/pricing, registrasi tenant trial, login dashboard, app/client key khusus WA-KU, dashboard usage/instance/outbox, tester kirim pesan, dan API `/api/waku/v1/*` berbasis `X-Waku-Key` + `X-Waku-Secret`.
- WA-KU Gateway eksternal ditambahkan sebagai fondasi portal `waku.ion.net.id`: endpoint `GET/POST /api/external/v1/waku/*`, halaman portal `/waku`, API docs, OpenAPI, token integrasi tenant, send message, message status, instance list, health, usage, dan queue-safe routing.
- Round-robin WA-KU diverifikasi dengan test kirim nyata: akun bergantian `waku2` lalu `waku1`. Jika semua instance sedang dalam jeda/rate-limit, pesan kini tetap masuk queue dengan `wa_account_id` kosong agar scheduler memilih instance lagi saat aman, bukan langsung gagal.
- Counter limit menit WA-KU kini memakai waktu pesan diterima provider (`provider_accepted_at`) dan fallback `created_at`, bukan `updated_at`, sehingga callback/status sync tidak membuat pesan pending lama terhitung ulang di menit berikutnya.
- Callback status WA-KU by KDI kini mengenali event `MESSAGES_UPDATE`/`SEND_MESSAGE` dari webhook inbound sehingga Outbox tidak terlalu lama menggantung di `Pending` saat provider sudah mengirim status. Analytics campaign juga menampilkan arahan cek Health/Webhook jika callback belum masuk.
- WA-KU by KDI kini dipindahkan menjadi menu side bar sendiri tepat di bawah `WhatsApp Bot`, dengan halaman khusus untuk instance, QR, health, webhook, group, broadcast, queue, limit, SLA, dan automation inbound.
- Modul WA-KU kini memiliki plan/feature flag `waku`, opsi plan di Platform Admin, permission role `waku.view` dan `waku.manage`, serta alias API internal `/waku/*` agar role WA-KU tidak harus diberi akses penuh `wabot.manage`.
- Backup manual kini tidak gagal saat `.env` tidak bisa dibaca oleh proses backup. Sistem tetap membuat backup database/storage dan menulis `env.redacted` berisi catatan aman bahwa `.env` dilewati karena permission.
- Endpoint API tenant seperti `/api/attendance` kini mengembalikan JSON `401 UNAUTHENTICATED` saat token kosong/tidak valid, bukan error server akibat redirect ke route login yang tidak tersedia.
- Smoke test production setelah node2 menjadi app node preferred memverifikasi `/api/auth/me`, Absensi, Pelanggan, Invoice, Tiket, File Manager, health HA, dan backup storage lokal.
- `Akun Saya > Keamanan` di NetAdmin kini memiliki pengaturan `Login Multi Device`, batas maksimal device aktif, daftar sesi/device aktif, tombol cabut sesi per device, dan tombol `Keluarkan Semua Device Lain`.
- Saat `Login Multi Device` nonaktif, login baru otomatis mencabut sesi NetAdmin lama. Saat aktif, sistem mempertahankan sesi lain sampai batas device yang dipilih dan mencabut sesi terlama bila melewati batas.
- Pesan error diperjelas untuk permission `account.security`, sesi/device yang sudah dicabut, setting belum siap karena migration belum dijalankan, dan input batas device yang tidak valid.

## 2026-06-27 - UX Tiket Teknisi Mobile
- Request Barang di `/absensi` kini mendukung satu request berisi banyak item. Teknisi bisa menekan `Tambah Item`, mengisi quantity/catatan per barang, dan sistem menolak item duplikat agar quantity digabung dalam satu baris.
- Request Barang di `/absensi` kini memuat dropdown `Barang` berdasarkan stok Gudang Asal/POP yang dipilih, bukan katalog global. Form juga memiliki pilihan `Referensi Tiket/Jadwal`, dan backend menolak barang/quantity/referensi yang tidak sesuai dengan pesan error jelas.
- Tab `Riwayat Tiket` ditambahkan di `/absensi > Teknisi`. Teknisi dapat melihat KPI tiket pribadi seperti Pending, Diproses, Sukses, dan SLA Terlambat, lalu memfilter riwayat berdasarkan periode, status, dan pencarian nomor tiket/pelanggan.
- Error koneksi NAS MikroTik di NetAdmin, misalnya `CHR-NGR: Gagal koneksi ke MikroTik`, kini tidak lagi muncul sebagai toast merah berulang saat refresh sesi/lease/secret. Error tetap tampil inline pada tabel Network/NAS agar operator tahu router mana yang perlu dicek tanpa mengganggu halaman lain.
- Upload foto progress tiket dari `/absensi` kini memiliki fallback local storage bila Object Storage/S3 tenant sementara tidak bisa dijangkau, sehingga catatan progress tetap bisa tersimpan dan error tidak berhenti di kegagalan provider storage.
- Field koordinat pelanggan di NetAdmin kini menjadi satu kolom `Titik Koordinat` pada tambah/edit pelanggan. Operator bisa paste `lat,lng` atau link Google Maps, lalu sistem otomatis memecah Latitude dan Longitude dengan pesan error jelas jika format/range tidak valid.
- Wizard `Tambah Pelanggan` di NetAdmin kini memiliki field `Waktu Eksekusi / Jadwal PSB` pada bagian Tiket PSB. Operator bisa menentukan jadwal eksekusi sebelum pelanggan disimpan, dan sistem menampilkan pesan jelas jika jadwal PSB kosong atau tidak valid.
- Update progress tiket di `/absensi` kini masuk mode fokus: tiket lain disembunyikan sementara, form progress muncul tepat di bawah tiket terkait, foto progress opsional bisa diupload dan auto-resize, serta tombol `Jadwalkan Ulang` tersedia untuk tiket aktif.
- Detail tiket di `/absensi` kini memisahkan tombol `Request Barang` dan `Request Uang`. `Request Barang` langsung membuka form barang dengan referensi tiket/jadwal tanpa melompat ke atas, sementara `Mulai Visit/GPS` tidak lagi menjadi aksi utama tiket dan hanya dipakai untuk jadwal/work log khusus.
- Tombol `Hapus` host di `Ping Monitoring` kini memakai dialog konfirmasi theme aplikasi, refresh daftar setelah berhasil, dan menampilkan pesan error jelas jika host tidak ditemukan atau user tidak punya izin.
- Status Outbox WhatsApp kini membedakan `Diterima Provider`, `Menunggu`, `Tersampaikan`, dan `Dibaca`. Response sukses dari Wablas yang masih `pending` tidak lagi terlihat seperti pesan sudah sampai ke penerima.
- Polling report realtime Wablas kini tidak lagi memunculkan error endpoint `422` saat provider membalas error sementara. Sistem menampilkan warning dan mencoba lagi otomatis pada polling berikutnya.
- Status device Wablas kini dicek langsung ke API Wablas setiap 30 detik saat halaman `WhatsApp Bot` provider Wablas aktif, sehingga tampilan tidak hanya bergantung pada webhook device yang bisa terlambat.
- Status pending Wablas kini dicek lewat report realtime setiap 5 detik saat halaman `WhatsApp Bot` atau `Inbox / Outbox` aktif, dan scheduler backend `wabot:sync-status` serta `wabot:process-queue` juga berjalan setiap 5 detik untuk mengurangi pesan menggantung di pending.
- Retry manual Outbox sekarang mengecek jeda akun WhatsApp lebih jelas. Jika Wablas masih meminta tunggu beberapa detik, UI menjadwalkan ulang hanya pesan yang diklik setelah jeda tersebut, bukan mengirim ulang semua pesan.
- Tombol `Kirim Ulang` di `Inbox / Outbox` kini hanya mengirim ulang pesan Outbox yang diklik. Retry manual tidak lagi memasukkan pesan ke antrian rate-limit massal, dan jika akun WhatsApp sedang jeda/rate-limit sistem menampilkan pesan jelas agar dicoba lagi nanti.
- Tab `Work` di `/absensi` kini menampilkan daftar pekerjaan/tiket ringkas yang bisa diklik untuk membuka detail mobile bersection: ringkasan, pelanggan/lokasi, penugasan, deskripsi, checklist/material, serta Reward & Punishment.
- Detail pekerjaan teknisi kini menampilkan sumber tiket, waktu dibuat, home base, teknisi/lead, tim, rute Maps, estimasi reward, syarat cair, hold payout, dan potensi punishment dengan pesan error yang jelas bila data pekerjaan belum termuat.
- Modul `Akun Saya` kini memiliki permission granular di `Role & Hak Akses`: `account.view`, `account.profile`, `account.security`, `account.bank`, `account.email`, dan `account.permissions`. Sidebar, tab, aksi profil/password, Akun Bank, Email Setting, dan tampilan hak akses mengikuti permission tersebut dengan pesan error yang jelas.
- Request Barang teknisi dari `/absensi` kini otomatis diarahkan ke `Gudang Teknisi`. Saat Gudang/Admin memproses request, sistem memastikan lokasi teknisi tersedia lalu memindahkan stok dari gudang asal ke gudang teknisi tersebut.
- Field `Gudang Asal` pada Request Barang `/absensi` kini hanya menampilkan gudang/POP yang sesuai Home Base teknisi. Jika tidak ada gudang yang cocok, teknisi mendapat pesan jelas agar admin mengecek mapping Home Base dan gudang wilayah.
- Policy Staff kini memiliki pilihan `Gudang Asal Utama Request Barang` sebagai default opsional per pengguna. Jika diisi, `/absensi` otomatis memilih gudang tersebut selama masih sesuai Home Base staff; jika tidak sesuai, penyimpanan policy ditolak dengan pesan yang menjelaskan mapping yang perlu diperiksa.
- Material proses tiket PSB di `/absensi` kini hanya menampilkan item, ONT, dan SN yang tersedia di `Gudang Teknisi`. UI menampilkan pesan jelas bila stok kabel, konektor SC, pigtail, ONT, atau item tambahan belum ada atau quantity melebihi stok teknisi.
- Form `Checklist PSB` kini memakai stok yang ada di `Gudang Teknisi`: ONT/SN, kabel, konektor SC, pigtail, dan item tambahan dipilih dari stok teknisi, lalu stok teknisi berkurang saat tiket PSB diselesaikan.
- Saat teknisi request barang yang masih ada di `Gudang Teknisi`, `/absensi` menampilkan detail stok tersisa dan meminta konfirmasi `Tetap Request Barang` sebelum request dikirim.
- Daftar `ONU / ONT` kini default menampilkan ONU yang paling baru teregistrasi/terdeteksi di posisi paling atas, dengan fallback ke waktu `created_at`, `last_seen`, lalu ID terbaru jika data registrasi tidak tersedia.
- Detail edit pelanggan kini menampilkan field `Latitude` dan `Longitude` di bagian Identitas, mendukung paste koordinat/link Google Maps, validasi range koordinat yang jelas, dan ikut menyinkronkan koordinat ke ONU yang tertaut.
- Tab `Portal` pada detail pelanggan kini memiliki tombol `Generate Email` untuk membuat mailbox Virtualmin bagi pelanggan existing, menyimpan email ke data pelanggan, dan menampilkan error jelas untuk permission, domain, format email, koneksi, login, atau kegagalan Virtualmin.
- Platform Admin `Domain Subscriber` kini memiliki tombol `Regenerate Domain Subscriber` yang membuat kandidat domain otomatis, menjalankan preview validasi, lalu meminta frasa konfirmasi sebelum mengaktifkan domain baru. Error diperjelas untuk job provisioning berjalan, kandidat domain habis, konflik domain, DNS/Cloudflare, dan domain master.
- Tombol tab import di modul `Pelanggan` kini bernama `Import Pelanggan`, bukan `Import MixRadius`, dan pesan gagal preview/import diperjelas untuk sesi, permission, ukuran CSV, validasi data, endpoint, dan server error.
- Modul `Pelanggan > Import` kini mendukung sumber CSV `Sisbro` selain MixRadius, dengan preview delimiter `;`, pengabaian baris kosong, mapping `No Pelanggan` sebagai kode pelanggan, auto-buat paket opsional, dan pesan error/solusi jelas per baris.
- Portal `/absensi` kini menampilkan foto profil/avatar user yang sudah diupload dari `/netadmin` pada halaman `Hari Ini`, header teknisi, dan halaman `Akun Saya`, dengan fallback inisial jika foto belum tersedia atau gagal dimuat.
- Field MAC Address pelanggan DHCP/Static di tambah pelanggan dan edit jaringan kini menerima format copy-paste seperti `AABB.CCDD.EEFF`, `AABBCCDDEEFF`, atau `aa-bb-cc-dd-ee-ff`, lalu otomatis menyimpan sebagai `AA:BB:CC:DD:EE:FF` dengan pesan error yang lebih jelas.
- Tiket PSB di `/absensi` kini memiliki wizard `Checklist PSB` tiga tahap: upload foto wajib, input material terpakai, lalu review sebelum `Selesaikan Tiket`.
- Foto PSB dari HP teknisi otomatis di-resize sebelum upload, tersimpan sebagai draft per kategori, dan bisa dikoreksi dengan upload ulang sebelum tiket ditutup.
- Material PSB wajib mencatat ONT/SN dari Request Barang terkait tiket, meter kabel, konektor SC, pigtail, serta item tambahan opsional; sistem memberi pesan error jelas jika data belum lengkap.
- Kartu tugas/tiket di `/absensi` kini menampilkan arahan `Langkah berikutnya` sesuai status tiket, sehingga teknisi tahu kapan harus mulai proses, update progress, atau menyelesaikan tiket.
- Tombol aksi tiket teknisi dibuat lebih bertahap: tiket baru menonjolkan `Mulai Proses`, tiket diproses menonjolkan `Update Progress` dan `Selesaikan`, sedangkan request uang/barang tetap tersedia sebagai aksi pendukung.
- Pesan error aksi tiket mobile diperjelas untuk sesi habis, permission/assignment kurang, tiket tidak ditemukan, validasi catatan, dan server error agar teknisi tidak melihat pesan teknis mentah.
- Mode sidebar `static` kini diterapkan untuk semua user tenant, termasuk user tanpa akses `settings.view`, melalui branding publik NetAdmin dan cache mode sidebar per tenant.
- Sidebar NetAdmin kini memakai permission eksplisit pada menu utama dan memaksa pindah ke halaman pertama yang diizinkan jika permission role terbaru tidak lagi mengizinkan halaman aktif.
- Visibility halaman/menu NetAdmin kini memakai `direct_permissions` dari role yang benar-benar dipilih, bukan permission hasil alias lama seperti `schedules.view` yang sebelumnya bisa ikut membuka `Absensi`.
- Preset role `teknisi` dan `viewer` tidak lagi membawa akses `ONU / ONT`, `Absensi`, `Notifikasi`, atau `Inbox / Outbox` secara bawaan; role sistem lama yang sudah tersimpan ikut dibersihkan dan seeder role tidak lagi menimpa permission yang sudah disesuaikan admin.
- Audit sidebar memastikan seluruh 48 menu utama memiliki mapping permission valid. Role sistem `teknisi` tersimpan dibersihkan lagi dari akses `Telegram Bot` dan `Email / SMTP`, serta override visibility sidebar kini memakai direct permission agar menu tidak muncul jika izin tidak dicentang.
- Endpoint baca `/notifications/settings` kini bisa diakses oleh `telegram.view` atau `smtp.view` selain `notif.view`, sehingga halaman `Telegram Bot` dan `Email / SMTP` bisa memuat konfigurasinya tanpa harus diberi izin penuh modul Notifikasi.
- Refresh global NetAdmin tidak lagi memanggil `/notifications/settings` untuk role tanpa `notif.view`, sehingga user teknisi yang tidak punya izin Notifikasi tidak mendapat error permission dari modul tersembunyi.
- Tab provider WhatsApp Bot kini dipertahankan saat refresh/render ulang. Memilih `BalesOtomatis.id` tidak lagi otomatis kembali ke `Wablas`, sementara badge `Aktif` tetap menunjukkan provider yang tersimpan di server.
- Tab `BalesOtomatis.id` pada WhatsApp Bot kini mengikuti API REST personal `send_personal_message`: opsi group, Test Grup, ignore group, dan download gambar group disembunyikan; pengiriman group dibatasi jelas ke Wablas agar Group ID tidak salah dikirim sebagai nomor personal.
- Hapus OLT di tab vendor ZTE/SNMP kini memberi pesan jelas bila inventori tetap tampil karena OLT masih dikelola SmartOLT/C-DATA, menampilkan badge sumber inventori di tabel SNMP, membersihkan duplikat OLT kosong bekas konfigurasi, dan sync SmartOLT tidak lagi membuat duplikat berdasarkan nama OLT saja.
- Tab `Akun Bank` di halaman `Akun Saya` kini dimuat untuk semua role login tanpa bergantung permission modul lain, dengan cache rekening di-reset saat user/tenant berganti agar data selalu milik akun aktif. Portal teknisi `/absensi` juga menampilkan kartu Akun Bank dan memakai rekening tersebut saat membuat Request Uang.
- Badge sumber ONU kini memprioritaskan sumber inventori utama seperti SmartOLT/C-DATA. Telemetry SNMP lama tidak lagi membuat ONU SmartOLT tampil sebagai `SNMP`, dan filter sumber ONU diselaraskan agar `ZTE SNMP` hanya berisi ONU yang memang murni SNMP.
- Role & Hak Akses kini memiliki permission eksplisit untuk halaman sistem `File Share` (`file_share.view/manage`) dan `Platform Admin` (`platform.view/manage`). Sidebar dan route File Share disinkronkan agar halaman tampil hanya jika izin terkait ada.

## 2026-06-26 - Akses Tiket Teknisi & Rekomendasi Assign
- Role `teknisi` kini hanya dapat melihat list/detail tiket yang di-assign ke dirinya, diklaim olehnya, atau mencatat dirinya sebagai anggota tim. Jika membuka tiket lain, backend memberi pesan akses yang jelas.
- `Assign Manual` tiket kini mengambil rekomendasi teknisi dari backend berdasarkan availability, home base terdekat dari koordinat case/pelanggan, beban tiket, jadwal aktif, dan jarak depot.
- Teknisi alternatif atau teknisi sibuk hanya bisa dipilih dengan `Override admin` dan alasan wajib, sehingga audit assignment lebih jelas.

## 2026-06-26 - Toggle Tombol Bantuan AI
- `Pengaturan AI` kini memiliki toggle `Tampilkan Tombol Bantuan AI` dengan default OFF per tenant. Saat OFF, tombol robot kanan atas, widget Bantuan AI halaman, dan mini button disembunyikan.
- Setting disimpan sebagai `ops_settings.ai_assistant_enabled` dan ikut dikembalikan oleh endpoint settings agar NetAdmin dan portal bisa membaca status visibilitas yang sama.

## 2026-06-26 - Insentif Teknisi

## 2026-06-26 - Assign Manual Tiket untuk Koordinator Teknisi
- Modal `Assign Manual` pada detail tiket kini menampilkan user role `Koordinator Teknisi` bersama teknisi aktif.
- Pool teknisi untuk jadwal, workload, dan home base mengikuti `technician_roles` tenant sehingga Koordinator bisa menerima pekerjaan tiket bila eligible.

- Modul `Insentif Staff` ditambahkan sebagai menu terpisah di NetAdmin di bawah area Absensi/operasional. Tab `Absensi > Insentif` dipisahkan agar Absensi fokus ke hadir, telat, izin/cuti, lembur, payroll, dan policy staff, sementara bonus, punishment, review, payout, dan riwayat insentif berada di modul khusus.
- Pengaturan komisi referral tetap berada di modul `Referral / Reseller`; jika referrer cocok dengan akun staff/teknisi melalui email atau nomor HP, bonusnya otomatis masuk ke `Insentif Staff` untuk review dan payout gabungan agar tidak ada duplikasi setting komisi.
- Tampilan `/absensi` sisi teknisi diringkas dengan sub tab `Tugas`, `Insentif`, `Uang`, dan `Barang` sehingga fitur lapangan tidak lagi menumpuk sebagai satu halaman panjang.
- Komisi `Referral` untuk staff/teknisi kini bisa masuk ke `Absensi > Insentif` sebagai sumber bonus baru. Referrer dicocokkan ke akun staff/teknisi lewat email atau nomor HP, muncul di Review Bonus, ikut payout insentif, dan saat dibayar status komisi referral ikut ditandai paid agar tidak dobel bayar.
- Detail reward pada tiket/tugas kini membawa syarat kategori dan syarat `Absensi > Insentif`; kartu tugas teknisi di `/absensi` serta detail tiket NetAdmin menampilkan hold pencairan, aturan bonus, syarat cair, dan catatan dokumentasi.
- Pilihan teknisi dan anggota tim pada `Tambah Tiket` kini memakai data availability berdasarkan `Waktu Eksekusi`, default hanya menampilkan teknisi available, dan backend menolak leader/member yang sibuk saat tiket atau jadwal dari tiket dibuat.
- KPI `Tidak Hadir` pada Absensi diperjelas sebagai hitungan `hari-staff` pada periode filter, dan perhitungan staff harian kini tidak memasukkan user yang sudah nonaktif.
- Load data `Absensi > Insentif` dibuat lebih stabil: reload halaman Absensi tidak lagi menghapus cache Insentif, response lama tidak menimpa data baru, dan jika endpoint settings/review gagal sebagian, data terakhir tetap tampil dengan pesan peringatan jelas.
- Baris `Review Bonus` di `Absensi > Insentif` kini bisa diklik untuk membuka detail lengkap review bonus: teknisi, aturan, sumber pekerjaan, nominal estimasi/disetujui, hold/release, status payout, syarat, catatan review, dan aksi Setujui/Pending/Tolak dari modal detail.
- Tampilan `Absensi > Insentif` dirapikan menjadi kartu aturan bonus/punishment yang theme-aware, grid responsif, empty state jelas, input/select/textarea konsisten dengan dark/light theme, dan validasi frontend lebih spesifik untuk nominal, hold, grace period, deskripsi, serta referensi payout.
- Pilihan `Anggota Tim` pada `Tambah Jadwal Teknisi`, `Tambah Tiket`, dan `Buat Jadwal dari Tiket` kini memakai dropdown checkbox bertema sama seperti `Tambah Tugas`, dengan validasi jelas jika `Kerja Tim` aktif tetapi belum memilih anggota tambahan.
- Modal `Tambah Tugas Lapangan` diperlebar dan dibuat lebih tinggi dengan layout khusus, panel anggota tim dirapikan menjadi grid di dalam modal, serta pesan error koordinat/rute/teknisi dibuat lebih jelas sesuai theme.
- Filter tanggal `Absensi` kini otomatis memakai tanggal hari ini saat halaman dibuka dan memakai formatter tanggal lokal agar tidak mundur sehari karena timezone.
- Payroll `Absensi` kini dimulai dari daftar staff aktif payroll, bukan hanya dari baris absensi yang sudah ada, sehingga staff tanpa absensi pada periode filter tetap tampil dengan hadir/lembur 0 dan komponen gaji sesuai Policy Staff.
- Pantun widget cuaca `/absensi` kini dipersonalisasi lebih kuat per teknisi memakai ID/email/nama/staff/tenant, stabil per hari dan kondisi cuaca, serta menampilkan pesan jelas jika identitas teknisi belum terbaca dan sistem sementara memakai Device UID.
- Detail `Penjualan > Sales Order` kini menampilkan preview invoice langsung di modal detail order: nomor invoice, status, nominal, jatuh tempo, periode, catatan, serta tombol Preview, Detail Invoice, Cetak, dan Catat Bayar bila belum lunas.
- Menambahkan tab `Absensi > Insentif` untuk konfigurasi Reward/Punishment Teknisi per tenant: aturan bonus, punishment, syarat & ketentuan, review bonus, payout, dan riwayat.
- Aturan bonus kini bisa mengatur nama aturan, sumber Tiket/Jadwal/Incident/Work Log, tipe pekerjaan, nominal, satuan hitung, pembagian team, hold pencairan, syarat dokumentasi/status, deskripsi tampil ke teknisi, dan status aktif/nonaktif.
- Aturan punishment kini bisa mengatur sumber, trigger, nominal/persen, mode otomatis atau `Perlu Review Admin`, grace period, deskripsi transparan, dan status aktif/nonaktif.
- Portal `/absensi` tab Teknisi kini menampilkan kartu `Bonus & Insentif` berisi estimasi bonus, status review, hold cair, syarat utama, dan status payout teknisi.

## 2026-06-25 - Flow Ganti Domain Subscriber
- Detail `Tiket & Helpdesk` kini menampilkan tombol `Reopen` untuk tiket `resolved/closed`; tombol ini tetap mengikuti policy reopen tenant sehingga role yang tidak diizinkan akan mendapat pesan error jelas.
- Opsi reopen tiket kini bisa dikonfigurasi dari `Pengaturan > SLA Tiket`: admin dapat memilih role staff yang boleh membuka kembali tiket selesai dan mengaktifkan pelanggan agar bisa reopen tiket miliknya dari `/portal` dengan alasan wajib.
- Default reopen tiket selesai tetap hanya Administrator atau Super Admin; teknisi tidak bisa reopen kecuali role teknisi sengaja diaktifkan di policy reopen tiket.
- Header brand `Absensi / Portal mobile staff` di `/absensi` kini hanya tampil pada halaman login; setelah staff login, area kerja mobile tampil lebih ringkas tanpa header brand tersebut.
- Portal `/absensi` kini bisa memproses tiket langsung dari HP teknisi: tombol `Proses Tiket`, `Progress`, dan `Selesaikan` pada daftar tugas/tiket aktif, dengan GPS opsional, catatan wajib yang jelas, dan validasi agar tiket tidak ditutup tanpa dokumentasi penyelesaian.
- Pesan error menu `Absensi` NetAdmin dan portal `/absensi` diperjelas untuk kondisi koneksi gagal, sesi kedaluwarsa, permission kurang, rate limit, endpoint tidak ditemukan, dan server error agar user tidak melihat pesan teknis mentah seperti `HTTP 500` atau `Failed to fetch`.
- Policy Staff Absensi kini mendukung potongan telat `Per Hari`, `Per Jam`, atau `Per Menit`, batas telat yang dianggap tidak masuk, dan daftar kategori `Potongan Lain` seperti BPJS, Asuransi, Kasbon, Koperasi, atau Lainnya.
- Memperjelas pesan error `/netadmin` untuk halaman Dashboard, Peta Jaringan, Alarm, dan Ping Monitoring: kini menampilkan penyebab seperti sesi login, permission, endpoint hilang, validasi filter, rate limit, server error, atau koneksi API.
- Memperjelas pesan error `/netadmin` untuk halaman ONU / ONT, OLT Devices, ODP / ODC Management, Network / NAS, dan PRTG Monitoring dengan box error inline plus pesan operasional untuk login API, permission, endpoint, server error, dan fallback lokal.
- Memperjelas pesan error `/netadmin` untuk halaman Pelanggan, Referral / Reseller, Mitra, Paket Internet, Hotspot / Voucher, Kontrak & Dokumen, Tiket & Helpdesk, Jadwal Teknisi, Absensi, Home Base, Lapangan, dan Catatan / Memo dengan box error inline serta detail permission, sesi API, endpoint, dan server error.
- Memperbaiki tombol `Trusted`/`Block` pada `Absensi > Device & Fraud` agar audit log device tidak lagi salah mengisi `employee_attendance_id` dengan ID device dan memicu error foreign key.
- Daftar `Absensi` di NetAdmin kini bisa diklik untuk membuka detail lengkap per baris, termasuk ringkasan waktu, GPS/Maps, radius, device, fraud score, liveness, metadata check-in/check-out, dan preview foto selfie via endpoint aman.
- Detail daftar `Absensi` kini menampilkan peta theme-aware dengan marker lokasi check-in dan check-out, garis perpindahan, link Google Maps, layout lebih ringkas, serta pesan jelas jika koordinat atau library maps belum tersedia.
- Foto check-in/check-out di detail `Absensi` dipadatkan menjadi kartu ringkas berdampingan dengan tinggi preview dibatasi, pesan kosong/gagal jelas, dan tetap mengikuti theme.
- Baris `Ringkasan`, `GPS & Radius`, dan `Device & Validasi` pada detail `Absensi` dirapikan agar label lebih pendek dan nilai tidak pecah per huruf saat modal sempit.
- Modal detail daftar `Absensi` di NetAdmin diperlebar dan dibuat layout dua kolom agar foto, Ringkasan, GPS, Device, dan metadata tampil lebih ringkas serta tidak memanjang ke bawah.
- Checkbox `Wajib Bukti` di `Absensi > Pengaturan` dirapikan menjadi grid kartu theme-aware agar tidak berdempetan atau berantakan.
- Tombol `Buka` pada detail/list tenant Platform Admin kini membuka domain tenant subscriber yang benar, misalnya `mercytech.kliniktech.com`, bukan memaksa switch tenant dari domain master.
- Dashboard `/absensi` kini menampilkan widget `Cuaca & Semangat Hari Ini` berbasis peringatan dini BMKG, salam sesuai jam server, kata penyemangat, pantun ringkas, cache backend, dan pesan jelas saat data BMKG belum bisa dimuat.
- Widget cuaca `/absensi` kini memakai GPS perangkat untuk mencocokkan lokasi user dengan polygon peringatan dini BMKG/CAP, menampilkan akurasi GPS, serta memberi pesan jelas jika izin lokasi ditolak atau tidak ada peringatan di titik user.
- Field `Area / Sumber` pada widget cuaca `/absensi` kini menampilkan desa/kecamatan/kota dari titik GPS bila tersedia, dengan fallback koordinat dan pesan jelas jika nama lokasi belum bisa dimuat.
- Pantun pada widget cuaca `/absensi` kini berbeda per teknisi berdasarkan identitas user, tanggal, dan kondisi cuaca, sehingga stabil untuk hari yang sama tetapi tidak sama antar teknisi.
- KPI `Lembur Approved` pada `/absensi` kini memakai satuan `jam`, bukan singkatan `j`.
- Menu cepat di tab `Hari Ini` pada `/absensi` dihapus agar dashboard lebih ringkas dan navigasi cukup memakai bottom tab.
- Footer `/absensi` diperbarui menjadi `© 2020-2026 PT KLINIK DIGITAL INDONESIA. All Rights Reserved`.
- Bottom tab `/absensi` disamakan lebarnya dengan area konten/kartu utama agar tidak melebar ke tepi layar.
- Daftar `Riwayat` pada `/absensi` kini bisa diklik untuk membuka detail ringkas berisi status, jam masuk/pulang, durasi, risiko, device, liveness, metadata lengkap, titik check-in/check-out, dan maps dengan pesan jelas jika koordinat belum tersedia.
- Form `Absensi > Approval Flow` kini memakai dropdown role tenant untuk Langkah 1 dan Langkah 2, mengikuti theme NetAdmin, serta menampilkan pesan error jelas bila nama flow kosong, role belum dipilih, role sama, atau role sudah nonaktif.
- Checkbox di `Absensi > Pengaturan` dirapikan menjadi grid/card theme-aware agar opsi Wajib Bukti, GPS Tracking, ilustrasi hero, dan Notifikasi Approval tidak berdempetan.
- Platform Admin kini memiliki flow khusus `Ganti Domain Subscriber`: preview domain lama/baru, cek konflik tenant lain, buat/aktifkan DNS baru terlebih dahulu, lalu cleanup DNS lama secara terpisah.
- Menambahkan endpoint guarded untuk `change-preview`, `change-apply`, dan `cleanup-old` dengan frasa konfirmasi berbeda: `CHANGE SUBSCRIBER DOMAIN` dan `DELETE OLD SUBSCRIBER DNS`.
- Pesan error provisioning domain diperjelas untuk frasa salah, domain sama, domain sudah dipakai tenant lain, DNS custom belum verified, domain lama masih primary, record ID Cloudflare kosong, credential Cloudflare belum lengkap, atau production provisioning masih terkunci.
- Detail tenant subscriber kini menampilkan kartu `Domain Subscriber` yang langsung terlihat untuk simpan metadata domain, preview ganti domain, aktifkan domain baru, dan cleanup DNS lama.
- Detail tenant dan Plan kini menampilkan limit `User VPN NAS/VPN`; tombol tambah user VPN di `Network / NAS > VPN` otomatis disabled saat limit plan tercapai dan backend mengembalikan pesan error limit yang jelas.
- Subdomain subscriber di domain platform kini boleh bebas, tidak lagi dipaksa sama dengan slug tenant. Sistem tetap memblokir `kliniktech.kliniktech.com` dan domain yang sudah dipakai tenant lain.

## 2026-06-24 - Modul Absensi Karyawan
- Menambahkan mockup HTML `Pure Native Windows` untuk konsep NetAdmin .NET WinUI/WPF di `public/mockups/netadmin-native-windows-winui.html`, plus URL pendek `public/netadmin-native.html`; mockup ini menggambarkan aplikasi native tanpa browser/webview.
- Menambahkan URL pendek `public/netadmin-desktop.html` agar mockup NetAdmin Desktop Windows mudah dibuka dari browser HP; halaman ini mengarah ke `public/mockups/netadmin-windows-desktop.html`.
- Menambahkan mockup HTML NetAdmin Desktop Windows di `public/mockups/netadmin-windows-desktop.html` untuk gambaran Electron/Tauri client: title bar Windows, sidebar, dashboard, status tray, notifikasi, dan desktop settings.
- Menambahkan tombol `A-`, `A`, dan `A+` di header `/absensi` agar user bisa mengecilkan, menormalkan, atau membesarkan font; pilihan tersimpan di perangkat user.
- Menambahkan footer rapi di `/absensi` dengan teks `Copyright @2026 ion.net.id`.
- Bukti/struk Request Uang di `/absensi` kini otomatis me-resize dan mengompres foto JPG/PNG/WEBP sebelum dikirim, dengan pesan error jelas untuk foto terlalu besar, format tidak didukung, PDF lebih dari 5 MB, atau foto gagal dibaca.
- Navigasi menu `/absensi` kini otomatis mengembalikan posisi halaman ke bagian paling atas setiap kali staff berpindah menu, sehingga tab panjang seperti Teknisi, Fitur, dan Akun tidak terbuka di posisi scroll lama.
- APK Builder kini mendukung tab `Absensi` untuk membuat APK Android yang otomatis membuka `/absensi/`, memakai template Capacitor existing, dan tetap mengikuti theme/branding Absensi dari NetAdmin.
- GPS Tracking `/absensi` ditambahkan untuk sesi visit/site work: titik lokasi tersimpan per work log, tracking berjalan saat visit aktif dan halaman terbuka, ada status aktif/stale/GPS lemah, offline queue tracking, serta pengaturan interval/akurasi/stale di NetAdmin.
- Login page `/absensi` disesuaikan seperti portal pelanggan: hero marketing-friendly, copywriting baru, benefit card, form login themed, toggle password, loading state, dan pesan error login yang lebih jelas untuk email, password, koneksi, hak akses, rate limit, dan server.
- Tombol utama kamera di `/absensi` dipoles agar theme-aware dan lebih jelas: `Buka Kamera`, `Ambil Foto`, `Check-in`, dan `Check-out` kini tampil sebagai action card dengan ikon, deskripsi, hover/focus, dan warna mengikuti branding portal.
- Pengaturan NetAdmin `Absensi > Pengaturan` kini bisa mengatur tampilan `/absensi`: judul brand, subjudul, judul/subtitle hero, mode theme default, warna aksen, warna hero, dan toggle ilustrasi hero.
- Halaman `Akun` di `/absensi` diperluas dan disesuaikan theme: profil staff, tenant, Device UID, status device, fingerprint browser, status sesi, theme switcher, install PWA, reminder, status browser, bantuan cepat, salin Device UID, dan logout aman.
- Tampilan tab `Teknisi` di `/absensi` disesuaikan ulang agar lebih dekat dengan mockup portal pelanggan: header profil, hero `Portal Teknisi`, KPI card, shortcut card 2 kolom, upload struk themed, dan bottom navigation rounded.
- `/absensi` diperluas menjadi portal teknisi mobile ala portal pelanggan: Tugas Hari Ini, Mulai/Selesai Pekerjaan, Request Uang Operasional, Upload Bukti/Struk, Request Barang, dan riwayat status request.
- Request Uang dari `/absensi` mendukung tipe operasional, bensin, reimbursement, pembelian barang, advance/kasbon, bukti wajib maksimal 5 MB, serta pesan error jelas untuk nominal, keterangan, format bukti, dan schema belum siap.
- Request Barang dari `/absensi` memakai modul Gudang existing, katalog item tenant, quantity wajib, alasan wajib, referensi tiket/jadwal, dan status pending approval Gudang/Admin.
- Liveness `/absensi` kini berjalan otomatis setelah kamera aktif; tombol `Verifikasi Liveness` tidak diperlukan lagi. Pengaturan Absensi menambahkan toggle `Liveness wajah wajib` dan input `Skor Minimal Liveness`.
- Menambahkan perluasan lanjutan `/absensi`: liveness camera, offline queue dengan retry otomatis, kalender shift mobile, approval mobile untuk admin/koordinator, QR/NFC lokasi, visit/site work, reminder browser, dan ringkasan payroll pribadi.
- Pesan error `/absensi` diperjelas untuk liveness gagal, antrian offline, approval tanpa hak akses, QR/NFC tidak valid, GPS visit, site work belum aktif, dan payroll tidak bisa dihitung.
- Memperluas portal `/absensi` dengan tampilan mobile baru: hero dashboard, KPI bulan ini, status device, checklist bukti, progres kamera/GPS, timeline riwayat, pengajuan terakhir, prompt install PWA, dan pesan error yang menampilkan saran aksi serta tabel schema yang hilang bila ada.
- Menambahkan Full Suite Absensi: portal mobile `/absensi`, device binding, anti-fraud score, mock GPS detection, Shift/Roster, Device & Fraud, Approval Flow, Payroll Detail, dan Analitik di NetAdmin.
- Menambahkan migration draft untuk `attendance_devices`, `attendance_shifts`, `attendance_rosters`, `attendance_approval_flows`, dan `attendance_request_approval_steps`; migration belum dijalankan ke production tanpa approval terpisah.
- Menambahkan endpoint mobile Absensi untuk status hari ini, riwayat pribadi, registrasi device, check-in/out kamera langsung, dan pengajuan mobile dengan pesan error jelas.
- Memperbaiki navigasi menu admin agar semua halaman selalu dibuka dari bagian atas setelah pindah menu atau render data selesai, bukan membawa posisi scroll halaman sebelumnya.
- Memperbaiki layout admin global: hanya halaman aktif ditampilkan di `#content` (tanpa menumpuk), widget Bantuan AI di luar area scroll, footer dashboard di bawah `#main`, dan tabel Pengguna dirapikan.
- Memperbaiki redirect `/netadmin` agar nginx tidak lagi membocorkan port internal `:8081`; akses tanpa trailing slash kini diarahkan relatif ke `/netadmin/`.
- Memperbaiki halaman admin kosong setelah navigasi dengan menormalisasi semua elemen `#page-*` agar menjadi child langsung `#content` sebelum route aktif dipilih.
- Menambahkan fail-safe `ensureVisibleContentPage()` agar dashboard/halaman target tetap aktif dan terlihat meski inisialisasi widget/data berhenti di tengah jalan.
- Menjadikan `#pages-root` sebagai container statis di HTML NetAdmin agar konten halaman sudah berada di area `#content` sebelum JavaScript navigasi berjalan.
- Memperbaiki tag penutup berlebih pada blok `Request Barang` yang menyebabkan `#pages-root` tertutup terlalu awal, konten admin kosong, dan footer naik ke bagian atas.
- Memperbaiki search `Role & Hak Akses` agar fokus dan posisi kursor tetap berada di kolom pencarian saat matrix permission dirender ulang.
- Menambahkan modul permission `Absensi` (`attendance.view` dan `attendance.manage`) di `Role & Hak Akses`; role lama yang punya `Jadwal Teknisi` tetap otomatis bisa mengakses Absensi lewat alias kompatibilitas.
- Memperbaiki refresh halaman NetAdmin agar `Absensi` dan `Penjualan` tidak lagi dianggap route tidak valid dan tidak terlempar ke Dashboard; sidebar juga membuka grup sesuai halaman terakhir.
- Check-in Absensi kini wajib memakai kamera langsung dari perangkat melalui pratinjau kamera live; upload/pilih foto dari galeri ditolak oleh frontend dan backend.
- Foto check-in Absensi dari kamera HP otomatis di-resize maksimal 1280px dan dikompres sebelum dikirim, dengan pesan error lebih jelas saat kamera/resize/foto gagal.
- Memperbaiki daftar `Request Barang` agar data lama berbasis mutasi stok (`inventory_movements`) tetap tampil bersama request multi-item baru, termasuk aksi approval/tolak untuk data lama.
- Memperbaiki footer login/tenant dan menyembunyikan footer dashboard admin agar tidak melebar atau muncul menyamping di area halaman.
- Pilihan Home Base di modal `Policy Staff` diubah menjadi dropdown multi-pilih yang mengikuti theme, bukan listbox native.
- Memperbaiki tombol `Hapus Semua Jadwal` agar saat konfirmasi dibatalkan tidak lagi menampilkan toast sukses seolah jadwal sudah dihapus.
- Request Barang kini mendukung 1 nomor request berisi banyak item, detail clickable, riwayat approval, serta workflow `Draft`, `Pending`, `Ditangguhkan`, `Disetujui`, `Ditolak`, `Diproses`, `Selesai`, dan `Batal`.
- Menambahkan menu `Absensi` untuk check-in/check-out, work log jadwal/tiket, approval lembur, pengaturan jam kerja, dan export CSV payroll.
- Menambahkan tab `Izin/Koreksi` untuk pengajuan izin, cuti, sakit, dan koreksi jam check-in/check-out dengan approval admin/koordinator dan alasan penolakan wajib.
- Menambahkan `Kuota Cuti`, notifikasi WA/Telegram untuk pengajuan pending, `Payroll` Absensi per periode, dan `Audit` trail untuk perubahan pengajuan, koreksi, approval, serta saldo cuti.
- Menambahkan `Policy Staff` untuk multi Home Base absensi, jadwal fleksibel per staff, gaji pokok, tunjangan, potongan, dan perhitungan Take Home Pay (THP).
- Menambahkan toggle `Staff Absensi`, `Ikut Payroll`, dan status kerja (`Aktif`, `Nonaktif`, `Kontrak`, `Freelance`, `Part-time`) di Policy Staff agar user admin/operator yang bukan karyawan absensi tidak ikut hitungan Absensi/Payroll.
- Check-in/check-out kini mendukung bukti GPS, radius kantor/home base, selfie wajib/opsional, badge bukti di tabel Absensi, dan pesan error jelas untuk izin lokasi, selfie, radius, serta schema database.
- Batas akurasi GPS default diset 50 meter; absensi ditolak bila sinyal lokasi lebih lemah dari batas tersebut.
- Filter tanggal di halaman Absensi disesuaikan dengan theme gelap/terang agar tidak lagi tampil sebagai input native putih.
- Aturan default: Senin-Sabtu 08:00-17:00, lembur Rp10.000/jam maksimal 5 jam dengan pembulatan 30 menit.
- Auto-schedule tiket kini mempertimbangkan cutoff retail/FTTH sore, sedangkan pelanggan Corporate/VIP dan incident seperti FO cut tetap dispatch sebagai emergency/on-call.
- Work log otomatis dibuat saat jadwal teknisi dimulai dan ditutup saat jadwal selesai, lalu sistem menghitung menit normal, lembur, nominal, dan status approval.

## 2026-06-24 - Perbaikan Selesaikan Semua Tiket
- Tombol `Selesaikan Semua` kini meminta catatan resolusi massal yang spesifik sebelum mengirim request.
- UI tidak lagi menandai semua tiket aktif sebagai selesai secara lokal; daftar dimuat ulang dari server setelah proses selesai.
- Endpoint admin `POST /tickets/resolve-all` kini memberi pesan yang jelas saat tidak ada tiket yang memenuhi syarat proses/dokumentasi.

## 2026-06-24 - Modul Penjualan
- Menambahkan menu `Penjualan` tenant-aware untuk lead/prospek, penawaran, sales order, pelanggan existing, dan non-pelanggan one-time order.
- Sales order mendukung item jasa, barang manual, dan item inventory; total, modal, dan margin dihitung otomatis dari item.
- Order dapat dikonfirmasi, dibuatkan invoice tipe `sales`, dibuatkan request stok keluar Gudang, dibuatkan Jadwal Teknisi, ditandai selesai, atau dibatalkan dengan alasan.
- Menambahkan laporan `sales` pada Laporan PDF untuk omzet, margin, status order, tipe order, dan tabel order periode.
- Permission baru `sales.view/create/update/approve/invoice/fulfill/delete/report` dan module flag `sales` ditambahkan ke paket tenant.
- UI Netadmin mengikuti theme existing: tab Dashboard, Leads, Penawaran, Order, Laporan, baris detail clickable, modal aksi, dan pesan error jelas untuk stok, migration, permission, item kosong, invoice, dan jadwal.

## 2026-06-24 - HA 3 Server, RADIUS, dan Tenant Domain Split
- Menambahkan endpoint `GET /api/health` untuk health check app node dan Cloudflare Load Balancer; respons `503 database_unreachable` memberi pesan jelas jika DB/Galera belum siap.
- Login dan switch tenant kini menghormati host routing: tenant master `kliniktech` tetap di `apps.ion.net.id`, sedangkan subscriber memakai `{slug}.kliniktech.com`.
- Provisioning subscriber domain dikunci ke `{slug}.kliniktech.com`; `kliniktech.kliniktech.com` diblokir dengan instruksi memakai `apps.ion.net.id`.
- Menambahkan endpoint guarded untuk provisioning Cloudflare DNS dan Virtualmin mailbox `{slug}@kliniktech.com`, dengan confirmation phrase dan metadata `tenant_domains`/`tenant_mailboxes`.
- Menambahkan runbook `HA 3 Server & RADIUS` untuk app node, Galera, FreeRADIUS HA, Cloudflare LB, tenant domain split, dan monitoring.

## 2026-06-24 - HA/MTDB Cutover Automation
- Platform Admin kini memiliki tombol guarded untuk `Production Status`, `Approve Production`, `Cutover Production`, `Rollback Dry-run`, dan `Rollback Production` pada panel Provisioning Tenant.
- Menambahkan endpoint internal Super Admin untuk approval, cutover, dan rollback production tenant DB dengan fingerprint readiness, approval ID, dan frasa konfirmasi wajib.
- Cutover production tetap memblokir tenant master `kliniktech`, wajib readiness fingerprint valid, approval metadata lengkap, maintenance window, rollback owner, dan monitoring owner.
- Rollback production mendukung dry-run tanpa write sebelum rollback aktual dijalankan.
- Dokumentasi API, Runbook HA/MTDB, dan Production Cutover Readiness diperbarui dengan workflow UI dan frasa konfirmasi yang harus diketik operator.

## 2026-06-24 - FiberHome GPON OLT
- Menambahkan dukungan vendor `fiberhome` pada modul `OLT Devices` untuk seri AN5516 dan AN5506.
- Tab FiberHome memakai tampilan sesuai vendor OLT lain, dengan form SNMP + Telnet, profile FiberHome GPON, dan pesan error jelas untuk profile, credential, SNMP, Telnet, serta ext-snmp.
- Polling status/signal ONU FiberHome berjalan via job `PollFiberhomeOnuStatusJob`; detail ONU berjalan via `PollFiberhomeOnuDetailJob`.
- Command `php artisan olt:poll` kini menerima `--vendor=fiberhome` dan scheduler FiberHome berjalan untuk status tiap menit serta detail tiap 15 menit.
- Migration menambahkan kolom `snmp_oid_base` pada tabel `olts` untuk menyimpan hasil auto-detect OID base `fh`, `hw`, atau `none`.

## 2026-06-24 - Modul Request Uang
- Menambahkan menu `Request Uang` untuk request dana operasional, reimburse/claim uang pribadi staff, claim bensin, pembelian barang, kasbon/advance, dan kebutuhan lain.
- Form tambah request wajib upload bukti/nota pembelian dengan format JPG, PNG, WEBP, atau PDF maksimal 5 MB.
- Riwayat request memiliki pencarian, filter status/tipe, pagination 20 data per halaman, KPI status, dan baris detail yang bisa diklik.
- Request hanya dapat disetujui, ditolak, dan dibayar oleh Administrator atau Super Admin; backend tetap mengunci approval meskipun tombol UI dimanipulasi.
- Pembayaran request otomatis membuat transaksi pengeluaran di `Data Keuangan` dengan sumber `money_request`, sehingga saldo Kas/Bank ikut berkurang dan audit tetap terhubung ke nota.
- Pesan error diperjelas untuk migration belum dijalankan, bukti/nota tidak valid, nominal kosong, kategori bukan pengeluaran, approval bukan admin, dan pembayaran tanpa akun Kas/Bank aktif.

## 2026-06-24 - Email Setting Akun dan Reply Inbox
- Menu `Akun Saya` kini memiliki tab `Email Setting` untuk konfigurasi incoming IMAP/POP3 dan outgoing SMTP seperti software email client.
- Default host/port/username dapat diambil dari mailbox Virtualmin tenant; operator tetap mengisi password mailbox.
- Checkbox `Sisakan email asli di server` aktif secara default. Hapus pesan di Inbox / Outbox hanya menghapus salinan lokal aplikasi, bukan email di server.
- Inbox / Outbox kini memiliki tombol `Sync Email` dan email masuk channel `Email` bisa dibalas memakai SMTP dari Email Setting akun.
- Pesan error diperjelas untuk migration belum dijalankan, ekstensi PHP IMAP belum aktif, host DNS salah, port ditolak, autentikasi mailbox gagal, dan SMTP belum lengkap.

## 2026-06-24 - Pencarian dan Filter Transaksi Keuangan
- Tab `Transaksi` pada modul `Data Keuangan` kini memiliki kolom pencarian di atas tabel.
- Transaksi dapat difilter berdasarkan tipe, akun Kas/Bank, kategori, sumber transaksi, dan periode.
- Daftar transaksi memakai pagination 20 data per halaman dengan kontrol sesuai theme.
- Pesan error diperjelas saat pencarian/filter gagal, endpoint finance belum aktif, permission kurang, atau tabel finance belum dimigrasikan.

## 2026-06-24 - Tab Hutang dan Piutang Keuangan
- Modul `Data Keuangan` kini memiliki tab `Piutang` untuk melihat invoice berstatus belum bayar atau terlambat.
- Menambahkan tab `Hutang` untuk mencatat, mengedit, menghapus, dan membayar hutang supplier/operasional.
- Pembayaran hutang otomatis membuat transaksi pengeluaran di Data Keuangan sehingga saldo Kas/Bank berkurang.
- Tab Hutang memakai tabel baru `finance_payables`; jika migration belum dijalankan, UI menampilkan pesan error yang menjelaskan perintah migration yang perlu dijalankan.
- Pesan error diperjelas untuk supplier tidak valid, kategori hutang bukan pengeluaran, akun bayar nonaktif, nominal kosong, dan hutang yang sudah lunas.

## 2026-06-24 - Detail, Edit, dan Hapus Data Keuangan
- Semua baris utama di modul `Data Keuangan` kini bisa diklik untuk membuka detail: transaksi, akun Kas/Bank, dan kategori.
- Detail menampilkan tombol sesuai theme: `Edit`, `Hapus`, dan `Tutup`; form edit memakai tombol `Simpan` dan `Tutup`.
- Menambahkan endpoint detail/update/delete untuk akun Kas/Bank, kategori, dan transaksi manual.
- Kategori sistem atau kategori/akun yang sudah dipakai transaksi tidak dihapus permanen; sistem menonaktifkannya agar riwayat tetap aman.
- Transaksi otomatis dari invoice, pembelian stok, atau transfer antar akun tidak bisa diedit/hapus langsung; pesan error menjelaskan agar operator membuat transaksi koreksi.

## 2026-06-24 - Upload Bukti Transaksi Keuangan
- Modal `Tambah Transaksi Keuangan` kini menyediakan upload bukti transfer/nota untuk transaksi `Pengeluaran` dan `Transfer antar akun`.
- Format bukti dibatasi ke JPG, PNG, WEBP, atau PDF dengan ukuran maksimal 5 MB.
- Daftar transaksi Data Keuangan menampilkan tombol `Bukti` untuk mengunduh file bukti yang sudah di-upload.
- Pesan error diperjelas jika format/ukuran file tidak valid atau migration kolom bukti transaksi finance belum dijalankan.

## 2026-06-24 - Pembelian Stok dari Kas/Bank
- Modal `Stok Masuk` kini memiliki opsi `Dibayar dari uang perusahaan / Kas-Bank`.
- Jika opsi pembayaran dicentang, stok bertambah dan sistem otomatis membuat transaksi Data Keuangan bertipe pengeluaran kategori `Perangkat / Inventaris`.
- Jika stok belum dibayar, stok tetap bisa dicatat tanpa mengurangi saldo Kas/Bank.
- Pesan error diperjelas untuk akun Kas/Bank kosong/nonaktif, tabel finance belum siap, harga beli nol, permission kurang, dan data stok masuk yang belum valid.

## 2026-06-24 - Perluasan Transaksi Data Keuangan
- Modal `Tambah Transaksi Keuangan` kini mendukung tipe `Transfer antar akun` untuk memindahkan saldo dari satu akun Kas/Bank ke akun lain.
- Transfer dicatat sebagai dua transaksi finance dengan referensi yang sama: keluar dari akun asal dan masuk ke akun tujuan.
- Transfer antar akun tidak dihitung sebagai income/pengeluaran laba rugi, sehingga laporan profit tetap fokus pada pendapatan dan biaya operasional.
- Pesan error diperjelas untuk akun tujuan kosong, akun asal dan tujuan sama, akun tidak aktif, tipe transaksi tidak valid, dan nominal/deskripsi yang belum lengkap.

## 2026-06-24 - Rekonsiliasi Pembayaran Invoice ke Kas/Bank
- Modal `Catat Pembayaran` kini menyediakan pilihan akun Kas/Bank agar pelunasan invoice langsung membuat transaksi income di Data Keuangan.
- Form `Invoice Baru` yang ditandai lunas juga dapat memilih akun Kas/Bank untuk rekonsiliasi awal.
- Backend pelunasan invoice menerima `finance_account_id`, memvalidasi akun aktif milik tenant, dan memberi pesan error jelas jika tabel finance belum siap atau akun tidak valid.
- Summary Data Keuangan tidak menghitung payment record sebagai `Income Manual`, sehingga pendapatan invoice tidak double-count di laba/rugi.

## 2026-06-24 - Modul Data Keuangan Awal
- Menambahkan menu `Data Keuangan` di grup Bisnis untuk ringkasan kas/bank, income manual, pengeluaran, piutang invoice, dan laba rugi periode.
- Backend menambahkan tabel `finance_accounts`, `finance_categories`, dan `finance_transactions` sebagai dasar kas/bank, kategori COA sederhana, dan transaksi manual.
- Laba rugi awal menghitung pendapatan invoice lunas + income manual - pengeluaran manual; saldo Kas/Bank memakai saldo awal dan transaksi manual finance.
- Pesan error dibuat jelas jika migration finance belum dijalankan, endpoint belum aktif, izin kurang, akun/kategori tidak valid, atau nominal/deskripsi transaksi belum lengkap.

## 2026-06-24 - Pagination Barang Inventaris di Atas
- Kontrol pagination daftar `Barang` pada menu Inventaris dipindahkan ke atas daftar agar jumlah item, pilihan per halaman, dan tombol pindah halaman langsung terlihat sebelum operator scroll daftar.

## 2026-06-24 - Tab Kontak Inbox / Outbox
- Menambahkan tab `Kontak` di menu Inbox / Outbox untuk melihat kontak dari pelanggan, pengguna, kontak manual, dan nomor yang muncul di riwayat pesan.
- Kontak pelanggan tetap bersumber dari data pelanggan; tabel `wa_contacts` hanya menyimpan kontak manual atau override status/tag/bot/catatan.
- Pesan error dibuat eksplisit untuk kasus migration `wa_contacts` belum dijalankan, endpoint kontak belum aktif, izin kurang, nomor tidak valid, dan nomor kontak duplikat.

## 2026-06-23 - Arsip ONU Saat OLT Dihapus
- Delete konfigurasi OLT SNMP sekarang mengarsipkan semua ONU aktif di bawah OLT tersebut, bukan hanya ONU hasil import ZTE.
- ONU yang diarsipkan dilepas dari `olt_id` dan `customer_id` agar data lama tidak muncul lagi di halaman ONU dan tidak mengganggu import/poll ulang.
- Pesan konfirmasi/toast UI diperjelas bahwa semua ONU aktif di bawah OLT ikut diarsipkan.

## 2026-06-23 - Perbaikan Suhu OLT C320
- Nilai SNMP `No Such Object/No Such Instance` sekarang dianggap data tidak tersedia, bukan suhu.
- Transformer suhu tidak lagi menambahkan `°C` pada pesan error SNMP.
- Metric suhu opsional dilewati dari snapshot jika OID tidak tersedia di firmware OLT.
- UI detail OLT mengabaikan snapshot lama yang berisi pesan `No Such Object`, sehingga tampilan kembali menjadi `—`.

## 2026-06-23 - Perbaikan Halaman ONU
- Halaman ONU kini menampilkan fallback nama yang lebih jelas: nama pelanggan/SNMP/CLI bila ada, lalu serial atau PON jika nama perangkat masih generik seperti ONU/EPON/GPON.
- Pencocokan status ZTE C320 diperbaiki untuk OID yang memakai variasi encoding PON/ONU pada `snmp_index`, sehingga status CLI tidak lagi banyak jatuh ke `unknown`.
- Status `unknown` di frontend dapat memakai flag SNMP/CLI online bila metadata poll sudah menunjukkan Online/working.
- Setelah poll ulang C320, status `unknown` turun menjadi 0 dan 1.339 ONU terbaca online.

## 2026-06-23 - Banner Pengembangan HIOSO
- Banner tab HIOSO diperjelas menjadi “HIOSO EPON — Fitur masih dalam tahap pengembangan”.
- Menambahkan badge `Discovery Lab` agar operator tahu fitur masih read-only untuk Test SNMP dan Poll Ringan GETBULK.

## 2026-06-23 - HIOSO EPON Discovery Lab
- Menambahkan modul/tab HIOSO di OLT Devices untuk OLT EPON HA7302CS/CST, HA7302V, HA7304, dan HA7308VX.
- Menambahkan profile SNMP `HIOSO EPON Read-only` dengan metric MIB-II dasar: sysDescr, sysName, sysUptime, dan summary interface.
- Polling walk table HIOSO memakai SNMP GETBULK (`snmpbulkwalk`) karena metric bertipe `walk` sudah diarahkan ke `SnmpClient::bulkWalk`.
- Backend menerima `vendor_hint=hioso` dan menyimpan source OLT sebagai `hioso_snmp`.
- UI menampilkan banner bahwa HIOSO masih tahap discovery; import ONU detail/provisioning menunggu validasi OID private per firmware.

## 2026-06-23 - GETBULK VSOL dan HSGQ
- Polling SNMP walk untuk metric table umum sekarang memakai SNMP GETBULK (`snmpbulkwalk`) melalui `SnmpClient::bulkWalk`.
- Berlaku untuk profile VSOL dan HSGQ read-only yang memakai MIB-II/interface table, dengan fallback otomatis ke `snmpwalk` bila `snmpbulkwalk` tidak tersedia.
- Snapshot metric menandai `method: getbulk` untuk walk table agar sumber polling lebih jelas saat troubleshooting.

## 2026-06-23 - Kolom Tabel ONU Lengkap
- Tabel ONU diselaraskan dengan kolom operasional: Nama, Serial, Sumber, OLT/PON, Zone, ODB, Status, ONU/OLT Rx, Tx Mbps, Rx Mbps, dan Uptime.
- Mapping data optical diperkuat agar ZTE/SNMP membaca fallback `cli_onu_rx_power`, `cli_onu_tx_power`, dan `olt_rx_power` dari metadata polling.
- Zone/ODB tidak lagi dibatasi hanya SmartOLT; jika metadata Zone/ODB tersedia dari sumber lain, tabel akan menampilkannya.

## 2026-06-23 - Parameter Polling SNMP ONU
- SNMP tetap stateless v2c; tidak dibuat connection pool untuk SNMP.
- Timeout SNMP disesuaikan menjadi 3 detik dengan retry 2 kali.
- GETBULK max-repetitions dinaikkan menjadi 100 untuk table ONU besar.
- Polling per OLT diberi offset 5 detik saat process pool memulai worker OLT, agar request antar OLT tidak meledak bersamaan.
- Status tetap per 1 menit dan inventory terpisah per 1 jam.
- Catatan optimasi lanjutan: bulk upsert database perlu tahap terpisah karena update saat ini masih merge meta per ONU agar tidak menimpa data provider/customer.

## 2026-06-23 - Pemisahan Polling ONU Dinamis dan Inventory
- Polling ONU per 1 menit disesuaikan hanya untuk data dinamis penting: online/offline, RX/TX optical, LOS/Dying Gasp, dan registration state.
- Discovery/inventory ONU seperti serial number, model ONU, firmware ONU, nama, dan deskripsi tidak lagi dijalankan tiap menit.
- Inventory ONU dijadwalkan terpisah setiap 1 jam melalui `olt:poll-onu-complete --workers=100 --with-inventory`; interval bisa dinaikkan sampai 6 jam jika trafik OLT perlu lebih ringan.
- Traffic counter/rate tidak ikut default 1 menit; aktifkan opsi `--with-traffic` jika dibutuhkan.

## 2026-06-23 - ZTE ONU Provisioning Detail Parser
- Membaca paket `olt-onu-polling-package.zip` dan mengintegrasikan bagian yang aman ke polling ZTE existing tanpa mengganti model/tabel OLT/ONU Kliniktech.
- Polling provisioning ZTE via CLI sekarang dapat menangkap detail tambahan: mode ONU, line profile, service profile, TR-069, IP management, WAN config, PPPoE user, dan VLAN table.
- Hasil detail tambahan disimpan ke `meta` ONU dengan prefix `zte_*`, sehingga tidak mengubah schema produksi.
- Menambahkan queue connection `olt` dan RateLimiter `olt-snmp`/`olt-telnet` sebagai persiapan worker OLT tanpa mengubah scheduler polling existing.
- Setelah tambah OLT ZTE baru, UI otomatis validasi SNMP, validasi CLI, lalu import ONU jika semua koneksi OK; jika gagal, pesan error menyebut tahap yang bermasalah.

## 2026-06-23 - Customer Package Correction
- Menambahkan tombol `Koreksi` paket pada detail pelanggan khusus Super Admin.
- Koreksi paket salah registrasi wajib menyertakan alasan audit dan memakai endpoint backend terpisah dari flow upgrade normal.
- Invoice pelanggan yang belum lunas dapat dihitung ulang ke harga paket baru; koreksi ditolak jika invoice sudah lunas, punya tanggal bayar, atau sudah punya payment record.
- Tiket terbuka pelanggan diberi komentar internal berisi paket lama, paket baru, dan alasan koreksi; deskripsi/resolusi tiket ikut diperbarui jika masih memuat paket lama.
- Pesan error dibuat jelas untuk permission Super Admin, paket tidak aktif, tipe layanan tidak cocok, alasan kurang jelas, dan invoice yang sudah memiliki pembayaran.

## 2026-06-23 - ONU Complete Poll 1 Menit
- Menambahkan command `olt:poll-onu-complete` untuk update data monitoring ONU per OLT setiap 1 menit.
- Scheduler menjalankan process pool per OLT dengan parameter `--workers=100` dan lock per OLT supaya polling lama tidak ditumpuk.
- Session SSH/Telnet ZTE dibuat persisten selama satu proses poll, sehingga command status/attribute/optical tidak login ulang berkali-kali ke OLT yang sama.
- Poll per-menit mencakup serial/status, traffic, dan optical; provisioning/config detail dipisahkan via opsi `--with-provisioning` karena terlalu berat untuk interval 1 menit.
- Poll SNMP lama diberi `withoutOverlapping` untuk mengurangi risiko bentrok dengan poll ONU lengkap.
- Catatan C300: OLT C300-TBN masih perlu validasi OID traffic C300 dan durasinya lebih berat daripada C320.

## 2026-06-23 - ZTE SNMP Bulk Polling
- Pola polling ZTE diubah memakai SNMP GETBULK (`snmpbulkwalk`) untuk indexed table ONU besar, dengan fallback otomatis ke `snmpwalk` jika bulk belum tersedia.
- Data ONU ZTE lama dari import `zte_snmp`/`zte_olt` dibersihkan lalu diimport ulang agar inventory mengikuti hasil polling baru.
- Pesan error traffic/optical/serial ZTE dibuat lebih jelas untuk community, UDP 161, ACL/firewall, firmware, dan validasi OID C300/C320.
- Catatan C300: traffic counter masih perlu validasi OID khusus firmware jika muncul error `OID traffic ONU belum menghasilkan data`.

## 2026-06-23 - HSGQ OLT Discovery Lab
- Menambahkan tab HSGQ di OLT Devices dengan banner bahwa fitur masih dalam tahap pengembangan.
- Menambahkan profil SNMP beta read-only untuk HSGQ GPON dan EPON, sehingga operator bisa test koneksi dan poll ringan tanpa menjalankan provisioning/authorize ONU.
- Pesan error SNMP diarahkan ke pengecekan IP management, community, UDP 161, ACL/firewall OLT, gateway, dan routing server aplikasi.

## 23 Jun 2026 - VSOL OLT Read-only SNMP

- Menambahkan tab `VSOL` pada menu `OLT Devices` untuk tahap awal integrasi OLT VSOL.
- VSOL memakai SNMP read-only untuk `Test SNMP` dan `Poll Ringan` MIB-II tanpa menjalankan provisioning/authorize ONU.
- Backend SNMP OLT dibuat vendor-aware sehingga source OLT bisa tersimpan sebagai `vsol_snmp`, `zte_snmp`, atau `generic_snmp`.
- Pesan error SNMP dibuat lebih jelas untuk community salah, timeout, UDP 161/firewall, routing management, izin user, dan SNMP client server belum tersedia.
- Menambahkan profile `VSOL GPON Read-only` melalui migration/seed; jika profile belum dimigrasikan, form VSOL tetap dapat fallback ke `Generic SNMPv2` dengan vendor hint VSOL.

## 23 Jun 2026 - DHCP VyOS Pending Binding dan Isolir

- Memperbaiki form tambah pelanggan agar MAC ONU/CPE benar-benar opsional untuk DHCP/Static; jika MAC belum diketahui, operator dapat lanjut dan melakukan pending binding dari DHCP lease setelah perangkat online.
- Menambahkan dokumentasi flow pelanggan DHCP/Static tanpa MAC: pelanggan dibuat dulu, lalu lease VyOS yang sudah online dihubungkan ke pelanggan.
- Menjelaskan mekanisme isolir telat bayar berbasis address-group VyOS `KT_ISOLATED_CUSTOMERS` agar tidak bergantung pada masa berlaku DHCP lease.
- Menambahkan catatan error operator yang lebih jelas untuk lease tanpa IP/MAC, MAC duplikat, pelanggan bukan DHCP/Static, dan NAS VyOS tidak aktif.


## 2026-06-23

### Ticket Attachment Upload UI

- Menyamakan tampilan tombol browse/upload lampiran di modul Tiket & Helpdesk dengan theme aplikasi.
- Form Tiket Baru dan tab Lampiran detail tiket menampilkan nama file secara responsif serta error inline.
- Gambar lampiran besar di-resize otomatis sebelum upload; PDF/DOC tetap divalidasi maksimal 10 MB.
- Pesan error upload lampiran dibuat lebih jelas untuk format, ukuran, permission, sesi login, dan error server/storage.

### Ticket Resolution Documentation Guard

- Menambahkan validasi backend agar tiket tidak bisa diselesaikan tanpa proses dan dokumentasi penanganan.
- Tiket harus pernah diproses/check-in/jadwal berjalan dan memiliki catatan resolusi jelas, komentar aktivitas, lampiran/foto, atau bukti check-in.
- `Selesaikan Semua` hanya menyelesaikan tiket yang memenuhi syarat; tiket lain dilewati dengan pesan jelas.
- UI tiket menampilkan hint syarat penyelesaian dan error yang lebih spesifik.

### Inventory And Customer Refresh Buttons

- Menambahkan tombol `Refresh` pada daftar item Inventaris dan daftar Pelanggan.
- Refresh Inventaris memuat ulang item, kategori, mutasi terbaru, dan jumlah approval.
- Refresh Pelanggan memuat ulang daftar pelanggan dan filter OLT.
- Pesan error refresh dibuat lebih jelas untuk sesi login, permission, endpoint, dan error server.

### Inventory Item Delete Guard

- Menambahkan tombol `Hapus` pada item inventaris untuk Super Admin saja.
- Backend menolak hapus item dari role selain Super Admin dengan pesan yang jelas.
- Item yang sudah punya riwayat stok, mutasi, serial number, atau aset akan dinonaktifkan, bukan dihapus permanen, agar histori gudang tetap aman.
- KB Gudang & Inventory diperbarui dengan panduan hapus item.

## 2026-06-22

### Phase 4AU Production Status API

- Menambahkan endpoint read-only `GET /api/platform/tenants/{tenant}/provisioning/tenant-db/production-status`.
- Endpoint mengembalikan cutover status, approval, module routing, feature flags, dan rollback readiness tanpa menulis perubahan.
- Routes, API docs, OpenAPI, dan audit log status diperbarui.

## 2026-06-22

### Phase 4AT Production Status Command

- Menambahkan command `tenant-db:production-status` untuk melihat status cutover, approval, module routing, feature flags, dan rollback metadata.
- Command mendukung `--summary-only` dan tidak menulis perubahan.
- Help test dan smoke validation diperbarui.

## 2026-06-22

### Phase 4AS Production Rollback Command

- Menambahkan command `tenant-db:rollback-production` berbasis metadata rollback yang disimpan saat cutover.
- Command mendukung `--dry-run` untuk validasi tanpa write dan membutuhkan `--confirm-rollback` untuk rollback aktual.
- Help test dan readiness package diperbarui.
- Smoke validation post-cutover tetap menjaga tenant master `kliniktech` di shared DB.

## 2026-06-22

### Phase 4AR Guarded Production Approval And Cutover

- Menambahkan service production approval/cutover berbasis metadata `tenant_databases.meta`.
- Menambahkan command `tenant-db:approve-production` untuk mencatat approval eksplisit dengan approver, window, rollback owner, monitoring owner, reason, dan fingerprint.
- Menambahkan command `tenant-db:cutover-production` yang wajib memakai approval ID, fingerprint match, dan flag `--confirm-production-cutover`.
- Readiness snapshot/gate sekarang bisa menampilkan approval production valid, tetapi tenant master `kliniktech` tetap ditolak.
- Cutover mengaktifkan feature flags tenant DB read/module routing dan mencatat metadata rollback/audit.

## 2026-06-22

### Phase 4AQ Commands-only Verify Status Sync

- Output `tenant-db:readiness --commands-only` sekarang ikut mencetak `verify_status_command`.
- Unit test diperbarui agar semua helper status tercakup dalam output command list.
- Production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AP Verify CLI Status-only Output

- Command read-only `tenant-db:readiness-verify` sekarang mendukung opsi `--status-only` dengan output `verified` atau `blocked`.
- Payload readiness snapshot menyertakan `evidence.verify_status_command`.
- Platform Admin menampilkan `Verify Status Command` dan tombol copy-nya.
- Docs dan unit tests diperbarui; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AO Readiness CLI Status-only Output

- Command read-only `tenant-db:readiness` sekarang mendukung opsi `--status-only` dengan output `ready` atau `blocked`.
- Payload readiness snapshot menyertakan `evidence.status_command`.
- Platform Admin menampilkan `Status Command` dan tombol copy-nya.
- Docs dan unit tests diperbarui; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AN Evidence Commands Command Helper

- Payload readiness snapshot sekarang menyertakan `evidence.commands_command`.
- Platform Admin menampilkan `Commands Command` dan tombol `Copy Commands Command`.
- API docs, OpenAPI, readiness package, dan unit tests diperbarui.
- Field helper tidak masuk `evidence.hash_fields`; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AM Readiness CLI Commands-only Output

- Command read-only `tenant-db:readiness` sekarang mendukung opsi `--commands-only`.
- Output mencetak semua command helper evidence: fingerprint, summary, verify, verify summary, decision, dan operator action.
- Help text, readiness package, dan unit tests diperbarui agar operator/script bisa mengambil semua helper tanpa parsing JSON penuh.
- Production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AL Evidence Summary Command Helpers

- Payload readiness snapshot sekarang menyertakan `evidence.summary_command` dan `evidence.verify_summary_command`.
- Platform Admin menampilkan dan menyediakan tombol copy untuk kedua command summary tersebut.
- API docs, OpenAPI, readiness package, dan unit tests diperbarui agar helper `--summary-only` terdokumentasi.
- Field helper tidak masuk `evidence.hash_fields`; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AK Readiness CLI Summary-only Output

- Command read-only `tenant-db:readiness` dan `tenant-db:readiness-verify` sekarang mendukung opsi `--summary-only`.
- Output summary mencetak evidence kunci per baris agar script/operator tidak perlu parsing JSON penuh.
- Unit test dan help text diperbarui untuk menjaga behavior compact output.
- Production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AJ Readiness Snapshot Summary Copy

- Platform Admin hasil `Readiness Snapshot` sekarang punya tombol `Copy Snapshot Summary` dan `Copy Snapshot JSON`.
- Summary berisi tenant, ok, fingerprint, version, pilot gate, production lock, write status, dan deferred ops.
- Perubahan hanya UX read-only untuk mempercepat evidence handoff operator; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AI Verify Result Summary Copy

- Platform Admin hasil `Verify Fingerprint` sekarang punya tombol `Copy Verify Summary` dan `Copy Verify JSON`.
- Summary berisi tenant, matched, decision, scope, expected/current fingerprint, production lock, dan operator action.
- Perubahan hanya UX read-only untuk mempercepat evidence handoff operator; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AH Verify Fingerprint Copy Buttons

- Platform Admin hasil `Verify Fingerprint` sekarang punya tombol `Copy Expected Fingerprint` dan `Copy Current Fingerprint`.
- Tombol ini membantu operator menyalin bukti mismatch/current snapshot tanpa membuka JSON penuh.
- Perubahan hanya UX read-only; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AG Verify Result Copy Buttons

- Platform Admin sekarang menampilkan tombol `Copy Decision` dan `Copy Operator Action` pada hasil `Verify Fingerprint`.
- Helper copy readiness dibuat lebih generik agar cocok untuk command, fingerprint, decision, dan operator action.
- Perubahan hanya UX read-only untuk memudahkan operator menyalin evidence; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AF Readiness CLI Output Coverage

- Menambahkan unit test murni untuk output command read-only `tenant-db:readiness`.
- Coverage memastikan `--fingerprint-only` mencetak `evidence.fingerprint` saja.
- Coverage memastikan default output tetap JSON, `production_cutover.allowed=false`, dan `write_performed=false`.
- Coverage memastikan exit failure tetap dipertahankan saat snapshot readiness blocked.
- Refactor kecil memindahkan output/exit-code ke helper statis agar bisa diuji tanpa koneksi database.

## 2026-06-22

### Phase 4AE Readiness CLI Help Text

- Signature command `tenant-db:readiness` sekarang mendokumentasikan argument tenant, `--limit`, dan `--fingerprint-only` di output `php artisan help`.
- Signature command `tenant-db:readiness-verify` sekarang mendokumentasikan argument fingerprint, `--decision-only`, dan `--operator-action-only`.
- Unit test menahan help text penting agar opsi compact output tetap discoverable untuk operator/script.
- Perubahan hanya dokumentasi CLI/help; command tetap read-only dan tidak menjalankan production cutover.

## 2026-06-22

### Phase 4AD Verify CLI Output Coverage

- Menambahkan unit test murni untuk output command read-only `tenant-db:readiness-verify`.
- Coverage memastikan `--decision-only` mencetak kode decision saja.
- Coverage memastikan `--operator-action-only` mencetak instruksi operator saja.
- Coverage memastikan default output tetap JSON dan exit failure tetap dipertahankan saat evidence mismatch/blocked.
- Test tidak membutuhkan koneksi database dan tidak menyentuh cutover maupun database production.

## 2026-06-22

### Phase 4AC Verify Operator Action-only CLI

- Command read-only `php artisan tenant-db:readiness-verify {tenant_slug} {fingerprint}` sekarang mendukung opsi `--operator-action-only`.
- Payload readiness snapshot menyertakan `evidence.operator_action_command` untuk mengambil instruksi operator tanpa parsing JSON penuh.
- Platform Admin menampilkan `Operator Action Command` dan tombol `Copy Operator Action Command`.
- API docs, OpenAPI, readiness package, dan unit tests diperbarui; field helper tidak masuk `evidence.hash_fields`.
- Production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AB Evidence Decision Command Helper

- Payload readiness snapshot sekarang menyertakan `evidence.decision_command`.
- Platform Admin menampilkan `Decision Command` dan tombol `Copy Decision Command` untuk mengambil kode decision tanpa parsing JSON penuh.
- API docs, OpenAPI, readiness package, dan unit tests diperbarui agar command helper decision terdokumentasi.
- Field helper tidak masuk `evidence.hash_fields`; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4AA Verify Decision-only CLI

- Command read-only `php artisan tenant-db:readiness-verify {tenant_slug} {fingerprint}` sekarang mendukung opsi `--decision-only`.
- Opsi ini mencetak hanya kode `decision` seperti `evidence_verified_pilot_ready`, `evidence_mismatch`, atau `master_shared_db_not_applicable` untuk kebutuhan script/operator.
- Default output JSON tidak berubah dan production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4Z Verify Fingerprint Decision Codes

- Hasil `verifyFingerprint()` sekarang menyertakan `decision` dan `operator_action` agar API/CLI/UI memberi instruksi aman, bukan hanya boolean.
- Decision mencakup `evidence_verified_pilot_ready`, `evidence_mismatch`, `invalid_fingerprint`, `evidence_matched_but_blocked`, dan `master_shared_db_not_applicable`.
- Platform Admin, API docs, OpenAPI, readiness package, audit metadata, dan unit tests diperbarui untuk field baru ini.
- Production cutover tetap tidak dijalankan dan tetap `production_cutover_allowed=false`.

## 2026-06-22

### Phase 4Y Matched Fingerprint Blocked Snapshot Coverage

- Menambahkan unit test untuk kondisi fingerprint cocok tetapi readiness snapshot tetap blocked.
- Test memastikan `matched=true` tidak membuat hasil verify menjadi OK jika `pilot_gate_green=false` atau masih ada write operation deferred.
- Production cutover tetap `production_cutover_allowed=false` dan `write_performed=false`.

## 2026-06-22

### Phase 4X API Docs Evidence Command Helpers

- Dokumentasi API dan OpenAPI untuk endpoint `readiness-snapshot` sekarang menyebut `evidence.fingerprint_command` dan `evidence.verify_command`.
- Docs kini selaras dengan payload API dan UI Platform Admin yang menampilkan command helper evidence.
- Perubahan ini hanya sinkronisasi dokumentasi; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4W Copy Evidence Fingerprint di UI

- Panel `Readiness Snapshot` menambahkan tombol `Copy Evidence Fingerprint`.
- Operator dapat menyalin hash mentah untuk form approval, endpoint verify, atau dokumentasi evidence tanpa menyalin command penuh.
- Perubahan ini hanya UI helper read-only; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4V Copy Evidence Command di UI

- Panel `Readiness Snapshot` menambahkan tombol `Copy Fingerprint Command` dan `Copy Verify Command`.
- Tombol copy memakai clipboard browser dan toast status, sehingga operator tidak perlu menyeleksi command evidence secara manual.
- Perubahan ini hanya UI helper untuk command read-only; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4U Evidence Command Helpers di UI

- Panel `Readiness Snapshot` di Platform Admin sekarang menampilkan `Fingerprint Command` dan `Verify Command`.
- Operator dapat melihat command helper langsung dari UI tanpa membuka JSON mentah.
- Perubahan ini hanya menampilkan evidence command read-only; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4T Evidence Command Helpers

- Payload readiness snapshot sekarang menyertakan `evidence.fingerprint_command` dan `evidence.verify_command`.
- Command helper memudahkan operator menyalin perintah CLI untuk mengambil fingerprint dan memverifikasi evidence tanpa menebak sintaks.
- Field helper tidak masuk `evidence.hash_fields`, sehingga fingerprint evidence tetap stabil.

## 2026-06-22

### Phase 4S Readiness Fingerprint-only CLI

- Command read-only `php artisan tenant-db:readiness {tenant_slug}` sekarang mendukung opsi `--fingerprint-only`.
- Opsi ini mencetak hanya `evidence.fingerprint` agar operator bisa menyalin hash untuk `tenant-db:readiness-verify` tanpa parsing JSON penuh.
- Default output JSON tidak berubah; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4R Verify Fingerprint Decision Matrix

- Menambahkan tabel `Interpretasi Verify Fingerprint` di readiness package production.
- Matrix menjelaskan tindakan operator untuk `matched=true`, `matched=false`, fingerprint invalid, dan `environment_scope=shared_db_master`.
- Dokumen menegaskan fingerprint cocok hanya evidence pilot, bukan approval production; mismatch harus menghentikan pembahasan approval sampai snapshot baru diambil.

## 2026-06-22

### Phase 4Q Verify Fingerprint Mismatch Coverage

- Menambahkan unit test untuk fingerprint SHA-256 valid tetapi tidak cocok dengan readiness snapshot terbaru.
- Test memastikan mismatch mengembalikan `matched=false`, tetap mengembalikan `current_fingerprint` untuk investigasi, dan tidak membuka production cutover.
- Production cutover tetap `production_cutover_allowed=false` dan `write_performed=false`.

## 2026-06-22

### Phase 4P Verify Fingerprint Production Lock Copy

- Hasil `Verify Fingerprint` di Platform Admin sekarang menampilkan catatan eksplisit bahwa fingerprint cocok hanya memvalidasi evidence readiness.
- UI menegaskan hasil verify bukan approval production; production cutover tetap terkunci sampai approval eksplisit, window maintenance, rollback owner, dan monitoring owner ditetapkan.
- Perubahan ini hanya hardening copy UI dan tidak menjalankan cutover.

## 2026-06-22

### Phase 4O Verify Fingerprint UI Detail

- Panel `Verify Fingerprint` di Platform Admin sekarang menampilkan `Expected Fingerprint`, `Current Fingerprint`, `Evidence Version`, dan `Scope`.
- Operator dapat membandingkan fingerprint bukti langsung dari UI tanpa membuka JSON mentah atau shell.
- Perubahan ini hanya meningkatkan visibilitas evidence; production cutover tetap tidak dijalankan.

## 2026-06-22

### Phase 4N Master Verify Fingerprint Guard Coverage

- Menambahkan unit test untuk verifikasi fingerprint tenant master `kliniktech`.
- Test memastikan fingerprint master dapat `matched=true`, tetapi hasil tetap `ok=false`, `environment_scope=shared_db_master`, `production_cutover_allowed=false`, dan `write_performed=false`.
- Guard ini menegaskan tenant master tetap di shared DB dan tidak pernah menjadi kandidat cutover tenant DB.

## 2026-06-22

### Phase 4M Readiness Evidence Audit Trail

- Audit log `tenant.db.readiness_snapshot` sekarang menyimpan `evidence_fingerprint` dan `evidence_version`.
- Audit log `tenant.db.readiness_snapshot_verify` sekarang menyimpan `expected_fingerprint`, `current_fingerprint`, `evidence_version`, dan status `matched`.
- Perubahan ini hanya memperkuat traceability evidence approval; tidak menjalankan production cutover dan tidak mengubah routing tenant.

## 2026-06-22

### Phase 4L Verify Fingerprint Service Coverage

- Menambahkan unit test khusus untuk `TenantDatabaseReadinessSnapshotService::verifyFingerprint()`.
- Test memastikan fingerprint uppercase tetap dinormalisasi dan cocok terhadap snapshot terbaru tanpa membuka production cutover.
- Test memastikan fingerprint invalid ditolak tanpa menjalankan checker tenant DB, dengan `production_cutover_allowed=false` dan `write_performed=false`.

## 2026-06-22

### Phase 4K Evidence Fingerprint Verify API/UI

- Menambahkan endpoint read-only `GET /api/platform/tenants/{tenant}/provisioning/tenant-db/readiness-snapshot/verify?fingerprint={sha256}`.
- Platform Admin menambahkan input dan tombol `Verify Fingerprint` pada panel `Readiness Snapshot`.
- Endpoint mengembalikan `matched=true/false` tanpa menjalankan cutover; `production_cutover_allowed=false` dan `write_performed=false` tetap eksplisit.
- Dokumen readiness production menambahkan opsi verifikasi fingerprint dari UI/API selain CLI.

## 2026-06-22

### Phase 4J Evidence Fingerprint Verify CLI

- Menambahkan command read-only `php artisan tenant-db:readiness-verify {tenant_slug} {fingerprint}`.
- Command membandingkan fingerprint evidence yang dicatat dengan snapshot readiness terbaru dan mengembalikan status `matched`.
- Output tetap menegaskan `production_cutover_allowed=false` dan `write_performed=false`; command ini tidak menjalankan cutover.
- Dokumen readiness production menambahkan langkah verifikasi fingerprint sebelum approval production dibahas.

## 2026-06-22

### Phase 4I Evidence Fingerprint Stability

- Menambahkan unit test yang memverifikasi `evidence.fingerprint` dihitung hanya dari field readiness inti.
- Test memastikan `generated_at` tidak masuk `evidence.hash_fields`, sehingga fingerprint tidak berubah hanya karena snapshot diambil ulang pada waktu berbeda.
- Production cutover tetap tidak dijalankan; perubahan ini hanya hardening evidence readiness.

## 2026-06-22

### Phase 4H Readiness Evidence Fingerprint

- Readiness snapshot CLI/API sekarang menyertakan `evidence.version` dan `evidence.fingerprint` SHA-256.
- Fingerprint dihitung dari field readiness inti tanpa timestamp agar operator punya ID bukti yang dapat dicatat saat approval.
- Platform Admin menampilkan `Evidence Fingerprint` di panel `Readiness Snapshot`.
- Dokumen readiness production meminta operator mencatat fingerprint sebelum membahas approval production.

## 2026-06-22

### Phase 4G Readiness Snapshot Master Guard

- Readiness snapshot sekarang memiliki early guard untuk tenant master `kliniktech`.
- Snapshot master tenant langsung mengembalikan `environment_scope=shared_db_master`, `production_cutover.allowed=false`, dan `write_performed=false`.
- Guard ini mencegah health/reconciliation/write-plan tenant DB dijalankan untuk tenant master yang tetap berada di shared DB `apps.ion.net.id`.
- Menambahkan unit test agar guard master tenant tidak regress.

## 2026-06-22

### Phase 4E Readiness Snapshot API

- Menambahkan service reusable untuk readiness snapshot agar CLI dan API memakai format evidence yang sama.
- Menambahkan endpoint read-only `GET /api/platform/tenants/{tenant}/provisioning/tenant-db/readiness-snapshot`.
- Platform Admin menambahkan tombol `Readiness Snapshot` untuk menampilkan pilot health, reconciliation, write-plan, cutover gate, dan lock production cutover dalam satu panel.
- Endpoint dan UI tetap tidak menjalankan production cutover; `production_cutover.allowed=false` dan `write_performed=false`.

## 2026-06-22

### Phase 4D Readiness Snapshot CLI

- Menambahkan command read-only `php artisan tenant-db:readiness {tenant_slug}` untuk menghasilkan snapshot HA/MTDB standar tanpa tinker.
- Snapshot menggabungkan pilot health, reconciliation summary, write-plan summary, dan cutover gate dalam satu JSON evidence package.
- Output command tetap eksplisit: production cutover tidak dijalankan, `production_cutover.allowed=false`, dan approval production masih wajib.

## 2026-06-22

### Readiness Package Lebih Mudah Diakses

- Menambahkan kartu `HA/MTDB Readiness` di halaman utama Knowledge Base.
- Platform Admin menambahkan tombol `Readiness Package` di panel Provisioning Tenant dan link dari hasil `Cutover Gate`.
- Operator dapat membuka checklist approval, rollback drill, monitoring plan, dan go/no-go tanpa menjalankan production cutover.

## 2026-06-22

### Phase 4C Production Cutover Readiness Package

- Menambahkan dokumen `mtdb-production-readiness.html` sebagai paket readiness setelah cutover gate pilot hijau.
- Paket readiness mencakup snapshot pilot, approval checklist, pre-cutover evidence, rollback drill, monitoring plan, dan go/no-go production.
- Status tetap No-go untuk production cutover sampai approval eksplisit, window maintenance, rollback owner, dan monitoring owner ditetapkan.

## 2026-06-22

### Hardening Gate Pilot vs Production Cutover

- Cutover gate response sekarang eksplisit membedakan `pilot_gate_green` dari `production_cutover_allowed`.
- Production cutover tetap `production_cutover_allowed=false` dan `production_approval_status=required_not_granted` meskipun gate pilot hijau.
- Platform Admin mengganti label `Cutover Allowed` menjadi `Pilot Gate Green` dan `Production Cutover Allowed` agar operator tidak salah mengeksekusi cutover production.

## 2026-06-22

### Phase 4B Cutover Gate Pilot Green

- Menambahkan schema tenant pilot untuk `ticket_comments` dan `ticket_attachments` agar jalur write tiket komentar/lampiran tidak lagi deferred.
- Write-plan tenant DB sekarang mengecek keberadaan schema komentar/lampiran di tenant DB sebelum menandai `tickets.comment_or_attachment` siap pilot.
- Menjalankan migration tenant schema ulang untuk `demo-kliniktech`; tabel `ticket_comments` dan `ticket_attachments` terbentuk di tenant DB pilot.
- Verifikasi `demo-kliniktech`: write-plan `operation_total=9`, `ready_total=9`, `deferred_total=0`; cutover gate pilot `cutover_allowed=true`, `blocks=0`.
- Ini hanya membuat cutover gate hijau di pilot. Production cutover tetap belum dijalankan dan masih membutuhkan approval eksplisit.

## 2026-06-22

### Phase 4A Resolve Reconciliation Warning Pilot

- Reconciliation tenant DB sekarang membedakan extra row pilot `DEMO-*` yang berasal dari seed demo tenant DB sebagai `accepted_pilot_extra`, bukan warning blocking.
- Extra row lain di tenant DB tetap dihitung sebagai `blocking_extra_key_count` dan masih membuat severity `warning`.
- Verifikasi `demo-kliniktech`: reconciliation `severity=ok`, `extra=3`, `accepted_pilot_extra=3`, `blocking_extra=0`, tanpa production cutover.

## 2026-06-22

### Runbook Migration dan Cutover Gate Tenant DB

- Menambahkan dokumen `mtdb-runbook.html` untuk SOP migrasi tenant non-master dari shared DB ke tenant DB dedicated.
- Menambahkan endpoint read-only `tenant-db/cutover-gate` untuk mengevaluasi pilot health, reconciliation, write-plan, dan block cutover.
- Platform Admin menambahkan tombol `Cutover Gate`.
- Gate production tetap membutuhkan approval eksplisit; endpoint ini hanya report dan tidak menjalankan cutover.
- Verifikasi `demo-kliniktech`: cutover gate `ok=true`, `cutover_allowed=false`, block `2` karena reconciliation masih warning dan masih ada write operation deferred.

## 2026-06-22

### Reconciliation Report Tenant DB

- Menambahkan service dan endpoint read-only `tenant-db/reconciliation` untuk merangkum gap shared DB vs tenant DB berdasarkan shadow-read.
- Menambahkan command `php artisan tenant-db:reconcile {tenant_slug}` untuk menjalankan report dari CLI.
- Platform Admin menambahkan tombol `Reconcile`.
- Report memberi severity `ok`, `warning`, atau `error` per tabel serta total missing/extra/mismatch.
- Verifikasi `demo-kliniktech`: command reconciliation menghasilkan severity `warning`, warnings `3`, errors `0`, extra `3` karena row demo tenant DB.

## 2026-06-22

### Dual-write Pilot Terbatas Tenant DB

- Menambahkan endpoint guarded `tenant-db/dual-write/run` dan `tenant-db/dual-write/rollback` untuk exercise dual-write pilot pada row demo `DEMO-*` di tenant DB.
- Run membutuhkan frasa `RUN TENANT DB DUAL WRITE PILOT`, menyimpan snapshot, lalu mirror update customer/invoice/ticket demo di tenant DB pilot.
- Rollback membutuhkan frasa `ROLLBACK TENANT DB DUAL WRITE PILOT` dan mengembalikan snapshot row demo.
- Guard: hanya tenant demo/pilot non-master, schema migrated, flag `tenant_db_dual_write_pilot` aktif, dan tidak mengubah routing production.
- Verifikasi `demo-kliniktech`: flag pilot aktif, run dual-write mengupdate `customers=1`, `invoices=1`, `tickets=1`; rollback mengembalikan `customers=1`, `invoices=1`, `tickets=1`.

## 2026-06-22

### Write-path Dry-run Planner Tenant DB

- Menambahkan endpoint read-only `tenant-db/write-plan` untuk audit jalur write customers/invoices/tickets sebelum dual-write pilot.
- Planner merangkum transaction boundary, idempotency key, mirror action, rollback strategy, dan operasi yang masih deferred.
- Platform Admin menambahkan tombol `Write Plan`.
- Tahap ini tidak menulis data dan belum mengaktifkan tenant DB sebagai source of truth.
- Verifikasi `demo-kliniktech`: write-plan `ok=true`, operasi `9`, siap pilot `8`, deferred `1`, write `false`.

## 2026-06-22

### Controlled Module Read Tenant DB Pilot

- Menambahkan resolver module read dengan default `shared_db`; tenant DB hanya dipilih jika tenant demo/pilot, schema siap, `use_tenant_db_read` aktif, dan flag granular modul aktif.
- Menambahkan endpoint read-only `tenant-db/module-read/{module}` untuk smoke test modul `users`, `customers`, `invoices`, dan `tickets` tanpa mengubah route production.
- Platform Admin menambahkan tombol `Module Read Pilot` untuk melihat source per modul.
- Tahap ini belum mengganti routing production user; hanya menyediakan adapter dan verifikasi controlled routing pilot.
- Mengaktifkan flag granular hanya untuk `demo-kliniktech`; smoke test module read `users/customers/invoices/tickets` semuanya `source=tenant_db`, routed `true`, count `1`.

## 2026-06-22

### Shadow-read Parity Tenant DB

- Menambahkan endpoint read-only `tenant-db/shadow-read` untuk membandingkan shared DB vs tenant DB pilot pada `users`, `customers`, `invoices`, dan `tickets`.
- Shadow-read menghitung source/target count, matched keys, missing keys, extra keys, field mismatch sample, dan status `parity_ready` per tabel.
- Platform Admin menambahkan tombol `Shadow Read` dan panel ringkasan parity.
- Tahap ini tidak mengubah routing production, tidak menulis data tenant, dan menjadi gate sebelum adapter read modul production.
- Verifikasi `demo-kliniktech`: shadow-read `ok=true`, parity penuh `false`, ready `1/4`, missing `0`, extra `3`, mismatch `0`; extra berasal dari data demo tenant DB yang tidak punya pasangan shared DB.

## 2026-06-22

### Pilot Health Tenant DB

- Menambahkan endpoint read-only `tenant-db/pilot-health` untuk merangkum kesiapan tenant DB pilot.
- Health check mencakup flag `use_tenant_db_read`, metadata/schema, koneksi DB, jumlah `users/customers/invoices/tickets`, kelengkapan demo flow, dan jumlah token pilot aktif untuk email pilot.
- Platform Admin menambahkan tombol `Pilot Health` agar operator bisa melihat readiness sebelum tahap shadow-read.
- Tahap ini tidak menulis data tenant dan belum mengubah routing modul production.
- Verifikasi `demo-kliniktech`: health `ok=true`, koneksi `true`, demo flow `true`, counts `users=1`, `customers=1`, `invoices=1`, `tickets=1`, token pilot aktif `0`.

## 2026-06-22

### Demo Flow Read-only Tenant DB Pilot

- Menambahkan endpoint read-only `tenant-db/demo-flow` untuk membaca rangkaian data demo `DEMO-CUST-001` → `DEMO-INV-001` → `DEMO-TICK-001` langsung dari tenant DB pilot.
- Demo flow memvalidasi bahwa invoice dan ticket terhubung ke customer demo melalui `customer_id`.
- Menambahkan route smoke khusus token pilot `/api/tenant-db-pilot/tenants/{tenant}/demo-flow` dan tombol Platform Admin `Demo Flow Pilot`.
- Tahap ini tidak menulis data baru dan belum mengubah routing modul production.
- Verifikasi `demo-kliniktech`: direct read `ok=true`, source `tenant_db`, complete `true`; smoke token route status `200`; token pilot sementara direvoke hingga sisa `0`.

## 2026-06-22

### Seed Demo Data Tenant DB Pilot

- Menambahkan service dan endpoint seed data demo kecil di tenant DB pilot: `DEMO-CUST-001`, `DEMO-INV-001`, dan `DEMO-TICK-001`.
- Seed bersifat idempotent agar tidak membuat duplikat jika dijalankan ulang.
- Menambahkan rollback data demo dengan frasa `ROLLBACK TENANT DB DEMO DATA` yang hanya menghapus row demo `DEMO-*`.
- Platform Admin menambahkan tombol `Seed Demo Data` dan `Rollback Demo Data`; tahap ini tetap belum cutover modul production.
- Verifikasi `demo-kliniktech`: seed membuat 1 customer, 1 invoice, dan 1 ticket; direct read dan smoke token pilot mengembalikan source `tenant_db`, count `1`; token pilot sementara direvoke hingga sisa `0`.

## 2026-06-22

### Pilot Read-only Invoices dan Tickets Tenant DB

- Menambahkan endpoint internal `tenant-db/invoices` dan `tenant-db/tickets` untuk membaca data dari tenant DB pilot jika flag `use_tenant_db_read` aktif dan schema sudah migrated.
- Menambahkan route pilot token `/tenant-db-pilot/tenants/{tenant}/invoices` dan `/tenant-db-pilot/tenants/{tenant}/tickets` untuk smoke test token dengan ability `pilot:tenant-db-login`.
- Menambahkan tombol `Read Invoices Pilot` dan `Read Tickets Pilot` di Platform Admin.
- Tahap ini belum cutover modul Billing atau Ticket production; untuk `demo-kliniktech` hasil valid dengan count `0` karena data invoice/ticket pilot belum di-seed.
- Smoke test token pilot ke route invoices dan tickets berhasil: status `200`, source `tenant_db`, routed `true`, count `0`, lalu token sementara direvoke hingga tersisa `0`.

## 2026-06-22

### Pilot Read-only Customers Tenant DB

- Menambahkan endpoint internal `tenant-db/customers` untuk membaca customers dari tenant DB pilot jika flag `use_tenant_db_read` aktif dan schema sudah migrated.
- Menambahkan route pilot token `/tenant-db-pilot/tenants/{tenant}/customers` untuk smoke test token dengan ability `pilot:tenant-db-login` tanpa akses Platform Admin.
- Menambahkan tombol `Read Customers Pilot` di Platform Admin.
- Guard master tetap aktif; tenant `kliniktech` tetap membaca shared DB `apps.ion.net.id`.
- Tahap ini belum cutover modul pelanggan production; untuk `demo-kliniktech` hasil saat ini valid dengan count `0` karena belum ada data customer pilot.
- Smoke test token pilot ke route customers berhasil: status `200`, source `tenant_db`, routed `true`, count `0`, lalu token sementara direvoke hingga tersisa `0`.

## 2026-06-22

### Smoke Test Token Pilot ke Auth Me

- Membuat token pilot sementara untuk `demo@kliniktech.com` pada tenant `demo-kliniktech`.
- Menjalankan smoke test internal ke `/api/auth/me` dengan token pilot; respons `200`, user `demo@kliniktech.com`, tenant ID `8`, slug `demo-kliniktech`, role `admin`.
- Memastikan token plaintext tidak muncul di response `/api/auth/me`.
- Token pilot sementara langsung direvoke setelah test; token pilot tersisa `0`.

## 2026-06-22

### Revoke Token Pilot Tenant DB

- Menjalankan revoke token pilot tenant DB untuk `demo@kliniktech.com` pada tenant `demo-kliniktech`.
- Token pilot yang dihapus: `tenant-db-pilot-token` ID `256` dengan ability `pilot:tenant-db-login`.
- Verifikasi setelah revoke menunjukkan token pilot tersisa `0`, sementara token non-pilot user tetap ada.
- Guard tenant master `kliniktech` tetap menolak lifecycle token tenant DB.

## 2026-06-22

### Lifecycle Token Pilot Tenant DB

- Menambahkan endpoint status token pilot untuk melihat jumlah token `tenant-db-pilot-token` dengan ability `pilot:tenant-db-login`.
- Menambahkan endpoint revoke token pilot dengan frasa wajib `REVOKE TENANT DB PILOT TOKEN`.
- Revoke dibatasi hanya token pilot; token production dan token lain tidak ikut dihapus.
- Platform Admin menambahkan tombol `Pilot Token Status` dan `Revoke Pilot Token`.

## 2026-06-22

### Pilot Login Internal Tenant DB

- Menambahkan endpoint internal `tenant-db/pilot-login` dengan frasa wajib `PILOT TENANT DB LOGIN`.
- Pilot login memvalidasi email/password terhadap tenant DB, lalu membuat token Sanctum pilot untuk user pusat yang email dan membership tenant-nya cocok.
- Guard aman: hanya tenant demo/pilot non-master, tidak mengubah `/auth/login`, tidak menghapus token existing, tidak update `last_login`, dan tidak update `tenant_id`.
- Platform Admin menambahkan tombol `Pilot Login`; audit log mencatat status tanpa menyimpan token atau password.

## 2026-06-22

### Auth-check Read-only Tenant DB Pilot

- Menambahkan endpoint internal `tenant-db/auth-check` untuk memverifikasi user dan password hash dari tenant DB pilot.
- Auth-check hanya aktif jika feature flag `use_tenant_db_read` aktif, tenant DB berstatus `schema_migrated`, dan tenant bukan master.
- Endpoint tidak membuat token, tidak mengubah session, tidak mengupdate `last_login`, dan tidak mengganti flow `/auth/login` production.
- Platform Admin menambahkan form `Auth Check Pilot` dengan audit log tanpa menyimpan password mentah.

## 2026-06-22

### Pilot Routing Read-only Users ke Tenant DB

- Menambahkan feature flag teknis `use_tenant_db_read` untuk pilot routing baca data user dari tenant DB.
- Menambahkan endpoint read-only `tenant-db/users` yang membaca dari tenant DB hanya jika flag aktif dan metadata tenant DB berstatus `schema_migrated`.
- Mengaktifkan flag hanya untuk tenant `demo-kliniktech`; tenant master `kliniktech` tetap membaca shared DB `apps.ion.net.id`.
- Platform Admin menambahkan checkbox pilot dan tombol `Read Users Pilot`; tahap ini belum cutover login atau modul production.

## 2026-06-22

### Read-sample Tenant DB Pilot

- Menambahkan service dan endpoint read-only untuk membandingkan sample data shared DB vs tenant DB pilot.
- Read-sample memeriksa tabel `users`, `customers`, `invoices`, dan `tickets`, menghitung source/target, matched key, missing key, serta sample kecil per tabel.
- Platform Admin menambahkan tombol `Read Sample` dan indikator `Tenant DB readable`.
- Tahap ini belum melakukan cutover login/API production; hanya membuktikan aplikasi dapat membaca data dari tenant DB pilot.

## 2026-06-22

### Domain Tenant Non-master kliniktech.com

- Menetapkan kebijakan tenant non-master/subscriber memakai domain utama `kliniktech.com` untuk mailbox tenant, misalnya `demo@kliniktech.com`.
- Provisioning tenant tetap memakai FQDN aplikasi berbasis subdomain seperti `demo-kliniktech.kliniktech.com`, tetapi mailbox default memakai domain root `kliniktech.com`.
- Menyesuaikan data pilot `demo-kliniktech` dari `demo@ion.net.id` menjadi `demo@kliniktech.com` di shared DB dan tenant DB pilot.
- Tenant master `kliniktech` tetap berada di `apps.ion.net.id`.

## 2026-06-22

### Copy Data Pilot Tenant

- Menambahkan service dan endpoint copy data pilot sungguhan untuk tenant demo/pilot dengan frasa wajib `COPY TENANT DATA PILOT`.
- Copy dibatasi ke data minimal `users`, `customers`, `invoices`, dan `tickets`, menolak eksekusi jika dry-run menemukan collision.
- Menambahkan rollback data pilot dengan frasa wajib `ROLLBACK TENANT DATA PILOT`; rollback menghapus row hasil copy terakhir tanpa drop database/user/schema pilot.
- Platform Admin menambahkan tombol `Copy Data Pilot` dan `Rollback Data Pilot`; tenant master `kliniktech` tetap ditolak dari jalur migrasi data.

## 2026-06-22

### Dry-run Copy Minimal Data Tenant

- Menambahkan service dan endpoint dry-run copy data minimal dari shared DB ke tenant DB pilot untuk tabel `users`, `customers`, `invoices`, dan `tickets`.
- Dry-run menghitung record sumber/target, memeriksa kolom yang bisa dipetakan, kolom yang belum tersedia di schema tenant minimal, dan potensi collision unique key.
- Platform Admin menambahkan tombol `Dry-run Copy Data` pada panel Provisioning Tenant.
- Dry-run bersifat read-only: tidak ada insert/update/delete ke tenant DB dan tenant master `kliniktech` tetap ditolak dari jalur migrasi.

## 2026-06-22

### Guard Tenant Master Tetap di apps.ion.net.id

- Menambahkan guard eksplisit agar tenant master `kliniktech` tetap berjalan di `apps.ion.net.id` dan tidak masuk jalur database-per-tenant.
- Jalur pilot DB-only, rollback pilot DB, dan migration schema tenant menolak tenant `kliniktech`, tenant `is_master`, atau `tenant_type=master`.
- Tenant subscriber lain tetap bisa diproses bertahap ke dedicated database setelah readiness dan konfirmasi terpenuhi.

## 2026-06-22

### Migration Schema Tenant Pilot

- Menambahkan migration khusus tenant di `database/tenant_migrations` untuk schema minimal: `users`, `customers`, `invoices`, dan `tickets`.
- Menambahkan service dan endpoint untuk preview/run schema tenant pilot dengan frasa wajib `RUN TENANT SCHEMA`.
- Platform Admin menambahkan tombol `Preview Schema` dan `Run Schema` pada panel Provisioning Tenant.
- Dynamic tenant DB tetap tidak memindahkan data production; migration hanya berjalan ke database pilot yang metadata-nya bertanda pilot.

## 2026-06-22

### Test Dynamic Tenant DB

- Menambahkan test koneksi tenant DB read-only dari metadata `tenant_databases`.
- Dynamic connection Laravel `tenant` kini mendukung password metadata yang tersimpan terenkripsi.
- Platform Admin menambahkan tombol `Test Tenant DB` untuk menjalankan query aman `SELECT 1`, `SELECT DATABASE()`, dan `SELECT CURRENT_USER()`.
- Test ini tidak menjalankan migration tenant dan tidak memindahkan data production.

## 2026-06-22

### Pilot DB-only Berhasil

- Membuat user MySQL provisioning `kt_provisioner`, menyimpan credential terenkripsi, dan membatasi privilege ke pola database `kt_tenant_%`.
- Menjalankan ulang `RUN DB PILOT` untuk tenant `demo-kliniktech` hingga berhasil membuat database `kt_tenant_pilot_db_only`, user tenant `ktu_pilot_db_only`, dan metadata `tenant_databases`.
- Memverifikasi koneksi ke database tenant dummy berhasil menggunakan credential tenant yang tersimpan terenkripsi.
- Production provisioning tetap terkunci (`production_unlocked=false`) dan side effect global DNS/mailbox/migration tenant belum dijalankan.

## 2026-06-22

### Pilot DB-only Gagal Aman

- Menambahkan auto-rollback internal pada pilot DB-only jika step create/grant gagal di tengah proses.
- Menjalankan pilot DB-only pada tenant demo, tetapi proses berhenti di step create database karena credential DB `kliniktech` hanya memiliki privilege pada database aplikasi, bukan privilege global create database.
- Verifikasi menunjukkan database pilot tidak dibuat dan metadata `tenant_databases` tidak tersisa.
- Langkah berikutnya membutuhkan credential MySQL khusus provisioning dengan privilege global terbatas untuk create database/user/grant pada database tenant pilot.

## 2026-06-22

### Rollback Pilot DB-only Tenant

- Menambahkan rollback DB-only pilot dengan frasa konfirmasi wajib `ROLLBACK DB PILOT`.
- Rollback hanya boleh berjalan pada metadata database yang bertanda pilot, lalu drop database/user dummy dan menghapus metadata `tenant_databases`.
- Menambahkan endpoint dan tombol `Rollback DB Pilot` di Platform Admin, lengkap dengan audit log sukses/gagal.
- Guard rollback diuji agar frasa salah ditolak sebelum ada side effect.

## 2026-06-22

### Pilot DB-only Provisioning Tenant

- Menambahkan service dan endpoint pilot DB-only untuk tenant demo/pilot dengan frasa konfirmasi wajib `RUN DB PILOT`.
- Preview pilot menampilkan database, DB user, step eksekusi, dan rollback plan tanpa side effect.
- Run pilot dibatasi ke tenant demo/pilot, readiness harus OK, dan hanya membuat database/user dummy plus metadata `tenant_databases`.
- UI Platform Admin menambahkan tombol `Preview DB Pilot` dan `Run DB Pilot`; DNS, mailbox, dan migration tenant tetap tidak dijalankan pada tahap ini.

## 2026-06-22

### Credential & Guardrail Provisioning Tenant

- Menambahkan tabel dan service untuk menyimpan credential provisioning secara terenkripsi, terpisah dari `.env` dan tidak diekspos mentah ke API/UI.
- Menambahkan endpoint Platform Admin untuk melihat status credential, menyimpan credential, dan menjalankan test read-only untuk DB admin, Cloudflare, Virtualmin, S3/storage, dan backup readiness.
- Menambahkan card `Credential Provisioning` di Platform Admin dengan guardrail dry-run: production provisioning tetap terkunci dan side effect off.
- Setiap update/test credential dicatat ke audit log tanpa menyimpan nilai rahasia di metadata audit.

## 2026-06-22

### Dry-run End-to-End Provisioning Tenant

- Menjalankan migration metadata provisioning pusat untuk mengaktifkan tabel `tenant_databases`, `tenant_domains`, `tenant_mailboxes`, dan `tenant_provisioning_jobs`.
- Menguji alur dry-run pada tenant demo: status provisioning terbaca, preview menghasilkan FQDN/database/mailbox rencana, dan draft job tersimpan.
- Readiness checklist kini melewati pemeriksaan tabel metadata, namun production provisioning tetap belum siap sampai credential DB admin, Cloudflare, Virtualmin, S3/storage, dan backup strategy diset.
- Tidak ada DNS, database tenant, mailbox, migration tenant, atau perubahan provider eksternal yang dijalankan.

## 2026-06-22

### UI Dry-run Provisioning Tenant

- Menambahkan panel `Provisioning Tenant` di detail tenant Platform Admin untuk preview subdomain, database tenant, DNS Cloudflare, dan mailbox Virtualmin.
- Menambahkan readiness checklist agar admin melihat status tabel metadata, credential DB tenant, Cloudflare, Virtualmin, S3/storage, dan backup strategy sebelum pilot.
- Endpoint status provisioning dibuat aman saat migration metadata belum dijalankan; preview tetap bisa digunakan, sedangkan draft memberi pesan jelas bila tabel belum siap.
- Tombol `Buat Draft` tetap tidak menjalankan DNS, create database, mailbox, migration tenant, atau side effect production.

## 2026-06-22

### Scaffolding Provisioning Tenant Multi-Database

- Menambahkan fondasi metadata untuk provisioning tenant: database tenant, subdomain/DNS, mailbox, dan draft job provisioning.
- Menambahkan endpoint internal Platform Admin untuk preview dan draft provisioning tenant tanpa menjalankan perubahan production.
- Menambahkan service dry-run untuk rencana Cloudflare DNS dan Virtualmin mailbox; service ini belum membuat DNS/email sampai ada aktivasi dan approval eksplisit.
- Menyiapkan arah arsitektur hybrid: master tetap di database pusat, tenant baru dapat dipilot ke database sendiri secara bertahap.

## 2026-06-22

### Notifikasi Group Saat Tiket Selesai

- Memperbaiki alur resolve/close tiket agar event `ticket_resolved` tetap dikirim ke group WhatsApp aktif meskipun tiket tidak punya pelanggan atau nomor pelanggan.
- Penyebab: notifikasi group untuk status selesai sebelumnya ikut jalur `notifyCustomer`, sehingga tiket manual tanpa customer tidak memicu group.
- Fallback baru mengirim template `ticket_resolved` ke group yang mengaktifkan event tersebut, dengan relasi tetap tersimpan pada `ticket_id` dan meta `ticket_reference_id`.
- Verifikasi: group `KDI-DAILY` aktif untuk `ticket_resolved`; syntax `TicketService` valid.


## 2026-06-22

### Outbox Kirim Group WA dari Detail Tiket

- Memperbaiki pencatatan Outbox untuk aksi `Kirim Group WA` dari detail tiket agar setiap klik kirim membuat baris Outbox baru.
- Penyebab: `reference_id` sebelumnya memakai `ticket_id`, sehingga kiriman berikutnya untuk tiket yang sama dianggap duplikat oleh dedupe `reference_type + reference_id`.
- Relasi tiket tetap disimpan lewat kolom `ticket_id` dan meta `ticket_reference_id`; `reference_id` dibuat kosong untuk aksi manual berulang.
- Tab Outbox sekarang otomatis membersihkan filter status `Received` yang terbawa dari Inbox agar daftar Outbox tidak tampak kosong.
- Verifikasi dry-run menunjukkan dua record Outbox untuk tiket yang sama berhasil dibuat dalam transaksi rollback.


## 2026-06-22

### Toast Sukses Selesaikan Tiket

- Memperbaiki alur tombol `Selesaikan` dan `Proses` tiket agar toast sukses tampil segera setelah API berhasil mengubah status tiket.
- Refresh dashboard/detail tiket dibuat tidak menghalangi toast; jika refresh tambahan lambat/gagal, pesan sukses tetap muncul.
- Menghindari duplikasi toast pada jalur GPS check-in dan jalur tanpa peta.


## 2026-06-22

### Inbound Wablas Media ke Inbox

- Memperbaiki webhook Wablas agar pesan media dari group/non-group tetap masuk ke Inbox, bukan hanya `messageType=text`.
- Jika pesan image/video/document/audio tidak punya caption, sistem menampilkan placeholder seperti `[Gambar masuk]` agar baris Inbox tetap terlihat.
- Metadata inbound kini menyimpan `media_url`, `media_mime_type`, `caption`, `group_id`, dan `participant_phone` untuk tracing pesan group.
- Tampilan Inbox / Outbox kini memberi label group lebih jelas dan menampilkan detail Group ID, peserta, tipe pesan, serta Media URL jika ada.
- Verifikasi dry-run payload Wablas `messageType=image` dari group berhasil membuat record Inbox dalam transaksi rollback.


## 2026-06-22

### Status Pending Kirim Group WA dari Detail Tiket

- Memperjelas respons API saat Wablas menerima pesan group tetapi mengembalikan `provider_status=pending`: UI sekarang memberi pesan bahwa pesan sudah diterima Wablas dan menunggu diproses provider, bukan sekadar dianggap sukses final.
- Log sistem tiket ikut membedakan status diterima provider/menunggu proses untuk notifikasi group manual.
- Verifikasi menunjukkan Outbox pending berasal dari respons Wablas `Message is pending and waiting to be processed`, bukan error aplikasi.
- Catatan konfigurasi: group `KDI-DAILY` saat ini memakai Group ID `120363012345678901@g.us`; pastikan ini ID asli dari Wablas/inbound agar provider dapat memproses sampai terkirim.


## 2026-06-22

### Notifikasi Tiket Manual Lebih Terarah

- Mengubah target notifikasi WhatsApp untuk event `manual_ticket`: jika tiket sudah memiliki `assigned_to`, pesan hanya dikirim ke teknisi yang ditugaskan tersebut.
- Notifikasi group WhatsApp tetap dikirim melalui routing group/event yang sudah aktif, sehingga group NOC/teknisi tetap mendapat salinan koordinasi.
- Jika tiket manual belum memiliki teknisi assigned, sistem tidak lagi broadcast ke semua teknisi; notifikasi diarahkan ke group WhatsApp jika routing group aktif, dan log menjelaskan tidak ada teknisi tujuan.
- Alur event lain seperti ONU offline, sinyal lemah, dan instalasi tetap memakai eligible technicians/home base seperti sebelumnya.


## 2026-06-22

### Kirim Notifikasi Group WhatsApp dari Detail Tiket

- Menambahkan tombol `Kirim Group WA` di detail Tiket & Helpdesk untuk user dengan izin `tickets.update` atau `notif.send`.
- Menambahkan modal pilih group WhatsApp aktif yang sudah didaftarkan di modul WhatsApp Bot.
- Isi pesan otomatis dibuat dari detail tiket: nomor tiket, status, prioritas, tipe, pelanggan, lokasi, home base, teknisi, dan deskripsi; pesan tetap bisa diedit sebelum dikirim.
- Menambahkan endpoint `GET /tickets/{id}/wa-groups` dan `POST /tickets/{id}/notify-wa-group`.
- Pengiriman memakai `WaBotService` dengan `is_group=true`, `ticket_id`, `reference_type=ticket_wa_group_manual`, dan tercatat di Outbox.
- Validasi dibuat jelas jika group tidak ditemukan atau nonaktif.


## 2026-06-22

### Tombol Hapus Tiket di Detail Tiket

- Menambahkan tombol merah `Hapus Tiket` pada detail Tiket & Helpdesk untuk role dengan izin `tickets.delete` atau admin wildcard.
- Menambahkan konfirmasi kuat melalui `guardConfirm` sebelum tiket dihapus.
- Memperketat backend `DELETE /tickets/{ticket}` agar menolak hapus tiket yang sudah punya relasi operasional penting: jadwal teknisi, laporan PSB, RFO, check-in lapangan, atau tiket anak incident.
- Tiket tanpa relasi penting dapat dihapus; lampiran fisik dan komentar tiket ikut dibersihkan.
- Setelah hapus berhasil, daftar tiket direfresh dan panel detail dikosongkan.


## 2026-06-22

### WhatsApp Bot Modal Simpan Langsung

- Mengubah tombol `Simpan Akun` di modal akun WhatsApp agar langsung menyimpan konfigurasi ke server via `/wabot/config`.
- Mengubah tombol `Simpan Group` di modal group WhatsApp agar langsung menyimpan group ke server.
- Jika penyimpanan gagal, perubahan draft lokal dikembalikan dan modal tetap terbuka agar user bisa memperbaiki input.
- Tombol `Simpan` bawah tetap dipakai untuk pengaturan global seperti auto-reply, abaikan pesan group, dan auto-register webhook.
- Hapus akun/group dari daftar juga langsung menyimpan perubahan ke server agar tidak perlu klik `Simpan` bawah lagi.


## 2026-06-22

### WhatsApp Group Test Pending Status

- Memperbaiki Test Kirim Grup Wablas tanpa akun eksplisit agar otomatis memakai akun Wablas aktif, bukan konfigurasi legacy tenant yang Base URL-nya kosong.
- Response provider Wablas `pending/queued/process/processing` kini dicatat sebagai Outbox `pending`, bukan `sent`.
- Pesan test grup yang masuk antrian provider menampilkan pesan sukses yang jelas: masuk antrian provider, bukan terkirim final.
- Menormalisasi Outbox lama yang sudah terlanjur `sent` padahal `provider_status=pending` menjadi `pending`.
- Membatasi request report status Wablas maksimal 100 item agar sync status tidak ditolak provider.


## 2026-06-22

### WhatsApp Outbox Queue untuk Jeda Akun

- Mengubah pesan WhatsApp yang terkena jeda/rate limit akun dari `failed` menjadi `pending` queue.
- Menambahkan metadata antrean di Outbox: `queued`, `queue_reason=rate_limit`, `queued_until`, dan `retry_after_seconds`.
- Menambahkan command `wabot:process-queue` untuk mengirim otomatis pesan pending saat waktu aman sudah tercapai.
- Menjadwalkan processor queue berjalan setiap menit melalui scheduler Laravel.
- Retry queue tetap memakai akun WhatsApp yang sama, tidak dialihkan otomatis ke akun lain.


## 2026-06-22

### WhatsApp Bot Daftar Group Notifikasi

- Menambahkan `Daftar Group Notifikasi` di bawah daftar akun WhatsApp Bot.
- Group disimpan di `wa_settings.groups` dengan Nama Group, Group ID Wablas, akun pengirim Wablas, status aktif, catatan, dan daftar event notifikasi yang diizinkan.
- Menambahkan pengiriman salinan notifikasi WhatsApp ke group aktif sesuai event, termasuk event PSB/instalasi dan tiket selesai.
- Menambahkan event routing `ticket_created`, `ticket_resolved`, dan `ticket_closed` agar bisa dipilih untuk target group.
- Pengiriman group memakai Wablas dengan `isGroup=true` dan Group ID sebagai target.


## 2026-06-22

### WhatsApp Test Grup Cache Permission

- Memperbaiki permission Laravel `storage` dan `bootstrap/cache` secara rekursif agar proses web (`www-data`) bisa menulis cache.
- Menambahkan guard pada cache WhatsApp Bot untuk round-robin, rate limit, dan jeda kirim agar kegagalan cache tidak menggagalkan test/kirim WhatsApp.
- Memverifikasi penulisan cache sebagai `www-data` dan jalur rate-limit akun Wablas berjalan tanpa error.


## 2026-06-22

### WhatsApp Group ID dari Pesan Masuk

- Memperbaiki parser inbound Wablas agar menangkap Group ID dari field seperti `group.id`, `group_id`, `groupId`, `chatId`, `remoteJid`, `key.remoteJid`, atau sender berformat `@g.us`.
- Pesan masuk grup sekarang disimpan dengan `sender = group_id` pada Inbox dan metadata `group_id`, `group_name`, `group_sender`, serta `participant_phone`.
- Menghindari error `Data too long for column phone` pada tabel legacy `wa_messages` dengan menyimpan nomor peserta untuk riwayat lama, sementara Group ID disimpan di log komunikasi.


## 2026-06-22

### Mockup Server Pulsa

- Menambahkan mockup web statis untuk alur modul Server Pulsa di `/mockups/server-pulsa.html`.
- Mockup mencakup alur order, validasi saldo, routing supplier, callback status, agen/reseller, produk/harga, API reseller, dan laporan profit.


## 2026-06-22

### Inventaris Mutasi Terbaru Clickable

- Membuat baris pada panel `Mutasi Terbaru` bisa diklik untuk membuka detail mutasi.
- Detail mutasi menampilkan barang, nomor mutasi, jumlah, alasan, waktu, lokasi asal/tujuan, requester, approver, referensi, serial number, catatan, dan alasan tolak bila ada.
- Menambahkan hover/cursor dan dukungan keyboard Enter/Space agar interaksi lebih jelas.


## 2026-06-22

### WhatsApp Bot Provider Mekari Qontak

- Menambahkan provider WhatsApp baru `Mekari Qontak` pada modul WhatsApp Bot.
- Menambahkan service `QontakService` dengan autentikasi HMAC Mekari (`Date` + `Authorization` HMAC SHA-256) untuk endpoint `https://api.mekari.com/qontak/chat/v1/broadcasts/whatsapp/direct`.
- Menambahkan dukungan akun Qontak di daftar akun provider: Client ID, Client Secret, Message Template ID, dan Channel Integration ID.
- Menambahkan validasi credential, pesan error yang jelas, tab provider, hint webhook inbound, dan test kirim via template WhatsApp approved.


## 2026-06-22

### Inbox inbound Telegram dan WhatsApp Bot

- Menambahkan endpoint inbound Telegram `GET/POST /api/telegram/inbound/{tenant}` agar chat ke Telegram Bot tercatat di Inbox / Outbox sebagai pesan masuk.
- Menambahkan auto-register webhook Telegram saat konfigurasi Telegram Bot disimpan.
- Mendaftarkan webhook Telegram tenant `kliniktech` ke `https://apps.ion.net.id/api/telegram/inbound/kliniktech`.
- Memverifikasi pesan inbound Telegram dan WhatsApp tercatat sebagai `direction=in` dan tampil di tab Inbox.


## 2026-06-22

### Tampilan Telegram Bot seperti WhatsApp Bot

- Halaman `Telegram Bot` sekarang memakai ringkasan KPI, flow notifikasi, konfigurasi credential, routing event, panduan setup, dan tombol aksi di header seperti pola WhatsApp Bot.
- Menambahkan opsi reset token Telegram tersimpan, status konfigurasi yang lebih jelas, dan modal test kirim dengan konteks Chat ID.
- KB Notifikasi dan daftar Semua Modul diperbarui agar operator memahami alur setup Telegram Bot baru.

## 2026-06-22

### Tutup form setelah submit Request Barang

- Setelah submit Request Stok Keluar, Transfer Gudang, atau Retur Barang berhasil, panel form otomatis direset dan berpindah ke tab Riwayat & Approval.
- Perubahan ini membuat user langsung melihat request yang baru dibuat dan form tidak tampak tetap terbuka setelah submit.


## 2026-06-22

### Detail Riwayat Request Barang

- Item pada daftar Riwayat & Approval di modul Request Barang sekarang bisa diklik untuk membuka detail.
- Detail menampilkan nomor request, status, tipe request, barang, jumlah, alasan, lokasi asal/tujuan, requester, approver, referensi, serial number, catatan, dan alasan penolakan bila ada.
- Tombol approve/reject di baris tetap bekerja tanpa membuka detail, dan aksi approve/reject juga tersedia di modal detail untuk request pending.


## 2026-06-22

### Perbaikan Request Barang inventaris

- Memperbaiki `Server Error` saat request barang akibat kegagalan notifikasi WhatsApp/cache server setelah request stok keluar dibuat.
- Kegagalan notifikasi inventaris sekarang dicatat sebagai warning/log, tetapi tidak membatalkan request barang.
- Memperbaiki permission storage/cache Laravel agar proses cache bisa menulis sebagai `www-data`.
- Menambahkan pesan error yang lebih jelas untuk request stok keluar, transfer, dan retur barang.


## 2026-06-22

### Bantuan Group ID Wablas

- Menambahkan tombol `Cara Ambil Group ID` pada modal Test Grup Wablas.
- Modal bantuan menjelaskan langkah resmi dari Wablas: device connected, aktifkan Get Incoming Message, kirim pesan ke grup, lalu salin Group ID dari Inbox Wablas.


## 2026-06-22

### Auto-register Wablas saat akun kosong

- Auto-register Webhook Device Wablas sekarang dilewati sebagai informasi, bukan error, ketika tidak ada akun Wablas aktif setelah akun dihapus.
- UI tidak lagi menampilkan toast error auto-register saat konfigurasi Wablas disimpan tanpa akun aktif.


## 2026-06-22

### Perbaikan ringkasan akun WhatsApp provider

- Mengganti angka `Total Provider` pada daftar akun WhatsApp menjadi `Akun Aktif` agar tidak tertukar dengan total pesan provider.
- Ringkasan kini menampilkan jumlah akun aktif dan total akun untuk provider tab yang sedang dipilih.


## 2026-06-22

### Validasi credential Wablas

- Menambahkan validasi agar Token dan Secret Key Wablas yang sama persis langsung ditolak dengan pesan jelas.
- Pesan auto-register/outbox kini menjelaskan cara pengisian credential: Token dan Secret Key harus berbeda, atau full authorization `token.secret_key` ditempel di kolom Token dengan Secret Key dikosongkan.


## 2026-06-22

### Edit terbatas detail barang inventaris

- Menambahkan tombol `Edit` pada modal Detail Barang Inventaris.
- Edit hanya mengubah nama item dan kategori agar stok, harga, supplier, dan serial number tetap melalui alur inventaris masing-masing.
- Menambahkan pesan error yang lebih jelas saat nama kosong, kategori tidak valid, izin kurang, atau server gagal menyimpan perubahan.


## 2026-06-22

### Detail log percakapan WhatsApp

- Baris Log Percakapan di modul WhatsApp Bot sekarang bisa diklik untuk membuka detail pesan.
- Detail menampilkan arah pesan, nomor target, akun, provider, status lokal/provider, message ID, customer, referensi, error, dan info teknis status sync/provider bila tersedia.


## 2026-06-22

### Perbaikan credential Wablas multi-akun

- Auto-register Webhook Device Wablas sekarang memakai Base URL dan credential dari akun Wablas aktif di daftar akun, bukan field global lama.
- Menambahkan dukungan input Token Wablas berupa token+secret terpisah atau full authorization `token.secret_key`.
- Memperjelas error Wablas `token format invalid`, `token invalid`, dan `device expired`, serta mencegah scheduler status Outbox dianggap crash aplikasi saat provider menolak credential.


## 2026-06-22

### Perbaikan tambah barang inventaris

- Memperbaiki error server saat menambah barang dengan kategori kustom seperti `Join Closure`. Kolom kategori lama di `inventory_items` kini menerima kode kategori kustom.
- Menambahkan pesan error yang lebih jelas pada modal Tambah Item jika validasi inventaris atau struktur kategori bermasalah.


## 2026-06-22

### WhatsApp Bot test grup Wablas

- Menjelaskan fungsi Tracking URL Wablas untuk status pesan dan Webhook Device untuk pesan/event masuk.
- Menambahkan tombol `Test Grup` pada akun Wablas. Test ini memakai Group ID sebagai target dan mengirim payload Wablas dengan `isGroup=true`.
- Memperjelas pesan error bila Group ID kosong atau provider selain Wablas dipakai untuk test grup.

# Kliniktech Knowledge Base Changelog

Semua perubahan fitur, modul, alur pengguna, integrasi, dan tampilan yang berdampak ke user harus dicatat di sini dan disinkronkan ke `public/kb/index.html`.

## 21 Jun 2026

### Mitra sebagai pelanggan dan paket khusus Mitra

- Menambahkan relasi Mitra ke pelanggan sehingga satu Mitra dapat memiliki layanan internet, invoice, pembayaran, tiket, kontrak, dan dokumen tanpa menduplikasi modul pelanggan.
- Detail Mitra kini memiliki tab `Profil`, `Layanan Internet`, `Tagihan & Pembayaran`, `Tiket`, dan `Kontrak & Dokumen`.
- Admin dapat menautkan pelanggan existing ke Mitra atau membuat pelanggan baru dari data Mitra setelah status Mitra disetujui.
- Modul `Paket Internet` kini mendukung penanda `Paket khusus Mitra`; paket ini wajib dipakai saat membuat pelanggan dari Mitra.
- Form admin `Tambah Mitra` kini memakai dropdown wilayah dari API `wilayah.id` untuk Provinsi, Kota/Kabupaten, Kecamatan, dan Desa/Kelurahan, sama seperti form publik `/mitra/daftar`.
- Akses menu dan aksi Mitra kini muncul di `Role & Hak Akses` sebagai permission `Mitra` dengan opsi lihat dan kelola.

### WhatsApp Bot multi-akun dan load balance

- Menambahkan dukungan banyak akun per provider pada modul `WhatsApp Bot`.
- Provider tetap dipisah menggunakan tab, dan setiap tab menampilkan daftar akun serta statistik provider/akun.
- Menambahkan mode routing `Akun utama saja`, `Round-robin bergantian`, dan `Weighted round-robin`.
- Menambahkan fallback ke akun sehat lain, cooldown akun gagal, limit per akun, dan jeda per akun.
- Log percakapan kini menyimpan akun/provider pengirim agar pengiriman load balance bisa diaudit.

### WhatsApp Bot provider Pancake

- Menambahkan provider `Pancake` pada modul WhatsApp Bot tenant.
- Pancake memakai `Page Access Token`, `Page ID`, dan Base URL `https://pages.fm` untuk API page-level.
- Webhook inbound Pancake dapat meneruskan event conversation ke endpoint WhatsApp Bot Kliniktech.
- Balasan otomatis dan test kirim Pancake memakai `Conversation ID`, bukan nomor HP biasa, sesuai API Pancake `reply_inbox`.
- Memperbarui UI WhatsApp Bot, KB Notifikasi, katalog Semua Modul, dan API docs internal.

### File Manager tenant

- Menambahkan menu `File Manager` untuk tenant dengan folder pribadi user dan folder umum tenant.
- Folder pribadi hanya terlihat oleh pemilik file, sedangkan folder umum dapat diakses semua user dalam tenant yang punya izin File Manager.
- Admin tenant dengan permission `file_manager.admin` dapat melihat semua folder user dalam tenantnya; superadmin atau tenant master dapat memilih tenant target untuk audit/support lintas tenant.
- File Manager memakai S3 Object Storage tenant jika aktif, dengan fallback local storage dan label storage yang jelas.
- Menambahkan audit log untuk upload, download, dan hapus file File Manager.
- Memperbarui KB Semua Modul dan API docs internal agar File Manager terdokumentasi terpisah dari File Share tenant master.

### Halaman Upgrade Paket disembunyikan

- Menyembunyikan `Upgrade Paket` sebagai modul/halaman tenant di Platform Admin agar tidak tampil sebagai menu terpisah.
- Fitur upgrade paket tidak dihapus: request, approval, portal customer, dan aksi upgrade dari detail pelanggan tetap tersedia sesuai permission.

### Penghapusan modul Bandwidth Monitor

- Menghapus menu dan halaman `Bandwidth Monitor` dari Netadmin.
- Menghapus modul `bandwidth` dari permission, preset role, dan pilihan feature flags/plan di Platform Admin.
- Menghapus endpoint khusus `/api/bandwidth/monitor` dan `/api/bandwidth/history/{onuId}`.
- Metrik trafik ONU tetap dipertahankan di detail ONU, dashboard, peta jaringan, dan PRTG agar data teknis tidak hilang.

### Mode Demo Aman

- Menambahkan flag `is_demo` untuk tenant agar platform owner dapat membuat sandbox/trial yang aman.
- Tenant demo menampilkan banner dan watermark `MODE DEMO` di Netadmin.
- Menambahkan guard backend `demo_guard_blocked` untuk membatasi aksi berisiko seperti hapus data, backup restore, payment gateway, API token integrasi, credential storage/notifikasi, broadcast, APK build, File Share master, dan upload besar.
- Notifikasi keluar dari tenant demo dibatasi ke whitelist internal untuk WhatsApp, Email, dan Telegram; Push Notification diblokir pada tenant demo.
- Memperbarui KB Pengaturan & Keamanan dan katalog Semua Modul dengan langkah penggunaan Mode Demo Aman.

### File Share tenant master

- Menambahkan menu `File Share` khusus tenant master sebagai repository file internal platform.
- File Share mendukung folder virtual: `docs`, `apk`, `reports`, `exports`, `templates`, `backup-verification`, dan `other`.
- File diupload melalui panel Netadmin dan disimpan memakai S3 Object Storage tenant master jika sudah aktif; jika belum, sistem memakai local fallback dengan label storage yang jelas.
- Menambahkan aksi download dan hapus file, serta audit log untuk upload, download, dan delete.
- Memperbarui KB agar File Share muncul di landing page, katalog semua modul, dan panduan Backup & Storage.

### Tombol Refresh Pengguna

- Menambahkan tombol `Refresh` di menu Pengguna agar admin dapat memuat ulang daftar user tanpa reload seluruh aplikasi.
- Jika refresh gagal, sistem menampilkan pesan error yang menjelaskan masalah koneksi API atau izin akses pengguna.

### WhatsApp Bot credential manual dan default pengiriman

- Mengosongkan field credential dan Base URL WhatsApp Bot agar tenant mengisi sendiri sesuai provider.
- Base URL Wablas dan BalesOtomatis.id kini wajib diisi manual; jika kosong, test/kirim pesan menampilkan error yang jelas.
- Default pengiriman WhatsApp Bot disetel menjadi maksimal 3 pesan per menit dengan jeda 10 detik per pesan.
- Menambahkan endpoint status callback dan cron `wabot:sync-status` setiap 5 menit agar status Outbox WhatsApp dapat diperbarui tanpa mengirim ulang pesan gagal.
- URL Webhook tampil untuk provider WhatsApp. Statistik Bot dan Log Percakapan membaca sumber Inbox/Outbox umum sehingga pesan dari webhook/provider ikut dihitung.

### Perbaikan gambar KB pada URL `/kb`

- Menambahkan base path `/kb/` pada seluruh halaman HTML Knowledge Base agar CSS, JavaScript, dan gambar `assets/*.svg` tetap terbaca saat halaman dibuka dari `https://apps.ion.net.id/kb` tanpa trailing slash.
- Perbaikan ini memastikan screenshot-like UI di halaman utama, katalog semua modul, dan halaman modul detail muncul konsisten.

### Knowledge Base semua tab/menu aplikasi

- Menambahkan `public/kb/semua-modul.html` sebagai katalog lengkap semua tab/menu aplikasi agar user dapat membaca fungsi, cara kerja, dan langkah penggunaan setiap halaman.
- Mendokumentasikan menu:
  - Dashboard, Peta Jaringan, Alarm, Ping Monitoring, ONU/ONT, OLT Devices, ODP/ODC Management, Network/NAS, dan PRTG Monitoring.
  - Pelanggan, Paket Internet, Hotspot/Voucher, Kontrak & Dokumen, Tiket & Helpdesk, Jadwal Teknisi, Home Base, Lapangan, dan Catatan/Memo.
  - Billing & Invoice, Payment Gateway, Referral/Reseller, Inventaris, Asset ONU, Request Barang, Laporan PDF, dan Statistik.
  - WhatsApp Bot, Telegram Bot, Email/SMTP, Notifikasi, Inbox/Outbox, Log & Aktivitas.
  - Backup & Restore, APK Builder, Platform Admin, Pengguna, Role & Hak Akses, dan Pengaturan.
- Menambahkan komponen visual `module-shot` di `public/kb/assets/kb.css` untuk membuat mockup screenshot-like UI langsung di halaman dokumentasi, bukan flow diagram.
- Menambahkan link `Panduan Semua Modul` dari `public/kb/index.html` agar user langsung menemukan katalog semua tab/menu.
- Memperbarui Cursor rule agar semua menu/tab yang terlihat di aplikasi wajib memiliki dokumentasi di `semua-modul.html` lengkap dengan fungsi, cara kerja, step-by-step, dan mockup UI.

### Knowledge Base multi-halaman step-by-step

- Merombak `/kb` menjadi landing page ringkas dengan halaman modul terpisah agar user tidak bingung membaca satu halaman panjang.
- Menambahkan halaman panduan detail:
  - `ticketing.html` untuk alur kerja tiket, assign teknisi, update lapangan, upload foto, dan penutupan tiket.
  - `pelanggan-billing.html` untuk tambah pelanggan, paket, invoice, pembayaran, reminder, overdue, dan suspend.
  - `gudang-inventory.html` untuk tambah item, stok masuk/keluar, retur, pemakaian PSB, dan stock opname.
  - `network-olt-onu.html` untuk tambah OLT, polling, mapping ONU, analisa LOS/redaman, dan eskalasi tiket.
  - `backup-storage.html` untuk S3 Object Storage, backup manual, backup otomatis, restore, dan status notifikasi.
  - `notifikasi.html` untuk WhatsApp, Email, Telegram Bot, Push Notification, template, dan troubleshooting channel.
  - `portal-api.html` untuk portal pelanggan, generate API key, akses read-only/read-write, dan penggunaan API docs.
  - `pengaturan-keamanan.html` untuk profil tenant, user/role, object storage, API Integrasi, audit, dan keamanan.
- Menambahkan CSS/JS bersama di `public/kb/assets/kb.css` dan `public/kb/assets/kb.js` agar layout KB rapi, responsif, dan mendukung tab internal.
- Menambahkan screenshot-like SVG untuk modul utama:
  - `screenshot-ticketing.svg`
  - `screenshot-customer-billing.svg`
  - `screenshot-inventory.svg`
  - `screenshot-network.svg`
  - `screenshot-backup-storage.svg`
  - `screenshot-notifications.svg`
  - `screenshot-portal-api.svg`
  - `screenshot-settings-security.svg`
- Memperbarui rule Cursor `knowledge-base-maintenance.mdc` agar perubahan user-facing wajib memperbarui halaman KB terkait, contoh kasus, troubleshooting, changelog, dan screenshot-like visual.

### Tab API Integrasi di Pengaturan

- Menambahkan tab `API Integrasi` di halaman Pengaturan tenant.
- Admin tenant dapat generate API key, memilih akses `Read only` atau `Read & Write`, mengisi expired opsional, melihat daftar token tenant, dan revoke token.
- Token baru hanya tampil satu kali saat dibuat, dilengkapi tombol copy dan peringatan untuk menyimpan token di secret manager.
- Menambahkan link langsung ke `https://apps.ion.net.id/api/docs` dari Pengaturan.
- Daftar token API integrasi kini bersifat tenant-wide sehingga admin tenant dapat melihat dan mencabut token integrasi tenant aktif.

### Channel notifikasi backup diperluas

- Menambahkan pilihan Telegram Bot dan Push Notifikasi untuk status backup manual dan otomatis.
- Notifikasi backup kini dapat dikirim melalui WhatsApp Bot, Email, Telegram Bot, dan Push Notifikasi.
- Telegram memakai Chat ID default tenant atau Chat ID khusus pada form backup.
- Push Notifikasi dikirim/ditandai untuk device Netadmin tenant yang memiliki FCM token; jika tidak ada device, status gagal ditampilkan jelas.

### Perbaikan gambar Knowledge Base

- Memperbaiki beberapa aset SVG di `/kb/assets` yang gagal tampil karena karakter `&` belum di-escape sebagai `&amp;`.
- Memvalidasi seluruh 10 file SVG Knowledge Base agar valid sebagai XML dan dapat dirender browser.

### Knowledge Base lengkap

- Memperluas halaman `/kb` dari ringkasan menjadi panduan operasional lengkap.
- Menambahkan panduan berdasarkan peran: Admin Tenant, Billing/Finance, NOC/Network, Teknisi, Pelanggan, dan Partner.
- Menambahkan modul lengkap: setup tenant, customer/billing, network, ticketing, inventory, storage, backup, notifikasi, portal, API, reports/audit, dan security.
- Menambahkan playbook cara penggunaan untuk setup awal, tambah pelanggan, billing bulanan, monitoring OLT/ONU, penyelesaian tiket, PSB, S3, backup, notifikasi, portal, API, dan audit keamanan.
- Menambahkan contoh kasus lengkap untuk LOS/power optic, invoice overdue, tiket teknisi, PSB inventory, backup S3, notifikasi gagal, integrasi CRM via API, dan portal pelanggan.
- Menambahkan troubleshooting cepat dan Q&A yang lebih luas untuk admin, billing, NOC, teknisi, pelanggan, partner, dan integrator.
- Menambahkan diagram baru:
  - `billing-payment-flow.svg`
  - `api-integration-flow.svg`
  - `notification-routing-flow.svg`
  - `inventory-psb-flow.svg`
  - `portal-customer-flow.svg`
  - `security-role-flow.svg`
- Memperkuat rule Cursor `knowledge-base-maintenance.mdc` agar update `/kb` wajib lengkap: modul, cara pakai, contoh kasus, Q&A, troubleshooting, gambar bila perlu, dan changelog.

### External API dan API Docs

- Menambahkan External API v1 di `/api/external/v1` untuk integrasi sistem eksternal.
- Menambahkan manajemen token integrasi tenant di `/api/integrations/api-tokens`.
- Menambahkan dokumentasi API publik di `https://apps.ion.net.id/api/docs`.
- Menambahkan OpenAPI JSON di `/api/docs/openapi.json`.
- Menambahkan rule Cursor agar setiap perubahan route, payload, response, auth, token, webhook, atau endpoint API wajib memperbarui API Docs.

### Text box mengikuti theme

- Memperbaiki tampilan input notifikasi backup agar background, border, teks, placeholder, dan focus state mengikuti dark/light theme.
- Menambahkan rule Cursor `theme-aware-textboxes.mdc` agar text box baru tidak memakai style default browser yang putih di dark theme.

### Knowledge Base publik

- Membuat halaman publik Knowledge Base di `/kb` untuk akses melalui `https://apps.ion.net.id/kb`.
- Menambahkan panduan awal penggunaan aplikasi, ringkasan modul, Q&A, contoh kasus, dan pencarian ringan di halaman.
- Menambahkan diagram detail:
  - `overview-dashboard.svg`
  - `olt-onu-monitoring.svg`
  - `ticket-field-work.svg`
  - `backup-s3-flow.svg`

### Backup & S3 Object Storage

- Mendokumentasikan penggunaan S3 Object Storage sebagai lokasi backup dan penyimpanan lampiran/foto teknisi.
- Menjelaskan fallback ke local storage jika S3 belum dikonfigurasi.
- Menjelaskan staging local pada backup sebelum upload ke S3.

### Dokumentasi wajib

- Menambahkan rule Cursor agar setiap perubahan fitur/modul wajib memperbarui Knowledge Base, Q&A, dan changelog bertanggal.
