CHECKLIST REVISI — ERP Kepala Toko (store_manager) Tanggal: 2026-09-03 Konteks: Admin/GM membuat outlet & menetapkan kepala toko. Kepala toko masuk ke ERP untuk mengelola stok produk outlet-nya sendiri dan melihat penjualan offline (POS) + online outlet tersebut saja. === SUDAH DIKERJAKAN === [x] Sidebar & ikon (semua role) - Ikon menu "Reports" & "Toko & Cabang" tidak muncul -> diperbaiki (daftar ikon Store/Buildings/UserGear didaftarkan; grup Reports diberi ikon). [x] Sidebar kepala toko dirapikan - Keanggotaan: DIHAPUS dari nav kepala toko (membership itu global, bukan per outlet). Backend tetap punya izinnya untuk kebutuhan kasir/POS. - Menu "Sales" (Katalog/Sales Order/Piutang): DIHAPUS dari nav kepala toko — transaksi jual dilakukan di aplikasi POS; penjualan outlet dilihat via Reports > Laporan Penjualan. - "Toko & Cabang": DIHAPUS dari nav kepala toko (dia hanya kelola cabangnya). - Settings, Changelog, Hubungi Developer: DI-COMMENT untuk SEMUA role (route masih ada, tinggal buka comment kalau mau ditampilkan lagi). - Halaman Launcher (grid modul di awal) punya daftar APPS sendiri -> ikut dirapikan: "Toko & Cabang" disembunyikan dari launcher untuk kepala toko, "Pengaturan" disembunyikan untuk semua role, tile "Laporan" mendarat ke Laporan Penjualan (aman untuk semua role). Untuk kepala toko launcher kini hanya: Dashboard, Inventory, Laporan, Sistem & Akses. [x] Inventory — Posisi Stok - Untuk kepala toko label jadi "Stok Produk" + subjudul "Stok produk yang tersedia di outlet Anda". Untuk admin pusat/GM tetap "Posisi Stok". - Data terfilter otomatis ke gudang outlet-nya (enforced backend). [x] Inventory — Pergerakan Stok - Terfilter otomatis ke gudang outlet kepala toko; filter "Semua Gudang" disembunyikan untuk kepala toko. - Penjualan OFFLINE (POS) dan ONLINE (ecommerce) tercatat di sini sebagai pergerakan "stock_out" (referensi: sales_order). Jadi kepala toko melihat "barang keluar karena terjual" + "barang masuk dari dropping/penyesuaian". [x] Inventory — Opname - Opname = pencocokan stok FISIK dengan sistem (stock-take). Selisihnya jadi penyesuaian. Tetap ada untuk kepala toko, terfilter ke outletnya. [x] Inventory — Transfer Stok - DIHAPUS dari nav kepala toko (dropping antar gudang diatur pusat/gudang). Endpoint tetap di-scope kalau diakses langsung. [x] Dashboard kepala toko - Tampilan khusus outlet: omzet hari ini / bulan ini (pisah offline & online), jumlah transaksi, nilai stok outlet, produk menipis / habis, produk terlaris, pergerakan stok terkini. Semua angka HANYA untuk outlet-nya. - Endpoint baru: GET /dashboard/outlet (backend, di-scope by store_users). [x] Reports kepala toko - Hanya "Laporan Penjualan" + "Laporan Stok", keduanya terfilter ke outlet. - Laporan Cashflow / Laba Rugi / Neraca / Konsolidasi disembunyikan untuk kepala toko (itu level perusahaan). [x] Backend — store scoping - /inventory/stock, /inventory/movements, /inventory/opnames, /inventory/transfers, /reports/sales, /reports/stock, /dashboard/outlet kini terfilter ke outlet pemanggil kalau dia store_manager. GM/admin tetap lihat data perusahaan penuh (tidak ada regresi — sudah diverifikasi). - Tulis stok ke gudang di luar outlet -> ditolak 403. - System -> Users: daftar kepala toko kini = akun kasir di outletnya + dirinya sendiri. Role yang tidak bisa ia kelola (admin, manajer lain) TIDAK muncul walau terdaftar di store_users outlet yang sama. Sebelumnya menampilkan SEMUA 22 user perusahaan. Akses user luar cakupan -> 404. Kasir baru yang dibuat kepala toko otomatis ditautkan ke outletnya. Verifikasi: KT lihat 4 (1 kepala toko + 3 kasir), admin 22. - Perbaikan terkait: akun 'admin'/'owner' selalu platform-wide meski terdaftar di store_users (seed menautkan admin ke semua outlet untuk demo POS). Tanpa ini, laporan/inventory admin ikut ke-scope ke satu outlet. (cmd/server/main.go — loader ScopeMiddleware.) [x] Backend — penjualan ONLINE per outlet - Checkout ecommerce sekarang bisa membawa store_id; kalau tidak, dipetakan ke outlet aktif pertama. Stok dipotong dari gudang outlet itu dan ordernya masuk ke ledger + laporan penjualan outlet tsb. [x] Seed / DB - kepalatoko@keluargamart.local (password Admin@1234) diikat ke store KM-PST. (seed.sql section 31 + migration baru 000046 untuk DB lama.) - Ditambah ~10 penjualan outlet KM-PST terakhir (POS + ecommerce) + item + stock_movements supaya dashboard/laporan/pergerakan stok terisi. - Login demo ERP: tombol "Kepala Toko" ditambahkan. [x] POS - Riwayat Transaksi: kolom Item & Kasir terisi - GET /sales-orders (dipakai halaman Transaksi POS) sekarang mengembalikan line_count, item_qty, dan cashier_name (kasir via shift, fallback ke sales rep). Sebelumnya kolom Item selalu "0 (0)" dan Kasir kosong untuk SEMUA transaksi karena endpoint daftar tidak mengirim data itu. - POS frontend (orders.js mapOrder + TransactionsPage.vue) dipetakan ke field baru. - Seed: penjualan outlet contoh (SO-OUT-PST-*) yang bertipe POS ditautkan ke SHIFT-DEMO-002 supaya kasirnya muncul; yang ecommerce tetap tanpa kasir (order online tidak punya kasir). - Kolom PEMBAYARAN masih kosong: butuh POS mencatat metode bayar (baris tabel payments / kolom di sales_orders) saat ProcessPOS - perubahan lebih besar, belum dikerjakan. === CATATAN / TINDAK LANJUT === - migrations/atlas.sum belum di-hash ulang untuk 000046 (butuh `atlas migrate hash`). Boot server (AUTO_MIGRATE) tidak butuh atlas.sum, hanya jalur scripts/migrate.sh berbasis atlas yang butuh. - Kebijakan "outlet mana yang memenuhi order online" saat ini: pakai store_id dari payload kalau ada, kalau tidak -> outlet aktif pertama. Perlu dikonfirmasi ke klien apakah butuh routing yang lebih pintar. - Scoping list stok/pergerakan pakai 1 gudang (kepala toko diasumsikan 1 outlet, sesuai desain migration 000044). CHECKLIST REVISI — Jadwal Kunjungan & Definisi AO Tanggal: 2026-09-13 === SUDAH DIKERJAKAN === [x] Jadwal Kunjungan Penagihan (Finance) & Rutin (SPV) - Backend (keluargamart-backend): sales_visits dapat kolom visit_type ('routine'/'billing') + scheduled_by. Permission baru: sales_tracking.visits.create.billing (Finance/Kepala Finance), sales_tracking.visits.create.routine (SPV), sales_tracking.visits.view_own (Sales — endpoint /me). Role 'sales' KEHILANGAN visits.create & visits.view (broad) — sales tidak lagi bisa membuat jadwal sendiri, hanya bisa lihat jadwalnya sendiri. Enforcement ada di internal/salestracking/service.go CreateVisit (SchedulePermission). Migration: 000060_visit_schedule_rbac.sql. - Frontend: halaman baru "Jadwal Kunjungan" (Finance/SPV bikin jadwal, jenis jadwal otomatis sesuai role) dan "Jadwal Saya" (Sales, read-only, GET /sales-tracking/visits/me). [x] Definisi AO (Active Outlet) diperbaiki - Sebelumnya "AO Aktif" di dashboard dihitung dari distinct sales_id yang melakukan kunjungan hari ini (salah — itu jumlah sales aktif, bukan customer aktif). Diperbaiki: AO = customer yang masih bertransaksi (ada sales_order non-cancelled) dalam 45 hari terakhir. internal/reports/repository.go ActiveAOCount. - Widget dashboard "Performa AO" (per sales, bukan per customer) di-rename jadi "Performa Kunjungan Sales" supaya tidak rancu dengan AO Aktif KPI di atasnya. === CATATAN / TINDAK LANJUT === - Rute multi-stop (sales_routes / halaman "Rute") SENGAJA tidak ikut diubah — scope revisi klien hanya soal jadwal kunjungan per-visit (penagihan/rutin), bukan rute harian. Kalau klien juga mau rute dibatasi Finance/SPV, perlu revisi terpisah. - Butuh restart backend + migrate untuk 000060 supaya permission baru aktif; user yang sudah login perlu logout/login ulang agar /auth/me mengambil permission set terbaru. CHECKLIST REVISI — LHPP Sales (kunjungan penagihan) Tanggal: 2026-09-20 Konteks: LHPP (§ jadwal kunjungan di atas) sebelumnya hanya ada untuk flow gudang -> supir (DO yang di-dispatch, dikelompokkan per driver+tanggal). Sales yang jadwal kunjungan penagihannya dibuat Finance (visit_type: 'billing') tidak punya LHPP sendiri untuk dicetak, karena kunjungan itu sama sekali tidak terhubung ke delivery order/driver. === SUDAH DIKERJAKAN === [x] LHPP Sales — endpoint & grouping baru - Backend (keluargamart-backend): internal/lhpp dapat buildSalesGroups() baru, dikelompokkan per sales_id+visit_date (bukan driver_id+do_date), join ke invoice TERBUKA milik customer (status unpaid/partial/overdue) alih-alih 1 invoice per DO. Query baru: ListLHPPSalesCandidateRows (db/query/lhpp.sql, di-generate via sqlc). Route baru di bawah GET/POST /lhpp/sales, /lhpp/sales/me, /lhpp/sales/{id}, /lhpp/sales/{id}/payments — pakai permission sales.lhpp.view/.pay yang sudah ada (tidak perlu migration permission baru, sudah tercakup wildcard sales.*). Pembayaran tetap lewat finance.Service.PayInvoice yang sama dengan ReceivablesPage & LHPP supir. - Frontend: src/mocks/controllers/lhpp.js dapat buildSalesGroups() mirror dari backend + listSales/mySales/getSales/recordSalesPayment; route mock didaftarkan di registry.js. src/mocks/data/tracking.js di-seed beberapa kunjungan visit_type:'billing' ke customer yang benar-benar punya invoice terbuka, supaya lembar cetak tidak kosong saat demo. Test baru: src/mocks/controllers/lhpp.sales.test.js (5 test, mock HTTP layer langsung, pola sama seperti registry.test.js). - UI: LhppPage.vue (/sales/lhpp) dapat tab kedua "LHPP Sales (Saya)" di samping "LHPP Supir" yang lama — satu menu, bukan halaman baru (selaras preferensi klien: sesederhana mungkin). Tab ini selalu pakai GET /lhpp/sales/me (self-scoped ke sales yang login, resolusi employee id sama seperti /sales-tracking/visits/me), jadi Finance/admin yang buka tab ini akan lihat kosong (memang bukan sales) — GET /lhpp/sales (semua sales) sudah ada di backend/mock tapi belum ada UI-nya. Tombol "Cetak LHPP" pakai window.print() sama seperti LHPP supir; PDF download (NextReport /reports/render) TIDAK ikut dibuat untuk tab sales — hanya cetak browser. === CATATAN / TINDAK LANJUT === - Diasumsikan 1 kunjungan penagihan per sales+customer+tanggal. Kalau Finance menjadwalkan customer yang sama 2x di hari yang sama, total tagihan customer itu akan terhitung dobel di grup LHPP Sales — dianggap human error penjadwalan, belum ada validasi/guard di backend maupun mock untuk kasus ini. - Belum ada UI untuk GET /lhpp/sales (semua sales, view Finance) — kalau Finance butuh lihat/cetak LHPP sales siapa pun (bukan cuma miliknya sendiri), perlu tab/filter tambahan di LhppPage.vue. - Verifikasi manual via browser sempat terhambat: RolesPage (Peran & Hak Akses) di mock butuh setup awal manual (matriks permission per role mulai kosong, "Selesai" menimpa seluruh matriks role itu, bukan menambah) sebelum akun non-admin bisa login sama sekali — ini perilaku existing yang sudah ada sebelum perubahan ini, bukan regresi baru. Verifikasi akhir dilakukan lewat test mock HTTP langsung (lhpp.sales.test.js) + full test suite (157 test).